mochiweb源码分析(二)

2012年5月30日 没有评论

原创文章,转载请注明: 转载自pagefault

本文链接地址: mochiweb源码分析(二)

这次主要来看mochiweb如何处理http协议以及如何将外部模块加载到mochiweb框架中。

首先在上一篇的分析最后,我们知道当accept句柄之后,mochiweb最终会调用call_loop方法,那么我们就从call_loop开始

call_loop({M, F}, Socket) ->
    M:F(Socket);
call_loop({M, F, [A1]}, Socket) ->
    M:F(Socket, A1);
call_loop({M, F, A}, Socket) ->
    erlang:apply(M, F, [Socket | A]);
call_loop(Loop, Socket) ->
    Loop(Socket).

阅读全文…

mochiweb源码分析(一)

2012年5月19日 4 条评论

原创文章,转载请注明: 转载自pagefault

本文链接地址: mochiweb源码分析(一)

这篇主要分析下mochiweb的整体结构。

我这里看的代码是github上的最新代码( https://github.com/mochi/mochiweb )。

由于mochiweb是一个框架,因此我们就从他自带的简单例子入手,来看他是如何工作的。我们就从keepalive.erl开始。

首先来看这个模块的start函数,也就是它的启动函数:

-define(LOOP, {?MODULE, loop}).

start(Options = [{port, _Port}]) ->
    mochiweb_http:start([{name, ?MODULE}, {loop, ?LOOP} | Options]).

可以看到启动函数非常简单,那就是直接调用mochiweb_http模块的start函数。那么我们整个分析流程,就从这个模块的start函数开始。这里要注意,我们可以看到是将当前回调模块的loop函数传递给了mochiweb_http,这就给后续调用回调,提供了接口。

%% @spec start(Options) -> ServerRet
%%     Options = [option()]
%%     Option = {name, atom()} | {ip, string() | tuple()} | {backlog, integer()}
%%              | {nodelay, boolean()} | {acceptor_pool_size, integer()}
%%              | {ssl, boolean()} | {profile_fun, undefined | (Props) -> ok}
%%              | {link, false}
%% @doc Start a mochiweb server.
%%      profile_fun is used to profile accept timing.
%%      After each accept, if defined, profile_fun is called with a proplist of a subset of the mochiweb_socket_server state and timing information.
%%      The proplist is as follows: [{name, Name}, {port, Port}, {active_sockets, ActiveSockets}, {timing, Timing}].
%% @end
start(Options) ->
    mochiweb_socket_server:start(parse_options(Options)).

阅读全文…

Nginx配置文件解析详解

2012年4月28日 1 条评论

原创文章,转载请注明: 转载自pagefault

本文链接地址: Nginx配置文件解析详解

Nginx的配置解析相关的部分比较绕,比如为何要有4重指针,比如NGX_MAIN_CONF , loc_conf,NGX_DIRECT_CONF有什么区别呢?这些我前面的blog都有些涉及,这次主要是把配置这块完全拿出来然后来分析下。

首先来看配置解析时的数据结构,这里主要是ngx_conf_t,这个结构保存了解析配置文件所需要的一些域,这个是非常重要的一个数据结构,我们详细来看这个结构:

struct ngx_conf_s {
//当前解析到的命令名
    char                 *name;
//当前命令的所有参数
    ngx_array_t          *args;

//使用的cycle
    ngx_cycle_t          *cycle;
//所使用的内存池
    ngx_pool_t           *pool;
//这个pool将会在配置解析完毕后释放。
    ngx_pool_t           *temp_pool;
//这个表示将要解析的配置文件
    ngx_conf_file_t      *conf_file;
//配置log
    ngx_log_t            *log;

//主要为了提供模块的层次化(后续会详细介绍)
    void                 *ctx;
//模块类型
    ngx_uint_t            module_type;
//命令类型
    ngx_uint_t            cmd_type;

//模块自定义的handler
    ngx_conf_handler_pt   handler;
//自定义handler的conf
    char                 *handler_conf;
};

阅读全文…

hotwheels源码剖析

2012年4月8日 1 条评论

原创文章,转载请注明: 转载自pagefault

本文链接地址: hotwheels源码剖析

在霸爷的推荐下,看了hotwheels的代码,接下来我就来分析下hotwheels的代码(主要是server端代码),hotwheels是干吗的呢,介绍在这里:
https://github.com/tolbrino/hotwheels

Janus is a messaging server optimized to unicast over TCP to thousands of clients subscribed to topics of interest.

The ultimate goal is to maintain a latency of less than 2 seconds for 20 thousand clients on Amazon EC2 (small instance).

首先来看janus.app:

{application, janus,
 [{description, "Janus"},
  {vsn, "0.0.1"},
  {id, "janus"},
  {modules, [barrier,
             bin,
             bot,
             client_proxy,
             common,
             flashbot,
             histo,
             janus,
             janus_acceptor,
             janus_admin,
             janus_app,
             janus_flash,
             launcher,
             mapper,
             pubsub,
             topman,
             t,
             transport,
             util
            ]},
  {registered, [janus_sup, 
                janus_topman_sup,
                janus_proxy_mapper_sup,
                janus_transport_sup,
                janus_listener]},
  {applications, [kernel, 
                  stdlib, 
                  mnesia,
                  inets
                 ]},
  {mod, {janus_app, []}},
  {env, []}
 ]
}.

阅读全文…

分类: erlang, 源码阅读 标签: ,

nginx中cache的设计和实现(一)

2012年3月21日 没有评论

原创文章,转载请注明: 转载自pagefault

本文链接地址: nginx中cache的设计和实现(一)

Nginx的cache实现比较简单,没有使用内存,全部都是使用文件来对后端的response进行cache,因此nginx相比varnish以及squid之类的专门做cache的server,可能效果不会那么好。特别如果cache内容比较大的话。不过还有一种折衷的处理,那就是挂载一个内存盘,然后让nginx cache到这个盘。

我这里的看的代码是1.1.17.

首先来看Nginx中如何开启cache,http cache主要是应用在upstream中的,因此upstream对应的两个命令来启用cache,一个是xxx_cache_path(比如proxy_cache_path),它主要是用来创建管理cache的共享内存数据结构(红黑树和队列).一个是xxx_cache,它主要是使用前面创建的zone。

先来看第一个命令,xxx_cache_path,它会调用ngx_http_file_cache_set_slot函数,在看这个函数之前,先来看ngx_http_file_cache_t这个数据结构,它主要用来管理所有的cache文件,它本身不保存cache,只是保存管理cache的数据结构。每一个xxx_cache_path都会创建一个ngx_http_file_cache_t.
阅读全文…

分类: nginx, server 标签: , ,

c reference manual读书笔记(五)

2012年3月4日 没有评论

原创文章,转载请注明: 转载自pagefault

本文链接地址: c reference manual读书笔记(五)

这次主要是介绍一些c语言中的类型转换。

1 在c语言里面,除了位域之外所有的数据对象都是表示为一堆抽象的存储单元,每一个存储单元都是由很多位组成的,每位的值不是1就是0,并且每一个存储单元都必须有唯一的地址,并且他们的每一个的大小都是和char的大小一致。每个存储单元有多少位,c语言并没有要求,可是每个存储单元必须能够存储基本字符集的每一个字符。 在标准c中也称存储单元为byte。

2 c程序员一般来说不需要在意一些机器的对齐限制,因为编译器会做对齐操作,但是,c也给了程序员忽略对齐限制的能力,比如cast一个指针到另外的类型。不过这里要注意,假设你要将一个类型为S的指针转换成类型T的指针,此时如果S的对齐因子如果不小于T的对齐因子,那么这个转换就是安全的(也就是说此时你取T的值,是没问题的). 可是如果S的对齐因子如果小于T的对齐因子,此时会出现两种情况,第一种就是当使用转换后的指针时,直接出错。第二种就是硬件或者编译器来帮你找到一个合适的指针来使用这个地址。

3 c99中定义了指针类型uintptr_t和intptr_t这两个值并不是指针的大小,而是指针类型不会超过这两个值, 这里要注意,很多实现中函数指针和数据指针大小是不一样的。而NULL只是数据空指针。对象的值和对象类型的值是不一样的,因为会有一些padding,而这些padding是没有定义的,
阅读全文…

nginx的upstream分享

2012年1月5日 2 条评论

原创文章,转载请注明: 转载自pagefault

本文链接地址: nginx的upstream分享

今天给同事做的分享.

分类: nginx, server 标签:

SPDY协议介绍

2011年10月22日 没有评论

原创文章,转载请注明: 转载自pagefault

本文链接地址: SPDY协议介绍

SPDY的主页: http://www.chromium.org/spdy

我主要看的是SPDY Protocol Drafts 3,这个草稿现在还没完成,google的人将它放在github上面: http://mbelshe.github.com/SPDY-Specification/

按照我的理解,SPDY只是在性能上对http做了很大的优化(比如它的核心思想就是尽量减少连接个数),而对于http的语义并没有做太大的修改(删除了一些头),基本上还是兼容http.

SPDY相对于HTTP,主要是有下面4个增强:

1 Multiplexed requests

对于一条SPDY连接,并发的发起多少request没有任何限制,其实也就是可以拥有多条stream(接下来会介绍stream).

2 Prioritized requests

提供具有优先级的请求(同一个SPDY 连接)。这个主要是解决了HTTP中的pipeline请求是严格的FIFO的。比如有多个request,如果先到的一个request处理时间比较长,则后面的request会被阻塞住,而在SPDY中,就会优先处理高优先级的stream中的frame(后面会介绍这个)

3 Compressed headers

在SPDY中,头是可以被压缩的。

4 Server pushed streams

Server可以主动的push数据给client,而不需要客户端的request.

———————————————————————

然后来看SPDY中的一些基本的概念。

首先在SPDY中的第一个概念是session,这里的Session也就是代表一条tcp连接。

在SPDY中第二个概念就是frame,frame也就是SPDY中server和client之间交互的数据。 SPDY Framing Layer是在tcp之上的一层,而当client端和server端建立连接之后,他们之间的交互数据就是frame。 SPDY中分为2种类型的frame,分别是control frame 和data frame。(具体frame的结构,我这里就不介绍了,可以去看协议中的介绍).而每种frame都有对应的flag.

第三个是Stream的概念,一条tcp连接,可以有多条的Stream,每个stream都有一个stream id.在SPDY中有3种control frame来控制Stream的生命周期,分别是:

SYN_STREAM – Open a new stream
SYN_REPLY – Remote acknowledgement of a new, open stream
RST_STREAM – Close a stream

可以看到,和tcp建立连接很像。这里要注意Stream也是有优先级的。如果一端发送一个设置了FLAG_FIN标记的frame,则这个stream将会成为半关闭的.(也就是不会再发送数据,而只能够接收收据,这个和tcp的半关闭很类似).如果一个发送端发送了带有FLAG_FIN标记的flag,如果它再次发送数据,则将会收到一个RST_STREAM的frame.

要注意,只有synstreamframe(建立一个stream的控制frame)才拥有priority,也就是说在SPDY中优先级只到stream这个级别,只有某个stream中的request会被优先处理,而同一个stream中的frame则类似于http的行为。

从stream我们可以看到相比较于http,SPDY可以很打程度上减少建立的连接的数目,因为每个stream其实就类似于一个虚拟的连接。

假设现在需要做一个http->spdy的代理,当一个http request过来的时候,如果这个request没有body,则将会发送一个设置了FLAG_FIN标记的frame,而对应的http头都将会放入到这个frame中。可以看下go 中对于SYN frame的结构定义:

type SynStreamFrame struct {
	CFHeader             ControlFrameHeader
	StreamId             uint32
	AssociatedToStreamId uint32
	// Note, only 2 highest bits currently used
	// Rest of Priority is unused.
	Priority uint16
	Headers  http.Header
}

这里可以看到http的头就是紧挨着SPDY的frame,不过这里注意由于http是文本协议,所以最终SPDY还是会将Headers转成二进制的。

reponse类似,下面就是go中的synReplay frame,可以看到和syn类似:

// SynReplyFrame is the unpacked, in-memory representation of a SYN_REPLY frame.
type SynReplyFrame struct {
	CFHeader ControlFrameHeader
	StreamId uint32
	Headers  http.Header
}

最后来看下pushed stream,在SPDY中,server能够发送多个replay给一个request,就是说有时候server能够知道需要发送多个资源给client,此时就需要server push资源给client,而如果没有这个特性,则需要client不断的发多个request来请求多个资源。这样子就极大的RT延迟。

在SPDY中,如果server要push数据给client,则它必须选择一个已经存在的stream id,并且server端必须得设置一个比较高的优先级,以便于客户端能够迅速的发现pushed stream,然后响应它。server端能push的内容必须是客户端能够通过一个get请求所得到的资源(也就是push过来的syn frame必须包含一些头信息)。

分类: SPDY, 协议 标签: , ,

c reference manual读书笔记(四)

2011年9月18日 没有评论

原创文章,转载请注明: 转载自pagefault

本文链接地址: c reference manual读书笔记(四)

主要是介绍c语言中的类型

1 c语言里面分为数值类型(Arithmetic,包括整数,浮点.enum 等),指针类型,数组类型,结构类型,联合类型,函数类型以及void类型。c99中添加了_Bool _Complex _Imaginary三种新的类型。

2 标准c只定义了整数类型的最小精度。char至少8位,short/int至少16位,long至少32位。 long long至少64位。所有的范围都保存在limits.h中。

3 一个有符号的整数所能表示的范围,不仅依赖于位数,还依赖于2进制编码,比如现在的计算机一般都是使用2进制补码的表示(two’s complement)。这里要注意整数类型默认都是signed.

4 一个有符号的整数和无符号的混合的表达式(四则运算,比较等),都会先将有符号的整数转为无符号整数再进行。而所有的无符号的算术运算,最后都会把结果对2的n次方取摸。
阅读全文…

linux下tcp选项TCP_DEFER_ACCEPT详解

2011年8月28日 没有评论

原创文章,转载请注明: 转载自pagefault

本文链接地址: linux下tcp选项TCP_DEFER_ACCEPT详解

TCP_DEFER_ACCEPT这个选项可能大家都知道,不过我这里会从源码和数据包来详细的分析这个选项。要注意,这里我所使用的内核版本是3.0.

首先看man手册中的介绍(man 7 tcp):

TCP_DEFER_ACCEPT (since Linux 2.4)
Allow a listener to be awakened only when data arrives on the socket. Takes an integer value (seconds), this can bound the maximum number of attempts TCP will make to complete the connection. This option should not be used in code intended to be portable.

我先来简单介绍下,这个选项主要是针对server端的服务器,一般来说我们三次握手,当客户端发送syn,然后server端接收到,然后发送syn + ack,然后client接收到syn+ack之后,再次发送ack(client进入establish状态),最终server端收到最后一个ack,进入establish状态。

而当正确的设置了TCP_DEFER_ACCEPT选项之后,server端会在接收到最后一个ack之后,并不进入establish状态,而只是将这个socket标记为acked,然后丢掉这个ack。此时server端这个socket还是处于syn_recved,然后接下来就是等待client发送数据, 而由于这个socket还是处于syn_recved,因此此时就会被syn_ack定时器所控制,对syn ack进行重传,而重传次数是由我们设置TCP_DEFER_ACCEPT传进去的值以及TCP_SYNCNT选项,proc文件系统的tcp_synack_retries一起来决定的(后面分析源码会看到如何来计算这个值).而我们知道我们传递给TCP_DEFER_ACCEPT的是秒,而在内核里面会将这个东西转换为重传次数.

这里要注意,当重传次数超过限制之后,并且当最后一个ack到达时,下一次导致超时的synack定时器还没启动,那么这个defer的连接将会被加入到establish队列,然后通知上层用户。这个也是符合man里面所说的(Takes an integer value (seconds), this can bound the maximum number of attempts TCP will make to complete the connection.) 也就是最终会完成这个连接.

阅读全文…

分类: kernel, 协议 标签: , ,