Sun Yongyue

该用户没有分享资料

首页: http://Array


Sun Yongyue

libmemcached 0.39 故障转移无效

晚上用错一个libmemcached库,原来用0.52的,改成0.39后故障转移一起无效,起初以为代码有问题,后来才发现版本关系也有影响,改回0.52后DONE。

lrwxrwxrwx 1 sunyy sunyy   63 Dec 14 13:50 libmemcached-0.39 -> /home/sunyy/local//x86_64/libmemcached-0.39-bin-x86_64-gcc4.1.2



 

本文链接

淘宝的Tengine都改了什么

Tegine logoTengine是由淘宝Servers Platform Team基于Nginx修改的一个服务器,中午趁着大家都在睡觉,粗粗地拿了代码看下都修改了哪些东西。个人认为里边比较有用的修改是input filter和sysguard,由于只是粗看,没有留意细节,可能有错漏。以下是分代码目录的修改记录笔记:

 
0. others
1) 配置默认值的修改ON->OFF之类的
2) bug/logic相关的修改,还细看就不细说了。
 
1. core
1) 增加configure的dump
2) 增加一此log方式或方法
3) 增加内存池管理realloc
4) 增加字符串处理ngx_atoll,修改unescape_uri以支持www的form
5) 增加log的另一种时间格式
 
2. event
1) 增加统计信息request_time
2) 修改/增加ssl加密方式
 
3. http
1) 增加input filter
2) 增加server tag用于response中Server这个header的设置,还有admin/info等用于修改一些页面信息-_-
 
4. http/module
1) 增加ngx_http_concat_module模块,用于多个静态文件的合并返回
2) 增加ngx_http_footer_filter_module模块,用于在末尾插入一段东西
3) 修改ngx_http_headers_filter_module模块,按type设置expire time
4) 修改ngx_http_limit_req_module模块,增加白名单,规则似乎也有修改
5) 修改ngx_http_stub_status_module模块,记录request time
6) 增加ngx_http_sysguard_module模块,用于在cpu/mem资源不足时,报警并执行action(可认为主动拒绝服务)
 
5. misc
1) 增加ngx_backtrace_module打印堆栈信息,便于与signal相关(SIGXXX)出错时的问题定位。
 
6. os
1) 增加realloc
2) 增加了pipe
3) 增加ngx_syslog_module,用于向syslog日志服务器写日志
4) 增加了cpu/mem的信息获取方法
 
7. mail
没管
 

2012-02-14 UPDATE:
关于ngx_syslog,因为我们也有类似的需求(但不是针对syslog服务),自己此前想过这个功能的实现,看了下Tengine发现与我要求的还有不同:该功能是同步阻塞的,发送出错也是直接失败,这些应该可以改进,比如异步,非阻塞,出错时区分中断、阻塞或其他错误等。

本文链接

Nginx开发小记

 

    关于Nginx开发,应属官网推荐的两篇文章最为经典,相当多的国内文章都是用这两篇文章作为蓝本,翻译修改。这里不重复,本来是想写个系列的,列好提纲发现来来去去都是那些基础知识,木有什么好说的。不如直接对着提纲简单说一说就行了,浅尝辄止。这里不讲什么细节的,另一篇开发细节指南在准备中,会有一些细节。

一、Phase与状态机

    NginxHTTP服务,跟众多其他的网络服务一样,就是一个状态机。状态机中的各个状态/阶段在Nginx中定义为各种各样的phase,细数一下达到了11个之多。各个phase与形形色色的钩子、异步机制协作,成就了nginx的高效、稳定与强大功能。

    状态机本身并不是Nginx独有,唯一需要注意的是状态机的中断、挂起(暂且这么说吧)在不同phasehandler中对应着不同的返回值,需要理解NGX_OK/NGX_DONE/NGX_AGAIN/NGX_DECLINED等在不同阶段的作用。

 状态机

    其他需要在开发中注意的就是状态机的东西了。状态机的“状态”除了Nginx中用于标识所在阶段的变量外,就是上下文,需要理解这个上下文结构,并对其进行维护。另外,一旦打断了原本完善的状态机,就要在适当的时候把routine接回去,或是自己完成所有其他操作。为方便维护、保持代码优雅,我倾向于前者。

 

