监控的盲点

【背景】
公司已经有较为完善的监控手段。monitor,通过累积量等一维数据监控自身的情况;智能监控,通过多维数据统计成功率和时耗。当然我们一直认为“一切尽在掌握”。

【问题】
某业务B_T的调用方发现出现超时的情况,排除网络问题,在我们看来只可能是后端服务S_T出问题了。而后查询对应的后端自身服务monitor监控正常,智能监控也变化ok。最后经后端童鞋深入排查,发现是自己server假死了。那么问题来了:
1) 为啥监控没啥变化
假死的server,在LB里面权重被放低了,所以请求量就少了,咋眼看去,monitor视图请求量几乎没变化(减少的很小),因此没有产生告警。而因为假死,所以正常的请求并未被S_T处理,所以智能监控不会被上报,成功率和时耗没有变。
2) 为啥其他业务没太大影响
S_T为很多业务服务,量大的服务,由于成功率都很高,所以通常不会有告警。而量小的业务(或者长尾业务)却很容易受到波动。
3) 为啥没有被LB完全踢掉
这个跟后端S_T的异常姿势有关系,由于是假死,偶尔处理一个请求,LB探测到S_T还alive,又会扔正常的业务数据过来,挂一会儿,又踢掉,周而复始。

【思考】
虽说根本上是后端的server开发者的问题,但是后台开发,监控为王。没有完美的程序,只有不完美的监控。这里暴露出来监控的一些特点,就是不能简单的通过自身的监控上报来判断服务质量,这样就会存在上诉的盲点,监控应该是一个纵深,立体化的过程。将调用方视角的后端服务质量纳入后端服务的监控范围,整合上下游的监控或许才能更加精准的为后端服务保驾护航。


原创文章,转载请注明: 转载自下雨天
本文链接地址: 监控的盲点

QR:  监控的盲点

记一次负载均衡不生效的问题

【背景】
公司目前使用一种基于调用方反馈来衡量后端服务的负载均衡组件,姑且称之为LB_T。正常情况下后端服务异常都会让调用方将此问题机器M_T踢掉,因为每一次成功的调用,调用方都会上报一个成功状态和时耗用以对后端可用性和负载做统计。

【问题】
但这一次只看到后端一直返回错误,而调用方却仍然再往这台机器发包。在基础组件上没有找到太多蛛丝马迹,而后进一步排查发现后端过载了仍然会向前端回包。这也就解释得通了,LB_T这种LB方式通常后端有回包就会认为是成功。虽然可以特化处理错误码,但为了整体的通用性,修改框架让后端过载时只上报自身服务质量和记录日志,不向前端回包,问题解决。

【思考】
问题虽然解决,但是引发了我们的思考:代码中使用了LB组件就一定能做到负载均衡,异常时能切换能容灾?答案是否定的:
1. 姿势非常关键。每一次请求都必定要对LB进行一次Update。
2. 上下游配合。一定要清楚的知道后端何为异常,对后端足够的了解,相互配合才能达到效果。

 

 


原创文章,转载请注明: 转载自下雨天
本文链接地址: 记一次负载均衡不生效的问题

QR:  记一次负载均衡不生效的问题

没有C++11我们如何实现c++的函数变参

传统的变参方法都是基于上古的VA_ARGS来实现的,c++11引入了变参模板,把编写变参方法的便利度提高了的一个前所未有的高度。re2这个库很好的诠释了C++里面即便没有变参模板,也能做便利的变参实现。

【背景】

FullMatch需要提取匹配到的所有子串,而正则表达式本身的灵活性决定了这里的参数(匹配到的子串)必然是变化的。

【RE2实现】

【使用c++11变参模板的实现】

RE2的变参实现通过整齐的重载operator()来实现,最大支持32个参数,因为只实现了32个重载。
而反观使用C++11的变参模板,则简化了很多代码,且没有参数32的限制。后者的便利性自然是极高的,但是在编译器的通用性上,前者更加通用。在没有C++11的情况下,google::re2又给出了实现变参的很好的一个例子。


原创文章,转载请注明: 转载自下雨天
本文链接地址: 没有C++11我们如何实现c++的函数变参

QR:  没有C++11我们如何实现c++的函数变参

轮server监控的层次

监控是服务的晴雨表,对开发者尤为重要,基本是server上线的标配,主要分两个层次。

1. 标准化监控
这里主要有两类 : 标准化的网络设备(主机监控),例如路由器交换机,通常都可以通过snmp协议采集到(即便是私有的od指标也能从生产商处获得);标准化的服务(中间件监控),例如httpd,nginx,mysql,web页面等等,通过对标准服务的特性采集,例如运行端口,进程名称,日志情况等来配置监控参数。
标准化维度的监控,绝大部分开源软件(zabbix,cacti等)都都能满足。

