TrafficServer源码初体验

转载 2012年03月22日 17:32:47

http://skysbird.bsdchina.org/?p=207

最近在研究TrafficServer的源码,将一些心得体会分享出来。先上一张主程序(proxy/Main.cc)流程图,用Understand反向工程出来的,比较有助于代码的理解和分析。

由于TrafficServer在2011年才刚刚由Yahoo释放出源码,所以关于TrafficServer的源码分析的文档非常有限,淘宝网的技术专家们对此研究的比较深入,有一些基础的知识可以参照淘宝CDN团队的官方博客。http://rdc.taobao.com/blog/cs/?tag=traffic-server 对TrafficServer的基本使用,以及事件系统,网络子系统有个简单的介绍。

本博是一方面是希望能够在阅读代码过程中能做个笔记,另一方面也希望能够深入的了解TrafficServer的源码实现,从而发现TrafficServer现有代码中存在的问题以及可以优化的地方。(这是因为TrafficServer这个项目经历了近10年,无数工程师都参与过开发,因此整个代码结构组成并不是非常清晰,而且存在很多已经废弃的代码。)

话不多说,开始从上面这张图入手分析:

这张图可以看到很多分支,实际上上面很大的一坨东西是在分析TrafficServer的各种配置文件,并且将配置文件的内容载入到内存中,使用高效的数据结构进行存储,以便可以在取得某个配置值的时候,可以达到较高的效率。

一直到  eventProcessor.start(num_of_net_threads); 这行之前,大部分工作都是在处理配置文件相关的工作。

eventProcessor.start(num_of_net_threads); 内部实现如下:

int
EventProcessor::start(int n_event_threads)
{
char thr_name[MAX_THREAD_NAME_LENGTH];
int i;

// do some sanity checking.
static int started = 0;
ink_release_assert(!started);
ink_release_assert(n_event_threads > 0 && n_event_threads <= MAX_EVENT_THREADS);
started = 1;

n_ethreads = n_event_threads;
n_thread_groups = 1;

int first_thread = 1;

for (i = 0; i < n_event_threads; i++) {
EThread *t = NEW(new EThread(REGULAR, i));
if (first_thread && !i) {
ink_thread_setspecific(Thread::thread_data_key, t);
global_mutex = t->mutex;
t->cur_time = ink_get_based_hrtime_internal();
}
all_ethreads[i] = t;

eventthread[ET_CALL][i] = t;
t->set_event_type((EventType) ET_CALL);
}
n_threads_for_type[ET_CALL] = n_event_threads;
for (i = first_thread; i < n_ethreads; i++) {
snprintf(thr_name, MAX_THREAD_NAME_LENGTH, "[ET_NET %d]", i);
all_ethreads[i]->start(thr_name);
}

Debug("iocore_thread", "Created event thread group id %d with %d threads", ET_CALL, n_event_threads);
return 0;
}

这部分代码实现的功能是启动事件处理子系统,事件处理子系统说白了就是一组线程(EThread类型,分类别存储,将不同的类型事件的线程用eventthread存储)。这些线程分别被start起来,start种会调用this->execute方法,execute方法的实现如下:

