Archive for November, 2013

作者:AngryFox 分类: Uncategorized November 29th, 2013 暂无评论


问题
极致的业务特性
流畅的消息收发
及时的通知
省电
省流量
瘦客户端
困难的后台-终端同步
同步多样数据:账户信息、通讯录、消息、朋友圈等
及时通知与同步
移动网络下的可靠同步
省流量与电量
方案
极简的同步协议
后台与终端只需要沟通一个数字,后台即可知道终端缺失的所有数据。
变更序列号/版本号:
后台对用户数据的每项变更,都赋予一个单调递增的序列号,即用户的每项数据都有一个全局递增序列号。
后台每次给终端发送数据都会带上所发送的所有数据的最大序列号。
终端每次请求数据时都会带上已经接受到的最大序列号。
高效的通知机制
ios Apple Push Network Service
Android等-长连接
GPRS/EDGE信令风暴优化
自适应心跳间隔调节
三层后台架构
Arch of weixin backend

统一的RPC框架
根据ProtocolBuffer定义生成服务器框架和客户端
服务器:开发人员填充接口实现
客户端: 应用方本地调用客户端提供的接口函数
屏蔽网络细节
支持基于TCP/UDP的网络调用
支持长短连接
丰富的功能
基于sharding的SET分布
基于一致性哈希的无状态存储
服务透明重定向
丰富的自动化监控:(1)QPS;(2)响应时间;(3)排队时间;(4)各个接口的调用频率与返回状态码分布;(5)各个服务之间的调用拓扑
高并发的协程RPC
服务器同步的调用模型相较异步模型更容易学习、使用和调错。但一台服务器支撑的进程数和线程数是非常有限的。

基于用户态线程(协程)的RPC

单机可以支撑数万甚至十万的用户态线程,仅受CPU和内存约束
提高并发性和性能
用户态线程RPC的实现

基于makecontext/getcontext/swapcontext
Hook Network: read/write/epoll
用户态线程调度
就近访问
就近访问IDC
就近网络接入:覆盖各大运营商
CDN:上传下载图片
腾讯自建CDN
AKAMAI
多IDC分布提升用户体验
国内复杂的网络环境
超过1亿的海外用户
分布在全球
更为多样化的网络环境
每个IDC都提供完整的功能和所需访问的所有数据
每个IDC有共同的数据和独立数据
账户信息全球一致
用户数据各自独立:(1)一个用户只属于某个IDC;(2)用户属性、关系链、消息;(3)按需共享的SNS数据-照片、评论、红心,减少网络同步带宽消耗
IDC分布数据的高可靠最终一致性保证
账户数据和SNS数据主备访问模型
用户所在的IDC是主IDC
其他IDC相对这个用户所在IDC都是备IDC
更新由主扩散到备
弱实时性跨IDC更新采用基于Zookeeper仲裁的主备任务队列
收拢跨IDC访问接口
重做保证跨IDC更新的可靠性
数据序列号在重做时保证达到最终一致性
关系链跨IDC更新
隐私控制要求实时性
直接跨IDC网络调用
后台批处理重试失败请求
容错和容灾机制
单IDC
用户按SET分布,各个SET之间独立

高可用的异地容灾
每个服务的主IDC都有一个灾备IDC
挑战:终端在主备IDC切换时的无缝连接;主备间的数据一致性。

作者:AngryFox 分类: Uncategorized November 29th, 2013 暂无评论