2. 业务服务监控
具体的业务监控,开源的东东就捉襟见肘了,仅仅少部分场景能通过脚本勉强满足,这就是为什么大部分成熟的互联网公司都有自己的一套或几套监控系统。监控 = 上报 + 绘图 + 告警,监控系统 = 上报api + 视图管理 + 告警管理。这里面又大致分成两大类 : 数据量维度的监控和状态维度的监控。

1). 数据量维度
上报点包括总请求量,总失败量,总成功量,各分支请求量,各分支失败量,各分支成功量,特殊上报量,时延区间量(例如50ms以下的处理请求量等)。
数据量是业务监控的最低层次,各项指标不正常,server必然是有问题,但指标正常并不代表server没有问题。

a) server嵌入上报api,monitor_api(int attr_id, int attr_value)
b) monitor_agent周期汇总本机的所有的attr_id的上报值发送给collect_svr
c) collect_svr对数据做处理,按(attr_id, report_ip) -> attr_value存储数据,按attr_id -> report_ip_list和report_ip -> attr_id_list存储索引关系
d) 页面上根据数据和索引关系聚合出各种视图 (attr_id, report_ip, day_timeline),(attr_id, all_report_ip, day_timeline),(report_ip, attr_id_list, day_timeline)
e) 告警模块中针对各个视图关系设置最大值/最小值等告警策略

2). 状态维度
服务的质量通常都是状态量,最常见的指标就是 时延和成功率。这两个指标基本可以反映出某条服务或接口的服务质量。

a) server调用后端结束时嵌入上报api,stat_api(int service_id, int stat_code, uint32_t cost_time)
b) stat_agent周期汇总或者直接转发本机的所有的service_id的上报数据发送给collect_svr
c) collect_svr对数据做处理,按(service_id, called_ip, report_ip) -> stat_value存储数据,按(service_id, called_ip) -> report_ip_list, (service_id, report_ip) -> called_ip_list存储索引
d) 页面上根据数据和索引关系聚合出各种视图 (service_id, report_ip, called_ip_list, day_timeline),(attr_id, called_ip, report_ip_list, day_timeline),(service_id, report_ip, called_ip, day_timeline)
e) 告警模块中对单机或者整个服务的 时延或成功率设置阀值

3. 调用链监控
仅仅是服务自身的监控是不够,还得能够拓扑出一条请求的来龙去脉。这里又分两种:这里业界通用做法都是通过一个traceId贯穿整个调用路径,然后由后端的日志平台离线分析去统计;另外前者必须保证整个公司所有路径都要规范上报,有时候比较困难,局部实现的话,在某一个程序上以调用者的视角去统计所有服务接口的各种信息去上报统计拓扑更容易实施。


原创文章,转载请注明: 转载自下雨天
本文链接地址: 轮server监控的层次

QR:  轮server监控的层次

macbook air在win7下的蓝牙问题

2014年产的macbook air,使用mac下bootcamp安装的win7,然后蓝牙无法使用,这不让我的耳机白买了么,不能忍,各种google各种折腾之后备忘下步骤 :
1. 卸载bootcamp默认蓝牙驱动
2. 驱动之家http://drivers.mydrivers.com/搜索“apple broadcom 蓝牙”下载最新版本
3. 设备管理器中蓝牙设备上右键更新驱动,进入最新版驱动目录查找


原创文章,转载请注明: 转载自下雨天
本文链接地址: macbook air在win7下的蓝牙问题

QR:  macbook air在win7下的蓝牙问题

yaaf_server的几种结构

总结下不同业务场景下yaaf(yaaf框架是个基于zmq的线程池框架写后台server)server不同的内部结构,大体有以下几种形式:

yaaf框架server结构

 

1. 单一zmq接口的dispatcher+worker(server_1)
早期,部门几乎绝大部分是web业务,中心绝大部分也是cgi+业务server这种形式,当时后台几乎都是面向cgi的,所有逻辑落在worker中,此时业务server的结构是单个zmq_dispatcher + 一组worker的形式。

2.支持多种接口的dispatcher+workerserver_2)
随着业务的增长,与外部门的合作增多,后台server除了给cgi调用之外,还会给诸如客户端提供服务接口 - 原生的udp/tcp/http等,此时框架封装了更多的dispacher负责对接不同来源的调用方,worker仍然保持尽可能的只关注业务逻辑,所以业务server的结构是多个worker_dispatcher + 一组worker的形式。