void
EThread::execute() {
switch (tt) {

case REGULAR: {
Event *e;
Que(Event, link) NegativeQueue;
ink_hrtime next_time = 0;

// give priority to immediate events
for (;;) {
// execute all the available external events that have
// already been dequeued
cur_time = ink_get_based_hrtime_internal();
while ((e = EventQueueExternal.dequeue_local())) {
if (!e->timeout_at) { // IMMEDIATE
ink_assert(e->period == 0);
process_event(e, e->callback_event);
} else if (e->timeout_at > 0) // INTERVAL
EventQueue.enqueue(e, cur_time);
else { // NEGATIVE
Event *p = NULL;
Event *a = NegativeQueue.head;
while (a && a->timeout_at > e->timeout_at) {
p = a;
a = a->link.next;
}
if (!a)
NegativeQueue.enqueue(e);
else
NegativeQueue.insert(e, p);
}
}
bool done_one;
do {
done_one = false;
// execute all the eligible internal events
EventQueue.check_ready(cur_time, this);
while ((e = EventQueue.dequeue_ready(cur_time))) {
ink_assert(e);
ink_assert(e->timeout_at > 0);
if (e->cancelled)
free_event(e);
else {
done_one = true;
process_event(e, e->callback_event);
}
}
} while (done_one);
// execute any negative (poll) events
if (NegativeQueue.head) {
if (n_ethreads_to_be_signalled)
flush_signals(this);
// dequeue all the external events and put them in a local
// queue. If there are no external events available, don't
// do a cond_timedwait.
if (!INK_ATOMICLIST_EMPTY(EventQueueExternal.al))
EventQueueExternal.dequeue_timed(cur_time, next_time, false);
while ((e = EventQueueExternal.dequeue_local())) {
if (!e->timeout_at)
process_event(e, e->callback_event);
else {
if (e->cancelled)
free_event(e);
else {
// If its a negative event, it must be a result of
// a negative event, which has been turned into a
// timed-event (because of a missed lock), executed
// before the poll. So, it must
// be executed in this round (because you can't have
// more than one poll between two executions of a
// negative event)
if (e->timeout_at < 0) {
Event *p = NULL;
Event *a = NegativeQueue.head;
while (a && a->timeout_at > e->timeout_at) {
p = a;
a = a->link.next;
}
if (!a)
NegativeQueue.enqueue(e);
else
NegativeQueue.insert(e, p);
} else
EventQueue.enqueue(e, cur_time);
}
}
}
// execute poll events
while ((e = NegativeQueue.dequeue()))
process_event(e, EVENT_POLL);
if (!INK_ATOMICLIST_EMPTY(EventQueueExternal.al))
EventQueueExternal.dequeue_timed(cur_time, next_time, false);
} else {                // Means there are no negative events
next_time = EventQueue.earliest_timeout();
ink_hrtime sleep_time = next_time - cur_time;
if (sleep_time > THREAD_MAX_HEARTBEAT_MSECONDS * HRTIME_MSECOND) {
next_time = cur_time + THREAD_MAX_HEARTBEAT_MSECONDS * HRTIME_MSECOND;
sleep_time = THREAD_MAX_HEARTBEAT_MSECONDS * HRTIME_MSECOND;
}
// dequeue all the external events and put them in a local
// queue. If there are no external events available, do a
// cond_timedwait.
if (n_ethreads_to_be_signalled)
flush_signals(this);
EventQueueExternal.dequeue_timed(cur_time, next_time, true);
}
}
}

case DEDICATED: {
// coverity[lock]
if (eventsem)
ink_sem_wait(eventsem);
MUTEX_TAKE_LOCK_FOR(oneevent->mutex, this, oneevent->continuation);
oneevent->continuation->handleEvent(EVENT_IMMEDIATE, oneevent);
MUTEX_UNTAKE_LOCK(oneevent->mutex, this);
free_event(oneevent);
break;
}

default:
ink_assert(!"bad case value (execute)");
break;
}                             /* End switch */
// coverity[missing_unlock]
}

上述的execute代码是事件处理子系统中的核心 事件处理部分,因此我用Understand将此部分实现反向工程出流程图,看起来比较清晰:

上面这张图从 (;;) 发射出来的红色的no的分支箭头,应该是Understand对源码分析错误,我从源码中并没有看到会有这个走向。实际上在REGULAR的case分支下,是一个for(;;)的永循环,专门负责循环处理常规事件。

代码中的 EventQueueExternal 是事件模型种提到的外部队列,EventQueue是内部队列。事件的处理过程是这样的:先从外部队列中取出一个事件e,查看这个事件是否需要立刻执行(通过判断e->timeout_at可以确定是否需要立刻执行),如果需要立刻执行,则调用process_event立刻执行事件(稍后会分析process_event的实现细节);如果取出的事件e,并不是一个需要立刻执行的事件,且不属于(epoll之类的网络事件),则将这个事件加入到内部队列EventQueue中;如果取出的事件e属于epoll这样的网络事件,则将其加入到NegativeQueue中,随后会有针对这种事件的处理。外部队列的事件处理完成后,接下来处理内部队列中的事件(这部分事件有刚刚在处理外部事件时加入到内部队列的事件)内部队列EventQueue的实现是用的优先级队列的方式,并且从代码上观察,应该是只要处理掉一个内部队列的事件就会再次尝试检测内部队列是否有需要处理的事件。直至一次检查过程中没有需要被处理的事件,才会完成对内部队列事件的检查。