riak中的 Gossip是一个应用于p2p中的理论,他的主要过程是通过一个N节点集群中的每一个节点与所有其它N-1个节点进行通信,实现数据的同步,
Gossip基于不要求集群中有一个Master存在,并能以病毒传播的方式将一个节点的变更传达到所有其它节点,而系统增加或减少一个结点的成本几乎为0。
riak的最终(强)一致的请求过程:
  首先介绍一下Riak的请求处理过程,以数据冗余N份存储,每次读取其中的R份,写操作需要写W份。
  通过计算得出请求的key所在的N个节点
  向这N个节点依次发起请求
  等待这N个节点中的W个(如果是写操作的)或R个(如果是读操作)返回成功
  返回相应的数据给客户端
  失败场景
  下面讨论几种失败场景:
  1.读取数据前其中一个节点故障
  数据以W=3成功写入三份
  其中一个节点故障
  数据再以R=3读取三份,发起三个请求
  此次读操作会返回了not_found
  而这次系统在检测到了数据只有两份,会启动修复器将数据备份一份到secondary节点上,以保证有三份备份
  后续的读操作将会从primary上读到两份,从secondary上读到一份数据,以实现成功读到三份数据。
  2.读取数据前其中两个节点故障
  数据以W=3成功写入三份
  其中两个节点故障
  数据再以R=3读取三份,发起三个请求
  此次读操作会返回了not_found
  后续的读操作将会从primary上读到一份,从secondary上读到两份数据,以实现成功读到三份数据。
  3.读取数据前三个节点全部故障
  数据以W=3成功写入三份
  其中三个节点故障
  数据读取操作将会永远返回not_found,直到某个节点恢复
  4.写操作前一个节点故障
  一个节点故障
  数据以W=3发起三个写请求
  一个secondary节点承担了其中的一个写请求
  后面的读请求会正常的读到三份数据
  5.写操作前一个节点故障,后来又恢复了
  一个节点故障
  数据以W=3发起三个写请求
  一个secondary节点承担了其中的一个写请求
  故障节点又恢复了
  在60秒内,一个叫hinted handoff的过程会启动,将secondary中的数据迁移到刚刚恢复的primary中
  在hinted handoff过程完成后,数据就恢复正常了
  6.在hinted handoff 过程中进行读写操作
  在hinted handoff过程中,由于原来的primary节点是启动的,所以数据读写操作都会到这个节点上来执行,这时候可能由于一些值还没有备份回来,所以会导致这个节点暂时的not_found返回。
  7.在两个primary节点故障后一个又恢复期间进行读写操作
  这时候刚刚恢复的节点会进行hinted handoff过程,而读写操作依然会由于not_found的发生而启动修复器进行数据备份到secordary中。

作者:AngryFox 分类: Uncategorized November 28th, 2013 暂无评论

1:整体初始化过程
在twemproxy启动过程中,需要core.c在core_start这个函数中创建context,这个core_start函数里就完成了配置文件的解析以及相应组件的初始化。core_start这个函数,我们来看看它的具体实现。
1):struct context *ctx;
首先,在这个函数的开始,创建了一个context对象。context,顾名思义:上下文,我们看它的定义:
一个context含有id, cf(配置),stats,

pool:server_pool, 因为一个配置文件中会有很多个节点,每一个节点其实就是一个server_ pool, 所以这里的pool是一个array.
ep:epoll描述符,twemproxy的事件驱动是基于epoll的。
nevent:调用epoll_waite是返回epoll_event的个数。

2):定义了context之后,便是对context这个变量分配内存以及对里边的元素做一些初始化。然后主要的初始化过程都在core_ctx_create这个函数里。
3):ctx->cf = conf_create(nci->conf_filename);这个函数里将会读取并解析配置文件,相关的配置将会存储在conf这样一个对象中并返回赋值给ctx->cf。
4): status = server_pool_init(&ctx->pool, &ctx->cf->pool, ctx); 这个函数主要完成的是将配置中的每一个节点(conf_pool)转换成server_pool,最后的转换结果会存储在ctx->pool(array对象)中。
5): ctx->stats = stats_create(nci->stats_port, nci->stats_addr, nci->stats_interval, nci->hostname, &ctx->pool); 为每一个server创建stats。
6): status = event_init(ctx, EVENT_SIZE_HINT);初始化epoll。
7): status = server_pool_preconnect(ctx);根据配置中的preconnect这项判断是否跟后端server建立一个连接。
8):status = proxy_init(ctx);对每一个server_pool(配置文件中的一个节点)初始化proxy, 即创建套接字,绑定proxy监听的接口,给套接字增加读写事件处理函数,并添加到epoll中。
通过这八个步骤,server_pool, stats, proxy都初始化完成,core_create创建context成功返回,最后回到nc_run启动事件轮训,程序启动,调用epoll_wait开始事件的处理。

