unity3d如何调整手机横屏

调整手机横屏方式

1. 菜单选择 File -> Build Settings,打开构建设置

2. 选择IOS,点击 Player Settings 打开播放器设置

3. 播放器设置中可以找到 Default Orientation 选择,调整为Landscape即为横屏模式

横屏

屏幕朝向模式有多种选择:

  1. Portrait: 竖屏模式,就是我们拿手机看新闻的模式
  2. Portrait Upside Down: 1模式上下颠倒
  3. Landscape Right: 右侧横屏模式
  4. Landscape Left: 左侧横屏模式
  5. Auto Rotate: 自动旋转模式,跟随陀螺仪自动判断方向

hexo generate生成文件为空,已解决

原因

hexo generate生成文件为空,原因是hexo版本太低。修改hexo在package.json中的版本,重新执行 hexo install后可恢复正常。

1
hexo install

背景

有几年没更新博客了,这次下载下来更新了一下就出问题了,hexo generate生成的文件都是空的。

在hexo仓库找到了一个issue,发布于2020年。

根据提示,更新hexo版本后成功解决。

引用

海量分布式运维差异

我跟我媳妇有时候会选择自己在家做饭。我帮她剥蒜,一颗一颗干干净净。

我们结婚的时候,全村人都到了,几十桌,请了做饭师傅,在祠堂举行。

我们去帮忙,搬搬桌子洗洗菜啥的。

蒜蓉基围虾需要很多大蒜。我们围在姑姑旁边,姑姑从袋子里掏出一捧蒜,放在地上,用刀背一拍,就交给我们处理了。

被刀拍过的蒜格外好剥,我们总能在吃饭前把大蒜处理好。

但是会不会有点脏呢?当然没有用手剥干净了。但是实际效果差别不大。

在我的技术生涯中,曾经有一段时间工作内容是开发新特性,并且维护平台的稳定性。那时我总能把每个问题跟进到底,处理的滴水不漏。

后来我有机会运维一些比较大的系统,开始引入SLA,从一种新的角度去对待自己的工作内容。

我们说4个9,这意味着我们要处理的请求量非常大了。我开始慢慢意识到不能像以前一样把系统打造得滴水不漏,事必躬亲,各种小概率导致的意外开始逐渐瓦解某种完整的印象。失控开始相继出现。

如果最后我们能良好的生存下来,必然需要某种平衡,从杂多的处理事项中识别出哪些更为重要。这是一种与对待小规模系统完全不同的思路。

现在,我们在学着享受在失控边缘的这种状态。而且可以确信的是,飘摇不定也是一种稳定的状态。

org-mode again

Table of Contents

  1. 这段日子org-mode去了哪里?
  2. 这段日子我去了哪里?
  3. 为什么要重新使用org-mode?
    1. 诉求
    2. 场景举例
  4. 结论

为什么会有这样一篇文章呢?

由于某些原因,我希望能借助到org-mode的执行能力。本来只是想写一个测试文档,后来一想来都来了,写篇正经点的文章也没什么不可以。所以就有了这样一篇文章。

这段日子org-mode去了哪里?

矛盾,真的很矛盾。

遇到很多的事情总是一致地相似,尝试过很多工具,最后结论就是,没有银弹。

很久之前重度依赖org-mode,着迷于它简单的格式。

使用它来应用gtd,记录时间,记录文章。因为org-mode很难跨平台提供手机端支持,一度为它提供了网页端来记录我使用手机打游戏的时间。

最后是什么原因失败了呢?我发现有一些软件已经做到了这些,虽然收费,但是物有所值。

那一段时间我换了软件,换了平台,emacs被我遗弃了。

这段日子我去了哪里?

去了哪里当然是说我在做什么,文章里突然消失了。虽然只有我自己意识到自己是否消失。

这段时间发生了一些事,行业也不是很景气,新冠也改变了自己的一些行为方式。换了个工作环境,新环境的融入耗费了一些精力。

在感觉一切就要复苏的时候,我回来了。