二、钩子与异步机制

    个人认为Nginx之所以有如此强大的功能,主要归结于其中形形色色的钩子和异步机制。都有哪些类型的钩子?几乎我能想到的Nginx都提供了,充斥在各个阶段,各个模块,各种功能。关于钩子的说明可以参考XXXXXX(名字隐去)相关文章,网络上应该也有一些相关文章在流传。

    需要注意的是细节,如有一些位置是可以放多个钩子,也有一些位置是排 他的,只能安放一个。如两种比较常用的逻辑介入方式中,多次设置的location  handlercontent handler)只会有一个生效,新handler会覆盖旧的;而多次向某个phase 追加handlerphase handler)则均可生效(如果没被中途打断的话)。

    异步机制,现在一般用epollepollevent什么,好多人好多书都讲,也没什么好说的。需要注意的是不同触发机器的区别,Nginx使用的是边缘触发,建立模型的时候得提醒自己:用的是边缘,用的是边缘……

边缘与水平触发

 

三、core的各种优雅

    Nginx的代码写得非常优雅,第一印象似乎耦合严重但实际上模块划分是很清晰的,看它的代码很过瘾。什么hash/array/rbtree/queue/list,简单实用。什么cpu/slab/page/buffer/shm/各种lock(部分在os),要看底层,这里也有啊,还汇编的呢。什么chain/mpool/log,细节,这就是细节。

    要说还缺点什么,真是没什么了。如果有需要,自己做个任务队列,非富一下数据结构种类,换一些更好的算法,加一点读写锁,给共享内存功能增强一点什么的。但没事也不乱折腾,够用也就行,简单一点好。

 

四、特色:subrequest

    之前虽然有一些地方也会有类似用法,但没有看到subrequest子请求这种明确的概念,或者会成为服务器一种新的标准?有另一个概念是internal redirect,看名字应该也能猜到它们的区别。相关的主要是Request结构体里边相关的三个属性/字段。最原始的用法是把多个子请求的结果合并到主请求(外部原始请求)中去,如果要改变这个默认行为,就注意下flag

    如何跟主请求的流程结合起来?跑完主请求的状态机退出来,跟着就跑下posted requests。如果自己介入了Nginx的异步机制,也需要留意除了状态机,还有posted requests。子请求天然是异步并发的,如果要强制同步,状态机上加点状态也就行了。

 

未完待补充……

本文链接

tt2mysql —— 一个异构数据库同步方案

大多数数据库都自带了同步方案,但通常是同步到同一类型的数据库。在一些特定的情况下,我们可能希望把数据从一种数据库,同步到另一种数据库,以便进行数据分析、统计、挖掘等,或是完成实时监控、实时搜索等服务。

本文介绍的就是这样一个方案,把数据从NoSQL数据库ttserver同步到MySQL上。

数据的同步过程基本上可以分解成:获取、解析、识别、处理。

获取同步(replicating)过程基本上就是处理高性能网络交互、各层通信协议、基于安全考虑的身份验证等问题的过程。解析(parsing)过程主要处理具体数据结构,由分派器(dispatcher)分派给具体的识别器(recognizer)进行识别。最终由处理器调用数据访问层完成整个过程。基本过程可见下方草图:

以上只是一个简化的草图,实际完成的时候,还有很多细节需要处理,如,
    1) 快照点的选择,以及生成快照点的方案。不稳定的数据是没用的;
    2) 协议、数据结构的可配置化。不同的场景下只需要简单配置,就能满足具体业务;
    3) 对前后端数据服务的抽象。只有抽象化,才能让它成为一个有生命力的方案;
    4) 高处理能力。异步、非阻塞、多worker、操作合并;
    5) 考虑对协议升级的兼容方案;
    6) 可支持前后端数据迁移、数据分片;
    7) 前后端多实例同时使用;
    8) 全局同步与增量同步方案同时支持,新同步点支持;
    9) 各种可能的出错:网络、数据、服务等处理、监控、告警;
    10) 稳定性、可用性;
    11) 完备的统计数据。方案做得怎么样,总要有数据才好说话吧:)

顺带把部分初期概念设计图放出来。后来已经有一些演变,但只是少数环节上。在大部分的环节上,基本思路没有太大的变化。

    经过一段时间的奋战把方案实现出来,功能验证也比较顺利通过了,实战又将跑得怎么样?具体业务及性能数字未经同意不准备在此公布,但可说侧面说一下。目前,该方案上线应该已经将近一年,除了少数几次前端迁移以外,未停过服。性能方面,一年后,在业务已经有量级发展的情况下,该方案仍能满足需求。

另外,虽然命名为tt2mysql,我从未把该方案当成是数据库之前的同步方案。以前说过会把东西总结出来,一直太懒,翻到了就写一下。

tt2mysql —— 一个异构数据库同步方案

    大多数数据库都自带了同步方案,但通常是同步到同一类型的数据库。在一些特定的情况下,我们可能希望把数据从一种数据库,同步到另一种数据库,以便进行数据分析、统计、挖掘等,或是完成实时监控、实时搜索等服务。

    本文介绍的就是这样一个方案,把数据从NoSQL数据库ttserver同步到MySQL上。

    数据的同步过程基本上可以分解成:获取、解析、识别、处理。

    获取同步(replicating)过程基本上就是处理高性能网络交互、各层通信协议、基于安全考虑的身份验证等问题的过程。解析(parsing)过程主要处理具体数据结构,由分派器(dispatcher)分派给具体的识别器(recognizer)进行识别。最终由处理器调用数据访问层完成整个过程。基本过程可见下方草图:

 

 

    以上只是一个简化的草图,实际完成的时候,还有很多细节需要处理,如,
    1) 快照点的选择,以及生成快照点的方案。不稳定的数据是没用的;
    2) 协议、数据结构的可配置化。不同的场景下只需要简单配置,就能满足具体业务;
    3) 对前后端数据服务的抽象。只有抽象化,才能让它成为一个有生命力的方案;
    4) 高处理能力。异步、非阻塞、多worker、操作合并;
    5) 考虑对协议升级的兼容方案;
    6) 可支持前后端数据迁移、数据分片;
    7) 前后端多实例同时使用;
    8) 全局同步与增量同步方案同时支持,新同步点支持;
    9) 各种可能的出错:网络、数据、服务等处理、监控、告警;
    10) 稳定性、可用性;
    11) 完备的统计数据。方案做得怎么样,总要有数据才好说话吧:)

  

    顺带把部分初期概念设计图放出来。后来已经有一些演变,但只是少数环节上。在大部分的环节上,基本思路没有太大的变化。

 

 

 

    经过一段时间的奋战把方案实现出来,功能验证也比较顺利通过了,实战又将跑得怎么样?具体业务及性能数字未经同意不准备在此公布,但可说侧面说一下。目前,该方案上线应该已经将近一年,除了少数几次前端迁移以外,未停过服。性能方面,一年后,在业务已经有量级发展的情况下,该方案仍能满足需求。

     另外,虽然命名为tt2mysql,我从未把该方案当成是数据库之前的同步方案。以前说过会把东西总结出来,一直太懒,翻到了就写一下。

本文链接

nginx http子模块conf的初始化

nginx.conf文件中有http这个模块,里边可以加入各种子模块ngx_http_module_t ,这些子模块自定义conf的初始化过程如下??

 

一、几个结构

ngx_conf_t                  // 路人甲

ngx_http_XXX_(main|srv|loc)_conf_t     // 自定义的conf结构

ngx_command_t        // 各个Directives