2:server_pool及server的初始化
在context的创建过程中会调用server_pool_init进行server_pool的初始化,现在我们看下server_pool具体的初始化过程。即nc_server.c: server_pool_init这个函数,首先通过 npool = array_n(conf_pool);通过这里,计算出总共配置了多少个server_pool(配置节点)。然后通过array_init初始化server_pool这个数组,其中这个array_init函数主要干的就是给server_pool的elem分配npool*sizeof(server_pool)大小的内存,然后将其他的属性也相应的初始化。注意,调用server_pool_init这个函数的时候,传递的第一个数是context-pool, 这个pool是一个array类型的数组,所以下边会调用array_each,并且传递一个函数指针作为回调函数,在这个回调函数里,会根据配置文件的配置信息,完成每一个struct server_pool的初始化工作。

作者:AngryFox 分类: Uncategorized November 27th, 2013 暂无评论

【问题】[notice] Data directory /opt/riak-2.0.0pre5/dev/dev1/bin/../data/leveldb is not mounted with ‘noatime’. Please remount its disk with the ‘noatime’ flag to improve performance

使用noatime属性优化Linux文件系统读取性能

当文件被创建,修改和访问时,Linux系统会记录这些时间信息。当系统的读文件操作频繁时,记录文件最近一次被读取的时间信息,将是一笔不少的开销。所以,为了提高系统的性能,我们可以在读取文件时不修改文件的atime属性。可以通过在加载文件系统时使用notime选项来做到这一点。当以noatime选项加载(mount)文件系统时,对文件的读取不会更新文件属性中的atime信息。设置noatime的重要性是消除了文件系统对文件的写操作,文件只是简单地被系统读取。由于写操作相对读来说要更消耗系统资源,所以这样设置可以明显提高服务器的性能。注意wtime信息仍然有效,任何时候文件被写,该信息仍被更新。

比如我要在根文件系统使用noatime,可以编辑/etc/fstab文件,如下:
proc /proc proc defaults 0 0
none /dev/pts devpts gid=5,mode=620 0 0
/dev/md0 /boot ext3 defaults 0 0
/dev/md1 none swap sw 0 0
/dev/md2 / ext3 defaults,noatime 0 0

要使设置立即生效,可运行:
mount -o remount /

这样以后系统读取 / 下的文件时将不会再修改atime属性。

作者:AngryFox 分类: Uncategorized November 26th, 2013 暂无评论

12核 32G内存 Intel(R) Xeon(R) CPU E5-2620 0 @ 2.00GHz
cpu MHz : 2000.071
cache size : 15360 KB
easy_install安装Tornado

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

application = tornado.web.Application([
    (r"/", MainHandler),
])

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

压测结果很不理想,在并发时延迟比较大
100000 fetches, 234 max parallel, 1.2e+06 bytes, in 48.4201 seconds
12 mean bytes/connection
2065.26 fetches/sec, 24783.1 bytes/sec
msecs/connect: 46.7055 mean, 21001.4 max, 0.05 min
msecs/first-response: 57.7509 mean, 843.253 max, 0.395 min
HTTP response codes:
code 200 — 100000

100000 fetches, 840 max parallel, 1.2e+06 bytes, in 66.2584 seconds
12 mean bytes/connection
1509.24 fetches/sec, 18110.9 bytes/sec
msecs/connect: 328.783 mean, 45001.9 max, 0.055 min
msecs/first-response: 59.2018 mean, 3281.66 max, 0.402 min
HTTP response codes:
code 200 — 100000

10000 fetches, 50 max parallel, 120000 bytes, in 4.87321 seconds
12 mean bytes/connection
2052.04 fetches/sec, 24624.5 bytes/sec
msecs/connect: 0.0577655 mean, 0.628 max, 0.034 min
msecs/first-response: 24.255 mean, 93.154 max, 8.762 min
HTTP response codes:
code 200 — 10000