bool done_one;
do {
done_one = false;
// execute all the eligible internal events
EventQueue.check_ready(cur_time, this);
while ((e = EventQueue.dequeue_ready(cur_time))) {
ink_assert(e);
ink_assert(e->timeout_at > 0);
if (e->cancelled)
free_event(e);
else {
done_one = true;
process_event(e, e->callback_event);
}
}
} while (done_one);

完成内部队列中的事件检查后,会检查刚刚提到的NegativeQueue队列中的事件(在处理NegativeQueue事件前,貌似源码种可以看到对于外部队列中的事件又做了一次检查,基本流程和上面的差不多,只不过加入了一些阻塞方法,例如EventQueueExternal.dequeue_timed(cur_time, next_time, false); 暂时没看太明白(从代码注释中理解,最后一个false表示,当外部队列中没有事件的时候,也不会阻塞线程等待,而会将事件取出,放到本地队列中)。然后进入到对poll事件的处理(NegativeQueue),代码如下:

// execute poll events
while ((e = NegativeQueue.dequeue()))
process_event(e, EVENT_POLL);
if (!INK_ATOMICLIST_EMPTY(EventQueueExternal.al))
EventQueueExternal.dequeue_timed(cur_time, next_time, false);
} else {                // Means there are no negative events
next_time = EventQueue.earliest_timeout();
ink_hrtime sleep_time = next_time - cur_time;
if (sleep_time > THREAD_MAX_HEARTBEAT_MSECONDS * HRTIME_MSECOND) {
next_time = cur_time + THREAD_MAX_HEARTBEAT_MSECONDS * HRTIME_MSECOND;
sleep_time = THREAD_MAX_HEARTBEAT_MSECONDS * HRTIME_MSECOND;
}

 

相关文章推荐

traffic server cache源码分析二 读写逻辑分析

http://blog.chinaunix.net/uid-23242010-id-2183028.html             第一章指出,ts对大文件与小文件的存储方式略有不同。对于...

TrafficServer源码初体验–2

http://skysbird.bsdchina.org/?p=237 刚刚在开篇中介绍到eventProcessor.start方法,这时trafficserver的事件处理子系统已经被启...

Scala深入浅出进阶经典 第59讲:Scala中隐式转换初体验实战详解以及隐式转换在Spark中的应用源码解析

Scala深入浅出进阶经典 第59讲:Scala中隐式转换初体验实战详解以及隐式转换在Spark中的应用源码解析
  • sd637
  • sd637
  • 2015-09-25 23:28
  • 202

tizen alpha 源码下载,sdk下载,sdk初体验

tizen alpha 源码下载,sdk下载,sdk初体验 tizen alpha 源码下载 下载网址: http://source.tizen.org/git/ 现在有了改变: http...

<java API源码初体验>2---collection集合之LinkedList原理分析

list—LinkedList:1.LinkedList源码:package java.util;/** * Doubly-linked list implementation of the {@c...

lua初体验(lua源码编译,IED安装)

和Cocos2dx群的大侠们交流后,得出一个结论,大侠都有lua! 那我也要当大侠嘛,所以我也得会lua 跟cocos2dx不同,lua的教程要少的多,从环境到IDE都如此。。 下面我就汇总一下...

F8App-ReactNative项目源码分析1-初体验

近期开始研究Facebook f8app项目,目标是理解Facebook官方React Native f8app的整体技术架构,给公司目前几个的React Native项目开发提供官方经验借鉴,并对原...
  • offbye
  • offbye
  • 2016-05-18 22:26
  • 5392

spark内核揭秘-05-SparkContext核心源码解析初体验

SparkContext在获得了一系列的初始化信息后开始创建并启动TaskScheduler实例: 进入createTaskScheduler方法: spark内核揭秘-05-SparkConte...

【经验】轻量化阅读源码初体验sublime+gradlew命令

本文的目的: 快速编译、打包、修改、阅读他人代码,阅读sdk提供sample源码等。 正文一、通过gradlew编译打包apk: 正文二、通过sublime文本编辑器查看、编辑修改代码:
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)