libevent学习八

来源:互联网 发布:淘宝质量好小众的店铺 编辑:程序博客网 时间:2024/06/01 08:48

Bufferevents高级应用

本节讲述了Libevent的一些高级特性。


配对bufferevents

有些时候你可能需要一个可以自我沟通的程序。例:多数情况下,用户的程序会向对外连接去写入数据,有时候用户需要对它自己开放一个连接,并往这条连接内写数据。一种解决方案是通过启动一个端口只给本程序使用,但是这种方案是通过网络栈来实现自我交互,它是比较浪费资源的。第二种方案就是通过配对的bufferevent来实现,这种方案并不会真的经过socket来沟通,而是把数据写入一个bufferevent,另外一个bufferevent接收它。

接口

int bufferevent_pair_new(struct event_base *base, int options,    struct bufferevent *pair[2]);
bufferevent_pair_new()会把pair提供的两个bufferevent关联起来,使它们相互连接彼此。除了BEV_OPT_CLOSE_ON_FREE之外的所有选项都支持,BEV_OPT_CLOSE_ON_FREE不会起作用,并且BEV_OPT_DEFER_CALLBACK总是存在的。

为什么配对bufferevent需要递延回调?因为在一个bufferevent中调用一些改变bufferevent的操作,这可能会触发另一个bufferevent,然后另一个bufferevent也改变bufferevent,导致需要再调用另一个bufferevent,如此往复多次。如果回调非递延,这种情况就很容易导致栈溢出,使其他连接饿死,并且它还要求所有的回调是可重入的。

配对bufferevent支持缓存刷新;设置BEV_NORMAL或BEV_FLUSH模式强迫所有关联的数据被从一个一端传到另一端,无视水标位限制。设置BEV_FINISHED模式在对端的bufferevent上产生一个EOF事件。

释放其中一端不会自动的释放另一端,也不会产生一个EOF事件;它只会使另一端变成unlinked。一旦bufferevent是unlinked,它就不可以再读或写数据,也不会产生任何事件。

接口

struct bufferevent *bufferevent_pair_get_partner(struct bufferevent *bev)
此方法是获得bev的另一端的bufferevent。如果bev是配对bufferevent其中之一,并且对端仍然存在,那么它会返回指向对端的指针。否则,返回NULL。

过滤bufferevent

一些情况下,你想用一个bufferevent对象来传输所有的数据。你可以在这个bufferevent上增加一个压缩层,或用另一个协议来包装当前的协议。

接口

enum bufferevent_filter_result {        BEV_OK = 0,        BEV_NEED_MORE = 1,        BEV_ERROR = 2};typedef enum bufferevent_filter_result (*bufferevent_filter_cb)(    struct evbuffer *source, struct evbuffer *destination, ev_ssize_t dst_limit,    enum bufferevent_flush_mode mode, void *ctx);struct bufferevent *bufferevent_filter_new(struct bufferevent *underlying,        bufferevent_filter_cb input_filter,        bufferevent_filter_cb output_filter,        int options,        void (*free_context)(void *),        void *ctx);
bufferevent_filter_new()创建一个新的filtering bufferevent,它包装了一个已经存在的“underlying” bufferevent。所有接收的数据在被传送到filtering bufferevent前,会经过input_filter,同理所有被发送的数据在发送给filtering bufferevent前,会经过output_filter。
filtering bufferevent增加一个filter而不是直接调用回调。当然,你仍然可以给“underlying” bufferevent的evbuffer增加一个回调,但如果你想让filter正常工作,你不能给bufferevent设置回调。
所有可用的选项都被支持,见option参数。如果设置了BEV_OPT_CLOSE_ON_FREE,那么释放filtering bufferevent也会释放"underlying" bufferevent。ctx参数是用来传给filter方法的。如果提供了free_context方法,会在filtering bufferevent关闭的时候处理ctx

input_filter在input buffer有新的可读数据时被调用。output_filter在out buffer有新的可写数据时被调用。它们都会收到一堆evbuffer:一个源evbuffer用来读取数据,一个目标evbuffer用来写入数据。dst_limit参数是destination的可写上限,这个值可以被忽略,但是这样做可能会违反水标位和速率限制。如果dst_limit是-1,表示无限制。mode参数告诉filter如何去写,如果它是BEV_NORMAL,正常的写;如果它是BEV_FLUSH,尽可能的多写;如果他是BEV_FINISHED表示filter方法在流结束时做一些清理工作。最后ctx参数是bufferevent_filter_new中的ctx

如果数据成功的写入到目标buffer,filter方法会返回BEV_OK;如果没再有数据需要被写入目标buffer,filter会返回BEV_NEED_MORE;如果发生了不可恢复的错误,filter会返回BEV_ERROR。