10000 fetches, 1000 max parallel, 120000 bytes, in 21.2817 seconds
12 mean bytes/connection
469.887 fetches/sec, 5638.65 bytes/sec
msecs/connect: 704.157 mean, 21000.6 max, 0.057 min
msecs/first-response: 93.8377 mean, 827.93 max, 0.816 min
HTTP response codes:
code 200 — 10000

http_load -p 50 -fetches 10000 /opt/1.txt

一些运维命令;
核心态:ring 0,操作系统内核代码运行的模式,可以
无限制访问系统存储、外部设备
用户态:ring 3,应用程序自身代码运行的模式,非特
权状态,能够访问的地址空间是有限的
系统调用:核心态开放给用户态的接口,以软中断实现
函数调用:用户态层面的概念,与核心态无关
strace命令能够显示所有在程序中使用的系统调用
进程组是一个或多个进程的集合,进程组ID是该组的领
头进程的pid
会话是一个或多个进程组的集合,当前与终端交互的进
程组为前台进程组,只能有一个,其余为后台进程组
进程已死亡,但父进程没有收尸,该进程就成僵尸进程
僵尸进程不打开任何文件,几乎不占内存,但是占据进
程表的资源,进程表记录pid、进程状态、CPU时间等
僵尸状态是每个子进程结束时必经的状态
系统监控中出现大量僵尸进程,应检查其父进程代码

汇总区域显示五个方面系统性能信息
负载:时间、登录用户数、系统平均负载
进程:运行、睡眠、停止、僵尸
CPU:用户态、核心态、NICE、空闲、等待IO、中断等
内存:总量、已用、空闲(系统角度的)、缓冲、缓存
交换分区:总量、已用、空闲

iostat -c:见top命令的CPU信息
iostat -d:吞吐率、每秒读写、总的读写
iostat -x:每秒读写次数、平均IO队列长度、平均每次
IO操作总耗时、平均每次IO操作的实际执行时间、IO使
用率
一个隐藏的等式:%util = (r/s + w/s) * svctm / 10

IO瓶颈有什么症状?
%util很高
await远远大于svctm
avgqu-sz比较大

为什么不算IO瓶颈?
%util还是很高,比较容易迷惑人
await接近svctm,略大一点点
avgqu-sz不大

打印内核“环缓冲区”的信息
内核“环缓冲区”保存Linux开机信息
也记录某些程序的内存错误(segfault)
dmesg | tail -l

查看文件系统阻塞,解决umount busy问题

lsof | wc -l

汇总统计tcp连接状态
netstat | awk ‘/^tcp/ {++S[$NF]} END {for(a in S)} print a, S[a]‘
查看端口被哪个进程占用

netstat -anp | grep 8080
vmstat -n 4

free memory急剧减少,回收buffer和cache也无济于事,于是大量使用
交换分区(swpd),页面交换(swap)频繁,读写磁盘数量(io)增
多,缺页中断(in)增多,上下文切换(cs)次数增多,等待IO的进程数
(b)增多,大量CPU时间用于等待IO(wa)

监控磁盘IO,与iostat类似
sar -bd 5
sar -R:空闲内存页面、缓冲页面和缓存页面的变化趋势
sar -r:当前内存的使用信息,与free命令类似
sar -S:当前交换分区的使用信息,与free命令类似
sar -H:当前巨页页面的信息

sar -uwq 5
proc/s:平均每秒创建的进程数
cswch/s:平均每秒的上下文切换次数
runq-sz:等待运行时间片的进程数
plist-sz:进程列表的总数
ldavg-x:过去一段时间内的系统平均负载,即进程状态为R或D的加权平均数
blocked:当前被IO阻塞的进程数

iostat -d -x 1
分析:IO集中在sda8,%util很高,await远远大于svctm,avgqu-sz也很大,
满足IO瓶颈的全部条件
使用df查看磁盘空闲块和inode

df -i /data1

分析:均有空闲,排除资源接近耗尽,此外,业务日志写在sdb1分区,也可排
除写入大量业务日志带来的压力
,使用lsof查看什么进程操作/home目录下的文件
$ lsof -n | grep home
排查、修改nginx的fastcgi设置
解决:nginx.conf中没有fastcgi的设置,即使用默认参数,fastcgi缓冲区大小
为36k,对于部分response来说太小,把它适当改大,问题解决。使用
vmstat、iostat等命令再查看皆恢复正常