为什么要重新使用org-mode?

诉求

那为什么又要使用org-mode呢?

这次不是因为gtd了,而是想要借一下org-mode的可执行手册的能力。

为什么需要可执行手册呢?

这要从几个月前说起了。。。此处陷入回忆,节奏放缓。。。

我之前的工作以编码为主,换到新环境后做起了SRE,众所周知,SRE岗位做的事情有很大一部分是琐事。

部门还处在建设之初,虽然各个事项已经手册化,但是频繁在手册与服务器间切换是很不容易的。可执行手册成为了我的救命稻草。

场景举例

我举一个比较常用的场景,比如我要在运维机器上执行命令探测远端机器的一个接口服务,普通模式下要登录运维机,然后将手册里的命令粘贴到终端执行。

比如,我可以定义远程执行函数如下:

(defvar foo-service-hosts '("127.0.0.1" "192.168.1.101"))
(defun run-on-ops (cmd)
  "Run CMD on ops server."
  (format "I am the cmd (%s) result." cmd))

然后在相关操作手册里使用下面的结构:

检测服务器请求是否正常:

(run-on-ops (format "curl %s/api/v1/hello" (first foo-service-hosts)))

这样就可以把大部分操作留在文档里,避免一些接触服务器的操作。理想情况下,就可以完全不直接接触服务器了。

结论

后面又会把org-mode使用起来了,不过是使用它的另一个能力。先练习使用起来再说。

好了,我要去找导出为markdown的命令了,然后把这个文章更新到github。

狗子

为什么?

初冬清晨里,

热带的太阳把地面烤得暖洋洋,

一只狗子眯着眼躺在地上翻滚。

而我,

不能。

站着的碎想

我们有太多的时间是处于站立的状态,即使在工作之后也是如此。

但是我们有几时曾感受到大地的厚实,他的纹理与外表形状。

一块木头,他的纹理、色泽、温度,也不是每个人都感受得到的。

这或许是区别暴发户的一个好手段,是否能对环境有细致体贴的感受,敏锐的区别各个环境的差异,才能真正明白什么是高端享受。

所谓不是所有人都懂得某样东西某件事的价值,其实也在于我们的敏锐程度是不一样的。

那就伸出手,抚摸一下大理石的温度与纹理,感受他给手指带来的反馈。

那就站起身来,好好感受一下大地的厚实与沉稳吧!

多了鞋子的障碍?没关系,脱掉鞋袜,踩踏一番,磨擦一番,大地总会回应我们的。

GTD实践再分享

引子

上回 总结反思自己的gtd实践还是19年的3月份,转眼已过去一年半,来到了2020年的下半年。

在之前的反思中,我回顾了自己对于时间这个概念在不同时期的理解及应对方式,然后介绍了自己对任务类型的理解,及在org-mode中的简单应用。

org-mode很强大,但是在现实场景中,需要自己去做一些额外工作,比如手机端和电脑端的同步、手机端的使用体验等,都是比较大的问题。

在使用一段时间后,我也自己写过一个兼容org的gtd工具,但是后面还是因为使用成本过高而终止了很长一段时间。

再一次用起gtd工具是几个月前,契机是有一些事情需要跟踪处理。时间很多和事情很多两种极端情况都是gtd大展身手的好时候。

虽然我也买了嘀嗒清单的vip,但是我相信工具应该是脱离实现的。所以在这里借一篇文章再次对gtd作一番梳理和思考。

解决的问题域

首先,工具的存在是为了解决问题的,我们当然要明确gtd的存在是为了解决什么问题。

解放大脑几乎是所有工具都会尝试解决的问题,这也是把制造工具作为人类与其他动物有别的一个原因。

当然,大脑功能很多,我们要明确的是,gtd要解放的,是大脑的记忆和注意力。

记忆是很好理解的,gtd首先是具备任务记录功能的,包括一些其它的电脑存储设备也可以很好地解决记忆容量的问题,比如磁盘文件等。

解放注意力是gtd有别于存储的一个重要特性,这里说的其实就是gtd的规划和调度的能力,我们需要一种工具让我们日益复杂的任务管理变得简单。

这里,我再来一句个人对gtd解决问题的理解,gtd是一种解放大脑存储负担,并方便我们安排调度任务的工具

工具手段

解放大脑负担是很简单的,大部分计算机存储部件都可以解决。协助我们安排调度任务则是一个需要一系列工具来协助的过程。

首先,我们的第一个强有力的工具就是流程化。当把复杂的任务抽象成一个流程框架后,整体过程就变得有章法可寻,一切新事物也变得似曾相识。

我说一下我个人的实践情况,可能跟标准gtd有些微差别,工具嘛,进行个人定制才是达到最佳效果。

gtd会定义一个收件箱的概念,它是一个暂存区或者缓冲区,有一些临时性的任务会被临时放置其中。为什么需要暂存?因为这样可以最大限度得减少上下文切换的开销。

比较常见的暂存区可以由一些工具来充当,比如记事本或者邮箱的草稿箱等。

暂存区的任务只用作暂存,是需要定期做处理的,具体的时间因人而易。比如我每天晚上都会清理下当天的暂存区。

清理就是为这些暂存任务添加属性的过程,比如这个任务是个人任务还是工作相关的任务,它的紧急程度是怎么样的,是否要加入近期的待办中,等等一类的属性。

其中比较重要的属性是排期,因为我们说gtd很重要的一个作用就是协助我们进行任务的排期调度。

排期可能有周期属性,比如连续某几天都要处理某件事;或者只是在某个时间点花较少的时间经历做个确认;或者压根就没有排期,这一类一般是不重要的任务,大多实践者会选择在空闲时选取不重要但想做的任务来做。

有了收件箱和任务清理,还有一个阶段就是定期对所有任务进行复盘,比如每周检查一下是否有遗漏的任务或者随着时间推移有的任务优化程度下降等。

这实际上是一个闭环的过程,而且整个过程的步骤都比较有章法,可以大大减缓个人的心智负担。

一些实践

现实是复杂的,所以工具需要反复打磨以符合现实中个人的要求。当然,一个子工具的概念和使用上的思考也可以帮助我们做出更好的优化改进。

下面是我对一些子工具的思考,它们是gtd这个系统中的一部分,使用熟练能够让整个系统运转更流畅更高效。

优先级

我个人有尝试过优先级方面的设定,但是一来优先级的变化频率比较高,二来优化级的设定比较耗费精力(任务属性往往比较多,且优化级不好量化),所以后面就省掉了优化级的维护。

但是优先级的存在可以帮助我们对未排期的任务做排序,如果不对优先级做排序,我们就需要替代的手段。

个人经验是通过标签+排期来区分优先级程度,因为这两个工具往往更贴近实际,标签有语义,排期有实际的操作时间。

比如个人成长标签一般是重要不紧急的,而某些重要项目当前比较紧急,那它的关联任务一般也会较为紧急。

如果一些任务比较紧急,我们就要提前为它们确认排期,未确认排期的任务再经过下个循环评估紧急程度。

标签

标签其实也是一个很有意思的设计模式,它可以提供强大的灵活性,让用户可以根据自己的情况灵活定制。

从数据结构上来说,标签是一个多对多的关系,一个标签可以贴到多个任务上,一个任务也可以贴多个标签。这样我们就可以很方便地使用标签对任务做过滤。

我的个人经验是,对标签做一个层级管理,这样方便对整个层级做统计和任务间的快速区分,任务标签只用二级。按照优先级里讲的方法,也是间接地设置了优化级及查看方式。

通过标签颜色,就可以在日历视图中很方便地看到自己一个月内的任务分布情况,如个人任务和工作任务的占比、时间分布等。

排期

上面说到用排期来指标任务的紧急程度,其实排期也可以有更为灵活的处理。

比如我们最近要做某个任务,但是并没有确定具体时间。其实就是一个deadline任务,那我们可以把它安排到明天或者后天,来提醒自己到明天或后天再来评估排期。

对排期分类的思考也是比较有用的,我在上篇gtd文章中也做过一些分类。现在看法更简单些了,要思考的无非两点,要不要排期,排在哪一天,当然也可能是排在哪一段时间。

我之前还有具体时间段的划分,org-mode也有这类工具,比如上午10点-11点看书。但是目前来看,至少对我个人来说,粒度过细是不可取的,因为没有足够的掌控空间。这个还是需要结合自己的时间特点,场景很重要。

我该用什么工具?

还是要选择自己感觉顺眼的工具,心情好,什么都好。

当然,它可能需要有一些比较明显的特点,对我来说,下面几点比较重要:

  1. 界面美观:上面已经说过,使用工具时工具反馈给我们的心情变化比较重要
  2. 功能覆盖:不一定全,但是一定要直接或间接满足自己的需要,且整个过程顺畅
  3. 足够灵活:工具是要贴合人的,而现实是复杂的,这就需要工具有一定的定制能力可以随场景发生一定程度的变化
  4. 全平台支持:这个应该算是目前软件的标配了,手机电脑需要互通是一项基本且重要的能力

如果只有一天,如何学习vuejs?

1. 树靶:从入门到精通?

伴随着计算机技术的发展,我们总是能听到或看到《n天从入门到精通》系列丛书,精通一词总能给人带来一阵兴奋,好像找到了武功秘籍,练后就登峰造极。

但这多半只是换来脑中一热,只是精通招式,还是不能为我所用创造出令自己满意的效果。

这篇文章必然不属于该系列,”一天”这个限制并不想蒙蔽读者,而只是为了创造一个极限条件,时间有限不能全部照顾到,逼迫我们去思考什么才是最重要的。

打蛇打七寸,当真屡试不爽。但是每个人都有自己的学习方式,并不一定对所有人都有效。不以为然者尽可以把这当作另一种思路借鉴。

下面我们就来看看,一天时间,能把哪些重要的东西学到手。

2. 探路:我眼中的vuejs是怎样的?

首先,我要做些铺垫,来说明一下我眼中的vuejs是个什么工具,用以解决什么问题。

计算机语言发展史,在我看来就是一个抽象层次不断升级的历史。

最开始的面向过程编程,比如c语言,实际上是将功能逻辑封闭到了函数,是对过程的抽象。这是第一层。

但是过程虽然做了抽象,数据却还是游离在代码中,面向对象编程即是再将数据跟逻辑一起封装为一个整体,即对象。这是第二层。

这两者都是代码层面,复用程度也还是有限。于是出现了一些第三方模块,复用程度较面向对象又高了一个层次。这是第三层。

再上一层就涉及到具体功能设计了,比如django的contrib.admin,即是django将整个功能以插件方式使用的极好例子,这些代码已可以完整提供功能模块了。这是第四层。

当然,上面还有更高明的设计,IaaS、SaaS、PaaS更是提供了整个运行时的能力,直接申请运行时即可使用,简单方便,程度又高了一层。这是第五层。

vuejs的设计介于第三层和第四层之间,虽说提供了某种层次的抽象,类似第三方模块,但是它更提供了抽象的解决方案。这正是它跟jQuery这类 js库 的差别所在。

更具体得说,vuejs实现了一个叫做component的前端复用粒度,并提供了一整套框架来使component足够灵活以满足绝大多数需求。

我们下面便来看一下vuejs中的component是如何达到抽象复用的。

3. 观详:vuejs中的component设计

这里的标题是component设计,而不是”component如何编写”,如何编写并不十分重要,我们想要知道的是,它是怎么设计来满足我们目标的。

为什么是它满足我们的目标而不是我们去学习它的使用?因为我们的思考是第一位的,否则很容易在这纷繁的世界中迷失,人云易云。

那我们就先设计一个框架,让vuejs实现这个框架(奇怪,竟然是vuejs来贴合我们的思考,而不是我们去学习了)。

那我们现在就来思考,如何设计一个可以复用的component,满足我们复用的需求。

。。。

经过了几分钟的思考,我认为一个可复用的组件,要满足以下几个条件:

  • 属性:我们可以利用属性来定义一个组件的外观,或者交互方式
  • 输出:component不光好看(或不好看),可能还能输出某种状态或数据,比如一个获取输入的组件,我们需要从组件中获取输入的内容
  • 控制:component需要提供一些对外接口,以便我们对它进行控制
  • 感知:有时component需要主动将自己的内部状态通知出来。跟被动控制不同的是,感知是component主动通知外部的行为

现在,我们只需要拿着这份药方去vuejs抓药就可以了。下面是我们找到的药品的位置:

  • 属性:vuejs中明显地提供了属性的定义及访问方式,component中使用 props 来指定其支持哪些属性,使用方可以通过 :prop="config.prop" 传入属性
  • 输出:输出的实现有些技巧,后面我们专列一个小节来说明
  • 控制:想要控制某个component,必先获取它对应的句柄(与之对应的某个变量)。vuejs为组件的使用提供了一个 ref 属性,可通过 this.refs 变量引用到,直接调用其函数
  • 感知:主动通知的实现方式,一般为信号(linux进程)和事件(js),但本质是相同的,都是底层提供一种通信方式。vuejs提供了 $emit() 函数,用以向component使用方发送通知

4. 试剑:不跑起来不算验收通过

上面的实现描述不够直观,还是要看到代码才能更清楚明白。下面我们就创建一个项目来验证上面的设计。

这里我们先编写一个简单的用例,一个用以显示文本的component,同时可通过一个api来修改字体大小。

我们先安装vue命令行工具,并用其创建一个脚手架项目:

script
1
2
npm install -g @vue/cli
vue create component-demo

然后我们创建一个component,该component可定义显示一行文本,可通过一个属性来设置文本颜色。同时,我们提供两个接口以供使用方调整字体大小。

先看使用方的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- src/App.vue -->
<Label color="green" text="hello" ref="label1" />
<button @click="enlarge">enlarge</button>
<button @click="diminish">diminish</button>

<script>
export default {
name: 'App',
components: {
Label
},
methods: {
enlarge() {
this.$refs.label1.enlarge()
},
diminish() {
this.$refs.label1.diminish()
}
}
}
</script>

下面是component的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- src/components/Label.vue -->
<template>
<span :style="{color: color, 'font-size': fontSize + 'px'}">{{ text }}</span>
</template>

<script>
export default {
name: 'Label',
props: ['color', 'text'],
data() {
return {
fontSize: 18
}
},
methods: {
enlarge() {
this.fontSize += 1
},
diminish() {
this.fontSize -= 1
}
}
}
</script>

5. 转锋:同步数据给父component

上面的组件已可以很好的工作了,但是它并没有覆盖到我们上面给出的四个条件,我对比了一下,缺少 输出感知 两个条件,这两个条件实现的技术实际是同一个。

vuejs提供了一种通信方式,可以向父component发送数据,原型为 $emit( eventName, […args] ),eventName为事件名,是一个字符串,可为该事件提供参数 args。

如果我们是直接看vuejs的官方教程,官方指出v-model可以实现数据的双向绑定(数据显示<=>数据变量这两个方向)。官方也给出了v-model背后的诀窍:

你可以用 v-model 指令在表单 input、textarea 及 select 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但 v-model 本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。

text 和 textarea 元素使用 value property 和 input 事件;

下面的代码更直白一些:

1
2
3
4
<input type="text" v-model="data">

<!--等同于下面的代码-->
<input type="text" v-bind="data" v-on:input="data = $event.target.value" />

即是如此,我们便可以通过包装一个input,并给上层上送input事件来通知上层更新数据了。下面是一个片段:

1
2
3
4
5
6
7
<!--父component-->
<InputComponent v-model="data" />

<!--子component-->
<input>
<input v-bind="data" v-on:input="$emit('input', data)">
</template>

当然,框架里还提供了一个model属性,可以修改v-model绑定的属性(这里默认为value)和事件(这里默认为input)。这算是个彩蛋了。

如果要更细致的使用,还需要了解一下生命周期的概念,不过这对于使用来说并不是最核心最要紧的。

6. 回首:再来看n天精通系列

我相信n天精通是存在的,但是这不是必然的。比起精通,能够产生有实际价值的产品,才是更令人兴奋的。

所以,n天能精通也罢,不能精通也罢,并不是那么重要。

网上看到一个朋友的说法,我觉得说得挺好:工具是帮助我们解决问题的,而不是让人成为大牛的。

精通和有效,必然有先后。我觉得大可先让学习有效,真正到了一定程度,自然便精通了。

有了上面的学习,我们就可以触类旁通,继续学习相关知识,一边学习、一边享受学习的成果,不也是一件很快乐的事吗?

zabbix监控方案

概况

zabbix是一个老牌的开源分布式监控方案,它是由Alexei Vladishev在2000年左右创建(PS: 那时我刚好升初中 😀),最早主要用来监控网络和服务器的健康度和完整性。

如果按照中国的法律,到现在的2020年,zabbix已经成年。

经过近20年的发展,zabbix已由最初的网络和服务器监控扩展到应用监控和web监控等更丰富的监控目标,数据库支持已从mysql覆盖到了elasticsearch、oracle等更多的第三方数据库,告警函数也从最初的6个增加到了5.0版本的29个。

架构

zabbix的架构比较典型,分为采集端、展示端、告警端。采集端主要通过agent采集信息,并上报到server,数据存储在mysql等数据库中。

下面是zabbix数据采集端接入zabbix的架构图:

zabbix-proxy

为了性能的考虑,zabbix有类似elk等架构的典型特点,agent和server间支持加入一个proxy来均衡负载。

2019年的4.4版本中,官方开始提供基于golang的agent2作为上报端,性能和维护性上也会好很多。未来有望取代c版本的agent。

agent2源码解读

这里以5.2.0alpha1为准,来分析一下agent2的核心实现。代码对应哈希86c3d82,agent2对应的代码在src/go下。

zabbix同时支持主动上报和被动请求两种,主动上报是agent主动发起的上报,而被动请求是由server端发起。

agent2代码量比较庞大,但是目录结构和处理逻辑比较清晰。核心代码主要集中在internal/agent中。

从agent2的总体设计来看,整个执行逻辑分为scheduler.Manager、server.Connector、ServerListener三个主要逻辑(goroutine启动),主程序对三个goroutine进行监控。

其中,SchedulerManager负责数据采集、配置更新的调度;ServerConnecter用于与server的连接,负责更新监控项配置;ServerListener主要监控server端发起的被动请求。

这里以三个小条目来分别讨论agent2的实现,分别是监控项同步、plugin机制以及一个简单的plugin实现。

监控项同步

server端启动时会监听一个端口(默认10051),agent2会定期从该端口拉取监控项,默认每两分钟拉取一次。

前面说过,拉取是由server.Connector来做的,它从server端获取配置项后,做一些简单处理,并将其包装为updateRequest,然后将配置作为input输入到scheduler.Manager中。

scheduler.Manager的数据结构如下:

1
2
3
4
5
6
7
8
9
10
11
type Manager struct {
input chan interface{}
plugins map[string]*pluginAgent
pluginQueue pluginHeap
clients map[uint64]*client
aliases *alias.Manager
// number of active tasks (running in their own goroutines)
activeTasksNum int
// number of seconds left on shutdown timer
shutdownSeconds int
}

input是一个输入源,上面server.Connector即是将updateRequest发送到Manager.input。

Manager会一直监听Manager.input,检测到*updateRequest后,会将updateRequests中包含的配置更新到plugins字段中,后面会根据plugins做收集和上报。

额外补充个,input还有两类输入,performer*queryRequestperformer 对应监控采集事件,而 *queryRequest 对应被动请求的事件。

需要注意的是,虽然agent2默认支持很多监控项,但是它们不会主动采集。只有server端配置了监控项的指标才会被采集上报。

plugin机制

agent2中,plugin机制为plugin预留了多种接口可供实现不同阶段和功能的逻辑,如下:

  • Collector:定期收集指标。一般与Exporter结合,以便将收集的指标上报给server
  • Exporter:上报指标数据到server。Exporter是这几个接口中唯一一个可以并行处理的接口
  • Runner:可在该接口实现初始化和清理操作
  • Watcher:可实现自定义的指标采集调度方式
  • Configurator:处理配置内容,如配置的有效性判断

plugin可以选择实现其中的一组或多组接口,来提供不同功能的处理。

agent2提供了一个指标注册函数,来注册plugin及其支持的指标,接口定义如下:

1
func RegisterMetrics(impl Accessor, name string, params ...string)

其中,impl即为实现了plugin接口的结构体,下面将使用一个例子来展示如何注册。

scheduler.Manager在初始化时,会将支持的plugins信息写入Manager.plugins。上面说的配置项也会更新到这里。plugins字段是一个核心字段。

插件示例

这里给出一个利用插件机制实现的简单插件,该插件提供了两个metric分别生成一个随机整数和一个随机浮点数。

该插件只实现了Exporter接口,并在init()中注册了该插件及两个监控项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type Plugin struct {
plugin.Base
}

func (Plugin) Export(key string, params []string, context plugin.ContextProvider) (interface{}, error) {
switch key {
case "test.randint":
return rand.Int(), nil
case "test.randfloat":
return rand.Float32(), nil
default:
return nil, errors.New(fmt.Sprintf("Unknown metric %s!", key))
}
return rand.Int(), nil
}

var impl Plugin

func init() {
plugin.RegisterMetrics(&impl, "test",
"test.randint", "Export random int.",
"test.randfloat", "Export random float.")
}

plugins下有plugins_darwin.go、plugins_linux.go、plugins_windows,里面分别配置了不同操作系统下支持的监控项,把test插件记录进去即可支持该监控了。

zabbix优缺点

作为一个老牌监控方案,zabbix提供的默认监控比较全面,而且技术上有一定积累,运行比较稳定。zabbix的部署相对比较简单。

zabbix的数据库多为mysql等结构型数据库或者elasticsearch等文档型数据库,缺少对时序数据库的支持。

虽然官网也有集成influxdb的方案(链接见参考部分),但都不是官方集成,且需要与其它平台结合使用。最受欢迎的方案3年也只有60个star,可见这种方式并不十分招人待见。

网上也有人提出使用postgresql的tsdb扩展,但是如果没有使用历史,而性能又比较关键的前提下,大家还是会比较关注数据落到何种db。

也有一些方案使用了混搭的方式来寻找出路,比如使用zabbix的采集端,使用grafana来作展示等,方案也是多种多样。这也可见监控相关产品的思路都已比较成熟,各有亮点,解耦也比较彻底。

参考

open-falcon 告警判定

引子

上一篇文章 《open-falcon 监控上报》中讲了open-falcon的设计以及监控上报的逻辑,这篇文章呈接上篇,继续探讨告警判定的逻辑。

监控数据是一个写多读少的数据,几乎每时每刻都在写,但是查看的时间一般很短。用户不可能一直盯着它,告警即是做为另一个入口补充进来的。告警如何配置、如何触发,如何优雅的通知到接收人,是告警要解决的问题。

在设计告警时,可能会存在几个坑,一是告警过多,如阈值设置太低,而没有做告警收敛就会遇到这种情况,既耗费了用户的精力,还有可能把告警通道阻塞;二是告警失效,如果后端发送如短信超载被禁就会出现该种情况。

第一种情况,告警过多一般会设置告警收敛,如一条告警告了几次就不要再告了,或者过一段时间再告,防止接收人被告警信息淹没;同时,一些不重要的告警可以设置告警屏蔽,先屏蔽它一小时,一小时后再来处理。

第二种情况,在确保告警通道稳定的情况下,最好提供多种方案,短信、微信、邮箱、钉钉,一个挂了还有另外的补全。同时,可发送多个相关人员,A没注意到B也可以处理。

下面主要从以下几个方面说一下open-falcon的告警逻辑:

  • 数据链路:agent 采集的数据是如何流转到告警判定模块的
  • 判定规则:judge 如何判定一份监控数据是否异常
  • 告警配置:open-falcon 中是如何管理告警配置的

数据链路

上篇文章中讲到,agent 组件会以多种方式(builtins、plugin、push)采集监控数据。open-falcon组件间多以rpc协议通信,agent通过rpc把采集到的数据上传到transfer,再由transfer分发给judge。

这里judge可以部署多个节点来分担负载,transfer会通过一致性哈希来决定将信息分发到哪个judge节点。

judge即是告警判定组件,它会从hbs中获取portal的监控规则配置,然后判断推送过来的数据是否应该告警,并将告警信息写入redis。

后端的alarm从redis中取告警信息,并通过不同的告警通道转发出去。

判定规则

judge 的判定条件有两种,一种是 strategy,它通过绑定告警模板到某个主机组生效;另一种是 expression,它可以监控非主机的情况。

这两种方式的实现核心是一样的,这里以机器监控为例。

假设我们希望定义一个告警规则,某个主机组中的主机,如果负载连续3次上报均大于4,则告警。我们会在该主机组绑定的告警模板下,添加一条判定规则:load.1min all(#3) > 4。

其中all为聚合函数,all(#3) > 4 意思是最近三次的值均大于4。open-falcon 提供了多种聚合函数,目前可以支持的聚合函数有:max, min, all, sum, avg, diff, pdiff, lookup, stddev。

该规则下发到judge后,被 ParseFuncFromString() 函数解析为一个包含判定逻辑的结构体:

1
2
3
type Function interface {
Compute(L *SafeLinkedList) (vs []*model.HistoryData, leftValue float64, isTriggered bool, isEnough bool)
}

all聚合函数对应的逻辑结构体如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
type AllFunction struct {
Function
Limit int
Operator string
RightValue float64
}

func (this AllFunction) Compute(L *SafeLinkedList) (vs []*model.HistoryData, leftValue float64, isTriggered bool, isEnough bool) {
vs, isEnough = L.HistoryData(this.Limit)
if !isEnough {
return
}

isTriggered = true
for i := 0; i < this.Limit; i++ {
isTriggered = checkIsTriggered(vs[i].Value, this.Operator, this.RightValue)
if !isTriggered {
break
}
}

leftValue = vs[0].Value
return
}

该函数可以判定历史数据是否触发告警规则,触发则生成一条 event,event 结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
// 机器监控和实例监控都会产生Event,共用这么一个struct
type Event struct {
Id string `json:"id"`
Strategy *Strategy `json:"strategy"`
Expression *Expression `json:"expression"`
Status string `json:"status"` // OK or PROBLEM
Endpoint string `json:"endpoint"`
LeftValue float64 `json:"leftValue"`
CurrentStep int `json:"currentStep"`
EventTime int64 `json:"eventTime"`
PushedTags map[string]string `json:"pushedTags"`
}

告警管理

前面提到过,open-falcon的告警规则是绑定到主机组的,这也是为了简化告警的配置。数台机器怎么配都不是问题,但是当机器增长到上千上万台,不分组管理是不现实的。

上面还提到一个 expression 的告警配置,这个配置比较适合于业务告警的场景,摆脱了主机组等实体的限制,更为灵活机动。

一些补充

值得一提的是,transfer 的后端支持比较丰富,目前还支持 tsdb 和 influxdb 数据库,可灵活使用后端来完成定制化的功能。

judge 的判定规则是基于阈值的,难以做一些同比环比类的告警,不过可以通过其它方式来自主实现,应该不是很大的问题。