ngx_http_module_t   // http中的子module

ngx_module_t           // 高层module

 

二、一些函数

http中子module初始化的过程:

main -> ngx_init_cycle -> ngx_conf_param -> ngx_conf_param -> ngx_conf_handler -> ngx_http_block -> ngx_http_merge_servers

在最后两个函数中调用了ngx_http_module_t结构中各个create、init和merge函数做自定义conf结构的初始化、合并等。

 

1. Main中调用了ngx_init_cycle,做了以下操作:

// ngx_module_t  ngx_http_module是NGX_CORE_MODULE

rv = module->create_conf(cycle);

cycle->conf_ctx[ngx_modules[i]->index] = rv;

conf.ctx = cycle->conf_ctx;. //此外的conf是一个ngx_conf_t

 

2. Ngx_init_cycle调用了ngx_conf_param,把上边conf传过来

ngx_conf_param(&conf)

 

3. Nginx_conf_param调用ngx_conf_parse

 rv = ngx_conf_parse(cf, NULL);,又接收了上方

 

4. ngx_conf_parse调用ngx_conf_handler

rc = ngx_conf_handler(cf, rc);,又接收上方

 

5. Ngx_conf_handler中把conf取出并传给module中各命令的set函数。

conf = NULL;

if (cmd->type & NGX_DIRECT_CONF) {

    conf = ((void **) cf->ctx)[ngx_modules[i]->index];

} else if (cmd->type & NGX_MAIN_CONF) {

    conf = &(((void **) cf->ctx)[ngx_modules[i]->index]);

} else if (cf->ctx) {

    confp = *(void **) ((char *) cf->ctx + cmd->conf);

    if (confp) {

        conf = confp[ngx_modules[i]->ctx_index];

    }

}

rv = cmd->set(cf, cmd, conf); 

 

6. Ngx_http_module_t中的http命令的set函数为ngx_http_block,其中做了一系列的各模块自定义的conf的创建、初始化等操作。最后调用的ngx_http_merge_servers又调用了各个merge函数。

for (m = 0; ngx_modules[m]; m++) {

        ctx->main_conf[mi] = module->create_main_conf(cf);

        ctx->srv_conf[mi] = module->create_srv_conf(cf);

        ctx->loc_conf[mi] = module->create_loc_conf(cf);

}    

for (m = 0; ngx_modules[m]; m++) {

       module->preconfiguration(cf)

}

for (m = 0; ngx_modules[m]; m++) {

        rv = module->init_main_conf(cf, ctx->main_conf[mi]);

    rv = ngx_http_merge_servers(cf, cmcf, module, mi); // 此处调用了各merge

}

for (m = 0; ngx_modules[m]; m++) {

    module->postconfiguration(cf)

}

 

7. ngx_http_merge_servers又调用了各个merge函数。

for (s = 0; s < cmcf->servers.nelts; s++) {

        rv = module->merge_srv_conf(cf, saved.srv_conf[ctx_index],

                                    cscfp[s]->ctx->srv_conf[ctx_index]);

        rv = module->merge_loc_conf(cf, saved.loc_conf[ctx_index],

                                    cscfp[s]->ctx->loc_conf[ctx_index]);

}

 

nginx http子模块conf的初始化

nginx.conf文件中有http这个模块,里边可以加入各种子模块ngx_http_module_t ,这些子模块自定义conf的初始化过程如下??

 

一、几个结构

 

ngx_conf_t                  // 路人甲

ngx_http_XXX_(main|srv|loc)_conf_t     // 自定义的conf结构

ngx_command_t        // 各个Directives

ngx_http_module_t   // http中的子module

ngx_module_t           // 高层module

 

二、一些函数

http中子module初始化的过程:

main -> ngx_init_cycle -> ngx_conf_param -> ngx_conf_param -> ngx_conf_handler -> ngx_http_block -> ngx_http_merge_servers