作者:AngryFox 分类: Uncategorized November 26th, 2013 暂无评论

包括easy_install和Pip,介绍一下Pip的安装与使用:
准备:
$ curl -O http://python-distribute.org/distribute_setup.py
$ python distribute_setup.py
安装:
$ curl -O https://raw.github.com/pypa/pip/master/contrib/get-pip.py
$ python get-pip.py
使用方法:
$ pip install SomePackage
$ pip search “query”
$ pip install –upgrade SomePackage
$ pip install –upgrade SomePackage==version

补充:
包安装后的py文件路径:/usr/local/lib/python2.7/dist-packages

easy_install tornado

> wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py
> python ez_setup.py –user

http://www.sqlite.org/sqlite-amalgamation-3.6.20.tar.gz

作者:AngryFox 分类: Uncategorized November 26th, 2013 暂无评论

LevelDb首先会去查看内存中的Memtable,如果Memtable中包含key及其对应的value,则返回value值即可;如果在Memtable没有读到key,则接下来到同样处于内存中的Immutable Memtable中去读取,类似地,如果读到就返回,若是没有读到,那么只能万般无奈下从磁盘中的大量SSTable文件中查找。因为SSTable数量较多,而且分成多个Level,所以在SSTable中读数据是相当蜿蜒曲折的一段旅程。总的读取原则是这样的:首先从属于level 0的文件中查找,如果找到则返回对应的value值,如果没有找到那么到level 1中的文件中去找,如此循环往复,直到在某层SSTable文件中找到这个key对应的value为止(或者查到最高level,查找失败,说明整个系统中不存在这个Key)。

那么为什么是从Memtable到Immutable Memtable,再从Immutable Memtable到文件,而文件中为何是从低level到高level这么一个查询路径呢?道理何在?之所以选择这么个查询路径,是因为从信息的更新时间来说,很明显Memtable存储的是最新鲜的KV对;Immutable Memtable中存储的KV数据对的新鲜程度次之;而所有SSTable文件中的KV数据新鲜程度一定不如内存中的Memtable和Immutable Memtable的。对于SSTable文件来说,如果同时在level L和Level L+1找到同一个key,level L的信息一定比level L+1的要新。也就是说,上面列出的查找路径就是按照数据新鲜程度排列出来的,越新鲜的越先查找。

为啥要优先查找新鲜的数据呢?这个道理不言而喻,举个例子。比如我们先往levelDb里面插入一条数据{key=”www.same.com” value=”科技”},过了几天,same网站改名为:69同城,此时我们插入数据{key=”www.same.com” value=”69同城”},同样的key,不同的value;逻辑上理解好像levelDb中只有一个存储记录,即第二个记录,但是在levelDb中很可能存在两条记录,即上面的两个记录都在levelDb中存储了,此时如果用户查询key=”www.same.com”,我们当然希望找到最新的更新记录,也就是第二个记录返回,这就是为何要优先查找新鲜数据的原因。

前文有讲:对于SSTable文件来说,如果同时在level L和Level L+1找到同一个key,level L的信息一定比level L+1的要新。这是一个结论,理论上需要一个证明过程,否则会招致如下的问题:为神马呢?从道理上讲呢,很明白:因为Level L+1的数据不是从石头缝里蹦出来的,也不是做梦梦到的,那它是从哪里来的?Level L+1的数据是从Level L 经过Compaction后得到的,现在的Level L+1层的SSTable数据是从原来的Level L中来的,现在的Level L比原来的Level L数据要新鲜,所以可证,现在的Level L比现在的Level L+1的数据要新鲜。

SSTable文件很多,如何快速地找到key对应的value值?在LevelDb中,level 0一直都爱搞特殊化,在level 0和其它level中查找某个key的过程是不一样的。因为level 0下的不同文件可能key的范围有重叠,某个要查询的key有可能多个文件都包含,这样的话LevelDb的策略是先找出level 0中哪些文件包含这个key(manifest文件中记载了level和对应的文件及文件里key的范围信息,LevelDb在内存中保留这种映射表),之后按照文件的新鲜程度排序,新的文件排在前面,之后依次查找,读出key对应的value。而如果是非level 0的话,因为这个level的文件之间key是不重叠的,所以只从一个文件就可以找到key对应的value。