3.woker_dispatcher+client_dispatcher+worker(server_3)
随着部门越来越大,所做的业务也越来越复杂,后台存在较多的调用更底层服务的情况。此时在server中用框架封装更多的client_dispatcher,用于向后端发请求。worker继续保持尽可能的只关注业务逻辑,这时业务server的结构是worker_dispatcher + client_dispatcher + 一组worker的形式。

4.多组dispatcher+多组worker(server_4)
随着参与业务的增多,越来越多的业务场景出现。当逻辑复杂到一定程度的时候,可能会将dispatcher-worker分组,来方便编写/维护不同的逻辑代码,不同分组内的worker负责模块内相对独立的逻辑,此时的业务server结构是多个dispatcher+ 多组worker。

5.去dispatcher(server_5)
当遇到请求量巨大或者cpu密集型的业务时,dispatcher本身可能成为瓶颈,虽然框架可以平行扩展,但为了榨干单机性能实现上会去掉dispatcher让worker直接对外提供服务,此时的server结构是一组worker。


原创文章,转载请注明: 转载自下雨天
本文链接地址: yaaf_server的几种结构

QR:  yaaf_server的几种结构

zmq自连接问题

【背景】
最近的项目过程中偶然的被同一个问题骚扰:一个基础server挂了(基于zmq的服务),自动拉起的脚本不能成功重启server,查询后发现是监听的端口被绑定了,且是被一个client绑定了,每次都得kill掉那个client然后再重启。

【认知】
经过多方查阅发现是linux的自连接问题,linux允许发生自连接,而zeromq官网也有人提出了这个bug – LIBZMQ-549。那么为神马现网环境的server从来没出现过这个问题呢?因为只有在同一台机器上,client和server五元组才有可能完全相等。

【解决】
1. 保证随机分配的端口与server绑定的端口不重合(/proc/sys/net/ipv4/ip_local_port_range)
2. hack代码,发现ip/port相等立马断开

 


原创文章,转载请注明: 转载自下雨天
本文链接地址: zmq自连接问题

QR:  zmq自连接问题

用glog打印格式化的日志

glog功能强大自然不必多说,C++下的iostream对象在格式很多的情况下使用非常不方便。cout << xxx << “str1″ << yyyy << “str2″…想着就头疼。由于项目中一直用glog来大日志,所以就开始思考如何用glog打印格式化的日志,glog里面有raw_logging.h的文件支持RAW_LOG(serverty, format_string, var1, var2 …),但细细的看了注释和实际体验之后存在一些问题(it logs straight and ONLY to STDERR w/o buffering): 只支持打印到stderr,由于server程序通常都是守护进程形式运行,终端标准输出和标准错误的描述符都是被关闭了的,使用这个宏就根本打不出日志。于是乎就封装了一层新packString的小接口。

使用的时候,形如:LOG(INOF) << pack_string(“xxx”, int1, “yyy”, str2 …..)


原创文章,转载请注明: 转载自下雨天
本文链接地址: 用glog打印格式化的日志

QR:  用glog打印格式化的日志

mysqlpp编译成静态库

项目中为了方便部署,通常都是将库封装成静态库供实际的server程序链接,但是有些第三方库的编译选项中只提供了动态库,mysqlpp就是其中之一。要么用其他库例如mysqlconncpp,要么就自己改。果断任性的选择了后者。

修改bakefile的配置文件,添加如下片段(类似于cmake的CMakeLists.txt),然后重新生成一遍makefile即可

 


原创文章,转载请注明: 转载自下雨天
本文链接地址: mysqlpp编译成静态库

QR:  mysqlpp编译成静态库

shell中注入常用工具

开发中经常会用到一些转换,例如时间戳与格式时间,ip字符串和ip整形,大小端等。最早都是跑到相应的工具网站上去进行转换,甚是不便,于是有了…

【第一阶段】
将这几个工具集成到了一个chrome的插件 – devtool。自己用着很方便,但好景不长,新版本chrome禁掉了第三方插件,虽然搞来搞去还是给搞出来了可以用,不过心里已经有了一块疙瘩。

【第二阶段】
linux终端下开发,何不就在终端下的封装一套工具。于是有了一系列的xxxxxx

【第三阶段】
第二阶段用了一段时间,经常忘了命令每次使用又要去查,也有点不便,于是,何不模仿类似的函数种到shell环境里。将工具封装成函数放到shell中,取名尽量用python,c++里面最常见的名字,例如inet_aton/inet_ntoa,htonl/ntohl等,同时用前缀模拟名字空间。

 


原创文章,转载请注明: 转载自下雨天
本文链接地址: shell中注入常用工具

QR:  shell中注入常用工具