分类: C/C++
2013-08-07 10:23:06
首先说说缓冲这部分的工作流程:
1.客户端发来GET或者HEAD请求,包好If-None-Match或者If-Match,以及一串由服务器发送的ETAG码。
2.服务器收到请求,先在缓存系统中查找是否应该文件,如果有返回该文件的相关信息。如果没有将该文件的缓存信息,则stat读取该文件的相关信息加入缓存树中。
3.缓存中的文件信息中包含符文的ETAG码,核对是否与客户端发来的ETAG一致,如果一致,返回304,否则返回200与文件内容,并告知新的ETAG码。
这篇文章主要介绍下状态缓存模块和ETAG。
第一ETAG部分
什么是ETAG:简单的说,ETAG就是文件的一个身份标识,如果文件变化了,那么它对应的ETAG也会改变。比如apache和lighttpd的etag生成都是使用inode+mtime+size三部分组成,只要文件改变,这个一定会变的。
如果客户要访问某一文件,发现该文件之前访问过,且没有改变,那么就可以利用客户端的缓存文件,而不需要重新下载一次,节约网络资源。
这部分代码保存在etag.c中
使用inode+mtime+size生成etag标签
etag_is_equal(buffer *etag, const char *matches);//比较两个etag是否相等
int etag_create(buffer *etag, struct stat *st, etag_flags_t flags);//根据标签生成初始的etag
int etag_mutate(buffer *mut, buffer *etag);//通常用来将初始的etag进行算法变形
这部分代码很简单,说说功能,不详细解释了。
第二部分,文件状态的缓存。
使用状态缓存就是为了节约stat()函数调用的时间(难道这个函数很占用资源?这里的原理不很理解)
首先介绍下大概的流程:总共有两个树,一棵是文件树,一棵是目录树。fam这个服务仅仅监控目录文件的变化。
首先在文件树中查找文件,如果找到该文件,而且上次节点改变时间戳与服务器时间戳一样(说明文件最近加入缓存,没有改变),返回该文件。
如果时间戳不对应,说明文件可能变了,在目录树种看看目录有没有改变,如果目录变了则认为文件一定变了,重新缓存然后返回,如果没有改变,说明目录下文件也没有改变,那么返回节点。
如果没有找到文件所在目录,重新缓存。
如果没有找到文件,重新缓存。
整个过程主要用到下面三个结构体:
typedef struct {
splay_tree *files; 树的每个节点保存一个stat_cache_entry;
buffer *dir_name; /* for building the dirname from the filename */
splay_tree *dirs; 该树的节点是fam_dir_entry
FAMConnection *fam; //整个缓存模块只需要一个连接,然后每个文件需要一个请求
int fam_fcce_ndx;
buffer *hash_key; 保存hash值
} stat_cache;
typedef struct {
buffer *name; //文件名
buffer *etag; //文件的etag
struct stat st; //文件的状态
time_t stat_ts; //上次这个节点改变的时间
char is_symlink; //标记是否是符号链接文件
int dir_version; //对应的目录版本号
int dir_ndx;
buffer *content_type; //文件的类型,对应http协议中的content-type
} stat_cache_entry;
typedef struct {
FAMRequest *req; //fam请求
FAMConnection *fc; //fam连接,来自stat_cache结构体
buffer *name; //目录的名字
int version;//版本号
} fam_dir_entry;
stat_cache *stat_cache_init(void)
static stat_cache_entry * stat_cache_entry_init(void)
static void stat_cache_entry_free(void *data)
static fam_dir_entry * fam_dir_entry_init(void)
void stat_cache_free(stat_cache *sc)
static void fam_dir_entry_free(void *data)
上面的初始化函数无非就是申请空间,赋初值。不解释了。
static uint32_t hashme(buffer *str) 采用DJB hash的方法对要加入树的数据进行hash,然后保存hash值
static int stat_cache_attr_get(buffer *buf, char *name) //XFS系统获取文件属性
handler_t stat_cache_handle_fdevent(server *srv, void *_fce, int revent) 根据传入的事件类型,处理事件
这个函数的流程图:
handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **ret_sce)
注意问题1:首先在stat_cache_handle_fdevent函数可以看出,当目录改变(文件改变也会使目录改变)时候版本号+1.但文件对应的版本号没变,所以两个版本号比较久可以判断文件是否变动。
问题2:符号连接
static int stat_cache_lstatt(server *srv, buffer *dname, struct stat *lst) 判断dirname保存的文件名是否是符号链接
大家注意在上面的流程图中,比较文件的时候先查找hash,然后对比了文件名。而目录则没有比较文件名。
关于这个函数,我有下面几个问题没搞懂:
第一处
ctrl的作用
第二处
buffer_append_long(sc->hash_key, con->conf.follow_symlink);
conf.follow-syslink应该来自配置文件,为什么不保存在svr中而是con中呢?
第三处
svr是全局变量,
所有线程共享
buffer_copy_string_buffer(sc->hash_key, name);
改变了svr中sc的这个值,其他的线程受影响
第五处
获取mimetype值
static int stat_cache_tag_old_entries(server *srv, splay_tree *t, int *keys, size_t *ndx)
int stat_cache_trigger_cleanup(server *srv)
这两个函数配合,删除距上次被访问超过2秒的节点
知识点1:http://blog.csdn.net/liuaigui/article/details/5050697
DJB HASH
知识点2:
int attr_get (const char *path, const char *attrname, char *attrvalue, int *valuelength, int flags);
需要包含attr/attributes.h 只有XFS系统可以只用
知识点3:
etag只有在GET和HEAD的时候有效,如果不是这个请求但是使用了etag,返回412
知识点4:linux时间处理 DST GMT struct tm
如果对一个DST时间,调用strchr(“....GMT”),会返回什么? CST 时区问题
知识点5:strchr strptime函数的使用
知识点6:
If-None-Match 和 ETAG
Last-Modified 与If-Modified-Since
ETags和If-None-Match是一种常用的判断资源是否改变的方法。类似于Last-Modified和HTTP-IF-MODIFIED- SINCE。但是有所不同的是Last-Modified和HTTP-IF-MODIFIED-SINCE只判断资源的最后修改时间,而ETags和 If-None-Match可以是资源任何的任何属性
当客户端再次请求该资源时,将在HTTP Request中加入If-None-Match信息(ETags的值)。如果服务器验证资源的ETags没有改变(该资源没有改变),将返回一个304状态;否则,服务器将返回200状态,并返回该资源和新的ETags。
当一个请求中ETag和LastModified都在,两个都满足才返回304
知识点5::stat和lstat的区别:当文件是一个符号链接时,lstat返回的是该符号链接本身的信息;而stat返回的是该链接指向的文件的信息。
知识点6:
FAM可以把监控文件的变化反映在一个文件描述符内,这样就可以使用select等模型来接受通知
FAM API
FAMCancelMonitor 当你的程序结束监管文件或者目录的时候调用,此时不再监管fr指定的文件。调用该函数后会发送一个FAMAcknowledge的事件,意味着你可以重新使用request number了
FAMClose 关闭fam连接
FAMPending
FAMNextEvent
FAMCONNECTION_GETFD
int FAMOpen(FAMConnection* fc)
FAMCONNECTION_GETFD(fc)
int FAMOpen2(FAMConnection* fc, const char* appName)
nt FAMMonitorDirectory(FAMConnection *fc, char *filename,FAMRequest* fr, void* userData) //随意提供,如果制定文件发生变动,该数据会保存在返回的FAMEvent的userdata部分,用来传递信息
int FAMMonitorFile(FAMConnection *fc,char *filename,FAMRequest* fr,void* userData)
FAMConnection
结构体中包含一个socket文件描述符,用来和程序通信,告诉程序什么时候文件发生改变
如果采用select来获取通知,就需要获取FAMConection中包含的文件描述符。
如果用poll模式,可以直接调用FAMPending(),该函数不等待,相当于设置0的poll。
得到通知后,由FAMNextEvent来获取下一个通知事件
FAMRequest监管目录或者文件的时候初始化