最后一个问题,如果给定一个要查询的key和某个key range包含这个key的SSTable文件,那么levelDb是如何进行具体查找过程的呢?levelDb一般会先在内存中的Cache中查找是否包含这个文件的缓存记录,如果包含,则从缓存中读取;如果不包含,则打开SSTable文件,同时将这个文件的索引部分加载到内存中并放入Cache中。这样Cache里面就有了这个SSTable的缓存项,但是只有索引部分在内存中,之后levelDb根据索引可以定位到哪个内容Block会包含这条key,从文件中读出这个Block的内容,在根据记录一一比较,如果找到则返回结果,如果没有找到,那么说明这个level的SSTable文件并不包含这个key,所以到下一级别的SSTable中去查找。

从之前介绍的LevelDb的写操作和这里介绍的读操作可以看出,相对写操作,读操作处理起来要复杂很多,所以写的速度必然要远远高于读数据的速度,也就是说,LevelDb比较适合写操作多于读操作的应用场合。而如果应用是很多读操作类型的,那么顺序读取效率会比较高,因为这样大部分内容都会在缓存中找到,尽可能避免大量的随机读取操作。

对于levelDb来说,读取操作如果没有在内存的memtable中找到记录,要多次进行磁盘访问操作。假设最优情况,即第一次就在level 0中最新的文件中找到了这个key,那么也需要读取2次磁盘,一次是将SSTable的文件中的index部分读入内存,这样根据这个index可以确定key是在哪个block中存储;第二次是读入这个block的内容,然后在内存中查找key对应的value。

levelDb中引入了两个不同的Cache:Table Cache和Block Cache。

作者:AngryFox 分类: Uncategorized November 26th, 2013 暂无评论

ALLOC.H 说明内存管理函数(分配、释放等)。
ASSERT.H 定义 assert调试宏。
BIOS.H 说明调用IBM—PC ROM BIOS子程序的各个函数。
CONIO.H 说明调用DOS控制台I/O子程序的各个函数。
CTYPE.H 包含有关字符分类及转换的名类信息(如 isalpha和toascii等)。
DIR.H 包含有关目录和路径的结构、宏定义和函数。
DOS.H 定义和说明MSDOS和8086调用的一些常量和函数。
ERRON.H 定义错误代码的助记符。
FCNTL.H 定义在与open库子程序连接时的符号常量。
FLOAT.H 包含有关浮点运算的一些参数和函数。
GRAPHICS.H 说明有关图形功能的各个函数,图形错误代码的常量定义,正对不同驱动程序的各种颜色值,及函数用到的一些特殊结构。
IO.H 包含低级I/O子程序的结构和说明。
LIMIT.H 包含各环境参数、编译时间限制、数的范围等信息。
MATH.H 说明数学运算函数,还定了 HUGE VAL 宏, 说明了matherr和matherr子程序用到的特殊结构。
MEM.H 说明一些内存操作函数(其中大多数也在STRING.H 中说明)。
PROCESS.H 说明进程管理的各个函数,spawn…和EXEC …函数的结构说明。
SETJMP.H 定义longjmp和setjmp函数用到的jmp buf类型, 说明这两个函数。
SHARE.H 定义文件共享函数的参数。
SIGNAL.H 定义SIG[ZZ(Z] [ZZ)]IGN和SIG[ZZ(Z] [ZZ)]DFL常量,说明rajse和signal两个函数。
STDARG.H 定义读函数参数表的宏。(如vprintf,vscarf函数)。
STDDEF.H 定义一些公共数据类型和宏。
STDIO.H 定义Kernighan和Ritchie在Unix System V 中定义的标准和扩展的类型和宏。还定义标准I/O 预定义流:stdin,stdout和stderr,说明 I/O流子程序。
STDLIB.H 说明一些常用的子程序:转换子程序、搜索/ 排序子程序等。
STRING.H 说明一些串操作和内存操作函数。
SYS\STAT.H 定义在打开和创建文件时用到的一些符号常量。
SYS\TYPES.H 说明ftime函数和timeb结构。
SYS\TIME.H 定义时间的类型time[ZZ(Z] [ZZ)]t。
TIME.H 定义时间转换子程序asctime、localtime和gmtime的结构,ctime、 difftime、 gmtime、 localtime和stime用到的类型,并提供这些函数的原型。
VALUE.H 定义一些重要常量, 包括依赖于机器硬件的和为与Unix System V相兼容而说明的一些常量,包括浮点和双精