filter会自动的开启底层bufferevent的读写功能。你不需要去主动管理它们:当不想去读的时候,filter会挂起底层bufferevent的读功能。

你不需要一定指定input filter和output filter:如果你没指定它们,任何被传入的数据都不会经过转化处理。


最大单次读写值

默认情况下,bufferevent在每次事件循环中不会一次性读或写最大字节;如果这样做会会导致奇怪的现象和资源的紧张。但是,默认行为不一定对所有情况都适用。

接口

int bufferevent_set_max_single_read(struct bufferevent *bev, size_t size);int bufferevent_set_max_single_write(struct bufferevent *bev, size_t size);ev_ssize_t bufferevent_get_max_single_read(struct bufferevent *bev);ev_ssize_t bufferevent_get_max_single_write(struct bufferevent *bev);
这些方法成功返回0,失败返回-1。

Bufferevent和速率限制

一些程序想去限制单个或某个组的bufferevent的宽带使用率。

速率限制模型

Libevent的速率限制是使用的“令牌桶”算法去决定一次可以有多少数据可以被读或写。每个速率限制对象,在任何时间,都有一个“读桶”和一个“写桶”,它们的大小决定了这个对象可以被允许读或写多少字节。每个桶都有一个填充率,一个最大突发值,一个定时器单元(“tick”)。当过了一个tick时,桶按照填充率被填充,但当它超过最大突发值时,任何超出部分都会被遗弃。

因此,填充率决定将要被发送或接收的最大平均速率,最大突发值决定了一次可发送或接收的最大值,定时器单元决定了传输的顺畅性。

给bufferevent设置速率限制

接口

#define EV_RATE_LIMIT_MAX EV_SSIZE_MAXstruct ev_token_bucket_cfg;struct ev_token_bucket_cfg *ev_token_bucket_cfg_new(        size_t read_rate, size_t read_burst,        size_t write_rate, size_t write_burst,        const struct timeval *tick_len);void ev_token_bucket_cfg_free(struct ev_token_bucket_cfg *cfg);int bufferevent_set_rate_limit(struct bufferevent *bev,    struct ev_token_bucket_cfg *cfg);
结构体ev_token_bucket_cfg包含着一对令牌桶相关的配置值。调用ev_token_bucket_cfg_new方法,同时提供读写的最大平均速率,读写的最大突发值,和一个定时单元的时间,这样就可以创建一个新的ev_token_bucket_cfg。如果tick_len参数为NULL,一个tick的长度默认为1秒。如果发生错误返回NULL。
注意read_ratewrite_rate是一个tick可用的值。也就是说,如果一个tick要0.1秒,read_rate的值是300,那么每秒最大的平均读速率是3000。速率和突发值不能超过EV_RATE_LIMIT_MAX。

调用bufferevent_set_rate_limit()并传入ev_token_bucket_cfg就可以限制bev的传输速率。这个方法成功返回0,失败返回-1。你可以给任意的bufferevent都设置同一个ev_token_bucket_cfg。如果cfg参数为NULL,则表示移除bufferevent的速率限制。

调用ev_token_bucket_cfg释放ev_token_bucket_cfg。注意:如果当前还有bufferevent在使用ev_token_bucket_cfg,此时去释放它会是不安全的行为。


给一组bufferevent设置速率

接口

struct bufferevent_rate_limit_group;struct bufferevent_rate_limit_group *bufferevent_rate_limit_group_new(        struct event_base *base,        const struct ev_token_bucket_cfg *cfg);int bufferevent_rate_limit_group_set_cfg(        struct bufferevent_rate_limit_group *group,        const struct ev_token_bucket_cfg *cfg);void bufferevent_rate_limit_group_free(struct bufferevent_rate_limit_group *);int bufferevent_add_to_rate_limit_group(struct bufferevent *bev,    struct bufferevent_rate_limit_group *g);int bufferevent_remove_from_rate_limit_group(struct bufferevent *bev);
获取当前速率限制值
接口

</pre><pre name="code" class="cpp">ev_ssize_t bufferevent_get_read_limit(struct bufferevent *bev);ev_ssize_t bufferevent_get_write_limit(struct bufferevent *bev);ev_ssize_t bufferevent_rate_limit_group_get_read_limit(        struct bufferevent_rate_limit_group *);ev_ssize_t bufferevent_rate_limit_group_get_write_limit(        struct bufferevent_rate_limit_group *);
上边方法是用来获取单个bufferevent或单组bufferevent的读写桶当前的大小。

接口

void bufferevent_rate_limit_group_get_totals(    struct bufferevent_rate_limit_group *grp,    ev_uint64_t *total_read_out, ev_uint64_t *total_written_out);void bufferevent_rate_limit_group_reset_totals(    struct bufferevent_rate_limit_group *grp);

0 0