在最后两个函数中调用了ngx_http_module_t结构中各个create、init和merge函数做自定义conf结构的初始化、合并等。

 

 

 

 

1. Main中调用了ngx_init_cycle,做了以下操作:

// ngx_module_t  ngx_http_module是NGX_CORE_MODULE

 

rv = module->create_conf(cycle);

cycle->conf_ctx[ngx_modules[i]->index] = rv;

 

conf.ctx = cycle->conf_ctx;. //此外的conf是一个ngx_conf_t

 

 

2. Ngx_init_cycle调用了ngx_conf_param,把上边conf传过来

ngx_conf_param(&conf)

 

 

3. Nginx_conf_param调用ngx_conf_parse

 rv = ngx_conf_parse(cf, NULL);,又接收了上方

 

 

4. ngx_conf_parse调用ngx_conf_handler

rc = ngx_conf_handler(cf, rc);,又接收上方

 

 

5. Ngx_conf_handler中把conf取出并传给module中各命令的set函数。

conf = NULL;

if (cmd->type & NGX_DIRECT_CONF) {

    conf = ((void **) cf->ctx)[ngx_modules[i]->index];

} else if (cmd->type & NGX_MAIN_CONF) {

    conf = &(((void **) cf->ctx)[ngx_modules[i]->index]);

} else if (cf->ctx) {

    confp = *(void **) ((char *) cf->ctx + cmd->conf);

    if (confp) {

        conf = confp[ngx_modules[i]->ctx_index];

    }

}

rv = cmd->set(cf, cmd, conf); 

 

 

6. Ngx_http_module_t中的http命令的set函数为ngx_http_block,其中做了一系列的各模块自定义的conf的创建、初始化等操作。最后调用的ngx_http_merge_servers又调用了各个merge函数。

for (m = 0; ngx_modules[m]; m++) {

 

        ctx->main_conf[mi] = module->create_main_conf(cf);

        ctx->srv_conf[mi] = module->create_srv_conf(cf);

        ctx->loc_conf[mi] = module->create_loc_conf(cf);

}    

 

for (m = 0; ngx_modules[m]; m++) {

 

       module->preconfiguration(cf)

}

for (m = 0; ngx_modules[m]; m++) {

 

        rv = module->init_main_conf(cf, ctx->main_conf[mi]);

    rv = ngx_http_merge_servers(cf, cmcf, module, mi); // 此处调用了各merge

}

for (m = 0; ngx_modules[m]; m++) {

    module->postconfiguration(cf)

}

 

 

 

7. ngx_http_merge_servers又调用了各个merge函数。

for (s = 0; s < cmcf->servers.nelts; s++) {

        rv = module->merge_srv_conf(cf, saved.srv_conf[ctx_index],

                                    cscfp[s]->ctx->srv_conf[ctx_index]);

        rv = module->merge_loc_conf(cf, saved.loc_conf[ctx_index],

                                    cscfp[s]->ctx->loc_conf[ctx_index]);

}

 

 

 

本文链接

vim中session自动保存及加载

    以前只保存单个文件最后一次编辑的信息,没有想过要保存整个会话。因为一般不会一次就同时动到几十个文件(-_-!!),每次都基本能搞定要做的事情。但是看代码不大一样,一次可以连上好多好多文件。上两个项目代码比较熟,要定位的信息在哪里很快就能找到,这次有点生,时间也短,于是想到用session自动保存、加载每次看代码的进度。同样可用在info上,不过我不用。直接上配置代码:
1 " auto sv and ld session
2 let g:AutoSessionFile="project.vim"
3 let g:OrigPWD=getcwd()
4 if filereadable(g:AutoSessionFile)
5 if argc() == 0
6 au VimEnter * call EnterHandler()
7 au VimLeave * call LeaveHandler()
8 endif
9 endif
10 function! LeaveHandler()
11 exec "mks! ".g:OrigPWD."/".g:AutoSessionFile
12 endfunction
13 function! EnterHandler()
14 exe "source ".g:AutoSessionFile
15 endfunction