作者:AngryFox 分类: Uncategorized November 26th, 2013 暂无评论

对于一个由C/C++编写的应用程序而言,程序的启动点一般都是main函数,所以对一个程序进行分析的话,也一般都是有main函数作为入口,以main函数作为入口。
找到文件nc.c定位到main,我们可以看到定义了一个变量,
struct instance nci;
下边我们看下这个instance的定义:

struct instance {
    struct context  *ctx;                        /* active context */
    int             log_level;                   /* log level */
    char            *log_filename;               /* log filename */
    char            *conf_filename;              /* configuration filename */
    uint16_t        stats_port;                  /* stats monitoring port */
    int             stats_interval;              /* stats aggregation interval */
    char            *stats_addr;                 /* stats monitoring addr */
    char            hostname[NC_MAXHOSTNAMELEN]; /* hostname */
    size_t          mbuf_chunk_size;             /* mbuf chunk size */
    pid_t           pid;                         /* process id */
    char            *pid_filename;               /* pid filename */
    unsigned        pidfile:1;                   /* pid file created? */
};

这个instance就相当于是一个twemproxy实例,后边整个程序的初始化很多都会用到。接下来就调用了nc_set_default_options()和nc_get_options()两个函数,其中nc_get_options这个函数是读取命令行参数。其中比较重要的几个参数分别有:
a) test_conf : 用于设置在启动twemproxy之前是否要对配置文件做检查,以确保配置文件格式的正确。
然后就会调用nc_pre_run,启动之前做一些预处理,包括:初始化日志级别以及日志文件;设置是否后台运行;对信号做初始化处理;是否创建pid文件。
接下来调用nc_run开始启动proxy;这个函数完成的工作就是调用core_start创建context,然后进入死循环调用core_loop开始整个事件循环的处理,接受请求并处理。当然,core_start以及core_loop这两个函数里边还包含了大量的处理工作,包括,配置文件解析以及读取,相关组件(server_pool,conf,context)的初始化等等,这些后边详细讲述。
总体来说,启动流程就是这些步骤,如下图:

作者:AngryFox 分类: Uncategorized November 24th, 2013 暂无评论

最终通过修改网络连接方式位桥接(Bridge Adapter)的方式,直接把网卡地址给Centos用。
修改步骤:关闭虚拟机,在管理界面选择虚拟机–>设置–>网络:

在visualbox中刚装好虚拟机后,默认没有配置eth0,所以用putty工具无法连接上虚拟机中的系统。
配置eth0的方法如下:

用vim打开eth0的配置文件:/etc/sysconfig/network-scripts/ifcfg-eth0
将以下内容拷入此配置文件中
DEVICE=”eth0″
HWADDR=”08:00:27:6B:AD:E0″
NM_CONTROLLED=”yes”
ONBOOT=”yes”
TYPE=Ethernet
BOOTPROTO=dhcp
DEFROUTE=yes
IPV4_FAILURE_FATAL=yes
IPV6INIT=no
NAME=”System eth0″

然后启动eth0

[root@RHEL6x86 network-scripts]# ifconfig eth0 up
重启一下系统或者重启一下网络服务
[root@RHEL6x86 network-scripts]# /etc/init.d/network restart
正在关闭接口 eth0: [确定]
关闭环回接口: [确定]
弹出环回接口: [确定]
弹出界面 eth0:
正在决定 eth0 的 IP 信息…完成。
[确定]
然后就可以用putty连接上虚拟机中的linux了。