vim中session自动保存及加载

    以前只保存单个文件最后一次编辑的信息,没有想过要保存整个会话。因为一般不会一次就同时动到几十个文件(-_-!!),每次都基本能搞定要做的事情。但是看代码不大一样,一次可以连上好多好多文件。上两个项目代码比较熟,要定位的信息在哪里很快就能找到,这次有点生,时间也短,于是想到用session自动保存、加载每次看代码的进度。同样可用在info上,不过我不用。直接上配置代码:
1 " auto sv and ld session
2 let g:AutoSessionFile="project.vim"
3 let g:OrigPWD=getcwd()
4 if filereadable(g:AutoSessionFile)
5 if argc() == 0
6 au VimEnter * call EnterHandler()
7 au VimLeave * call LeaveHandler()
8 endif
9 endif
10 function! LeaveHandler()
11 exec "mks! ".g:OrigPWD."/".g:AutoSessionFile
12 endfunction
13 function! EnterHandler()
14 exe "source ".g:AutoSessionFile
15 endfunction

本文链接

Memcached遍历Key

     前段时间XX升级之后出了点小问题,最后定位到是缓存数据格式变动造成的。当时就想要个工具去把memcached里的key拿出来看,之后虽然没有用上,但自己还是写了个,以备后用。

    目前功能比较简单,只是遍历所有的key,稍加修改可以 1)取出指定格式(正则等)的key;2)显示value。交互可以更好一点:)

1 # -*- coding:utf-8 -*-
2  import telnetlib
3  import re
4  def send(tn, cmd):
5 tn.write(cmd + "\r\n")
6 return tn.read_until("END\r\n")
7 HOST = "localhost"
8 PORT = 31211
9 tn = telnetlib.Telnet(HOST, PORT)
10 slabs = send(tn, "stats slabs")
11 chunk_pattern = "STAT (?P<type>\d+):chunk_size \d+\r\nSTAT \d+:chunks_per_page \d+\r\nSTAT \d+:total_pages \d+\r\nSTAT \d+:total_chunks (?P<total>\d+)\r\nSTAT \d+:used_chunks \d+\r\nSTAT \d+:free_chunks \d+\r\nSTAT \d+:free_chunks_end \d+\r\n"
12 key_pattern = "ITEM (\S+) \[(\d+) b; (\d+) s\]"
13 ret = re.findall(chunk_pattern, slabs)
14 cnt = 0
15  import datetime
16  for obj in ret:
17 cmd = "stats cachedump %s %s" % (obj[0], obj[1])
18 dump = send(tn, cmd)
19 keys = re.findall(key_pattern, dump)
20 for key in keys:
21 print "%s, %s, %s" % (key[0], key[1], datetime.datetime.fromtimestamp(float(key[2])))

ps: 网上已经有很多类似实现,好吧,我out了。

2011-04-08更新:stats cachedump 输出最多为2MB,超出部分无法得到,这个时间只能显示前边部分key:(。还没看到其他命令可以获取。

// 留意此值为回送内容的最大字节数,当回送内容达到2M时,即使没有达到用户想要的item数也会返回
    int memlimit = 2*1024*1024;
    ……
        // !!! alert:  回送内容最多为2M
        if (bufcurr + len + 6 > memlimit)  /* 6 is END\r\n\0 */
            break;
    ……

9号晚在google上搜索别人的实现,找到一个Xmemcached客户端,在1.2.2版本后支持遍历所有Key。大喜,但快速阅读相关部分代码后发现一样存在这个问题,跟作者确认过了。附上邮件中的部分内容。

原因是:stats cachedump限制了输出的最大字节数为2M,当某一slab class里的item比较多时,该命令会在输出超过2M前返回。除了key的内容以后,cachedump每条信息需要至少5+2+1+4+10+5=27个字节,贪心的情况下key长度应该为3个字节(理论值),总共30个字节,所以最大可显示的key数量不超过69904.9(理论值)个。