Akawa

ETY001的博客

转自:http://blog.codingnow.com/2011/02/zeromq_message_patterns.html

在需要并行化处理数据的时候,采用消息队列通讯的方式来协作,比采用共享状态的方式要好的多。Erlang ,Go 都使用这一手段来让并行任务之间协同工作。

最近读完了 ZeroMQ 的 Guide。写的很不错。前几年一直有做类似的工作,但是自己总结的不好。而 ZeroMQ 把消息通讯方面的模式总结的很不错。

ZeroMQ 并不是一个对 socket 的封装,不能用它去实现已有的网络协议。它有自己的模式,不同于更底层的点对点通讯模式。它有比 tcp 协议更高一级的协议。(当然 ZeroMQ 不一定基于 TCP 协议,它也可以用于进程间和进程内通讯。)它改变了通讯都基于一对一的连接这个假设。

ZeroMQ 把通讯的需求看成四类。其中一类是一对一结对通讯,用来支持传统的 TCP socket 模型,但并不推荐使用。常用的通讯模式只有三类。

  1. 请求回应模型。由请求端发起请求,并等待回应端回应请求。从请求端来看,一定是一对对收发配对的;反之,在回应端一定是发收对。请求端和回应端都可以是 1:N 的模型。通常把 1 认为是 server ,N 认为是 Client 。ZeroMQ 可以很好的支持路由功能(实现路由功能的组件叫作 Device),把 1:N 扩展为 N:M (只需要加入若干路由节点)。从这个模型看,更底层的端点地址是对上层隐藏的。每个请求都隐含有回应地址,而应用则不关心它。

  2. 发布订阅模型。这个模型里,发布端是单向只发送数据的,且不关心是否把全部的信息都发送给订阅端。如果发布端开始发布信息的时候,订阅端尚未连接上来,这些信息直接丢弃。不过一旦订阅端连接上来,中间会保证没有信息丢失。同样,订阅端则只负责接收,而不能反馈。如果发布端和订阅端需要交互(比如要确认订阅者是否已经连接上),则使用额外的 socket 采用请求回应模型满足这个需求。

  3. 管道模型。这个模型里,管道是单向的,从 PUSH 端单向的向 PULL 端单向的推送数据流。

任何分布式,并行的需求,都可以用这三种模型组合起来解决问题。ZeroMQ 只专注和解决了消息通讯这一基本问题,干的非常漂亮。

基于定义好的模型,我们可以看到,api 可以实现的非常简单易用。我们不再需要 bind/listen/accept 来架设服务器,因为这个模型天然是 1:N 而不是 1:1 的,不需要为每个通道保留一个句柄。我们也不必在意 server 是否先启动(bind),而后才能让 client 工作起来(connect)。

这以上模型中,关注的是通讯双方的职责,而不是实现的方式:监听端口还是连接对方端口。对于复杂的多进程协同工作的系统, 不必纠结于进程启动的次序(在我前几年设计的系统中,启动脚本写起来就非常麻烦)。

使用 ZeroMQ 不必在意底层实现是使用短连接还是长连接方式。ZeroMQ 中的 Transient (短暂) 和 Durable (持久) socket 也并非区别于实现层是否保持了 tcp 连接。而是概念上的不同。对于 Durable socket ,其生命期可以长于一个进程的生命期,即使进程退出,再次启动后依旧可以维持继续之前的 socket 。当然,这并不是帮助你挽救你的程序因出错而崩溃的。它只是提出这个模式,让你根据设计需要可以实现。对于 ZeroMQ ,如有需求(若内存有限),甚至把数据传输的 buffer 放到磁盘上。

对于网络游戏,我觉得基于 ZeroMQ 来架构服务器非常合适。对于玩家 Client - Server 部分倒不必使用 ZeroMQ ,而可以写一个前端程序,比如前些年写过的一篇 blog 中提到的连接服务器依然适用。这个连接服务器对内的服务集群则可以用 ZeroMQ 的协议通讯。

转自:http://www.cnblogs.com/OnlyXP/archive/2007/08/10/851222.html

在linux的网络编程中,很长的时间都在使用select来做事件触发。在linux新的内核中,有了一种替换它的机制,就是epoll。
相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。并且,在linux/posix_types.h头文件有这样的声明:

1
#define __FD_SETSIZE    1024

表示select最多同时监听1024个fd,当然,可以通过修改头文件再重编译内核来扩大这个数目,但这似乎并不治本。

epoll的接口非常简单,一共就三个函数:

  1. int epoll_create(int size);

创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

  1. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:

EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;

第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:

1
2
3
4
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};

events可以是以下几个宏的集合:

1
2
3
4
5
6
7
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
  1. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

等待事件的产生,类似于select()调用。参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。


从man手册中,得到ET和LT的具体描述如下

EPOLL事件有两种模型:

Edge Triggered (ET)
Level Triggered (LT)

假如有这样一个例子:

1. 我们已经把一个用来从管道中读取数据的文件句柄(RFD)添加到epoll描述符
2. 这个时候从管道的另一端被写入了2KB的数据
3. 调用epoll_wait(2),并且它会返回RFD,说明它已经准备好读取操作
4. 然后我们读取了1KB的数据
5. 调用epoll_wait(2)......

Edge Triggered 工作模式:

如果我们在第1步将RFD添加到epoll描述符的时候使用了EPOLLET标志,那么在第5步调用epoll_wait(2)之后将有可能会挂起,因为剩余的数据还存在于文件的输入缓冲区内,而且数据发出端还在等待一个针对已经发出数据的反馈信息。只有在监视的文件句柄上发生了某个事件的时候 ET 工作模式才会汇报事件。因此在第5步的时候,调用者可能会放弃等待仍在存在于文件输入缓冲区内的剩余数据。在上面的例子中,会有一个事件产生在RFD句柄上,因为在第2步执行了一个写操作,然后,事件将会在第3步被销毁。因为第4步的读取操作没有读空文件输入缓冲区内的数据,因此我们在第5步调用 epoll_wait(2)完成后,是否挂起是不确定的。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。最好以下面的方式调用ET模式的epoll接口,在后面会介绍避免可能的缺陷。
i 基于非阻塞文件句柄
ii 只有当read(2)或者write(2)返回EAGAIN时才需要挂起,等待。但这并不是说每次read()时都需要循环读,直到读到产生一个EAGAIN才认为此次事件处理完成,当read()返回的读到的数据长度小于请求的数据长度时,就可以确定此时缓冲中已没有数据了,也就可以认为此事读事件已处理完成。

Level Triggered 工作模式

相反的,以LT方式调用epoll接口的时候,它就相当于一个速度比较快的poll(2),并且无论后面的数据是否被使用,因此他们具有同样的职能。因为即使使用ET模式的epoll,在收到多个chunk的数据的时候仍然会产生多个事件。调用者可以设定EPOLLONESHOT标志,在 epoll_wait(2)收到事件后epoll会与事件关联的文件句柄从epoll描述符中禁止掉。因此当EPOLLONESHOT设定后,使用带有 EPOLL_CTL_MOD标志的epoll_ctl(2)处理文件句柄就成为调用者必须作的事情。

然后详细解释ET, LT:

LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.

ET(edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认(这句话不理解)。

在许多测试中我们会看到如果没有大量的idle -connection或者dead-connection,epoll的效率并不会比select/poll高很多,但是当我们遇到大量的idle- connection(例如WAN环境中存在大量的慢速连接),就会发现epoll的效率大大高于select/poll。(未测试)

另外,当使用epoll的ET模型来工作时,当产生了一个EPOLLIN事件后,
读数据的时候需要考虑的是当recv()返回的大小如果等于请求的大小,那么很有可能是缓冲区还有数据未读完,也意味着该次事件还没有处理完,所以还需要再次读取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
while(rs)
{
buflen = recv(activeevents[i].data.fd, buf, sizeof(buf), 0);
if(buflen < 0)
{
// 由于是非阻塞的模式,所以当errno为EAGAIN时,表示当前缓冲区已无数据可读
// 在这里就当作是该次事件已处理处.
if(errno == EAGAIN)
break;
else
return;
}
else if(buflen == 0)
{
// 这里表示对端的socket已正常关闭.
}
if(buflen == sizeof(buf)
rs = 1; // 需要再次读取
else
rs = 0;
}

还有,假如发送端流量大于接收端的流量(意思是epoll所在的程序读比转发的socket要快),由于是非阻塞的socket,那么send()函数虽然返回,但实际缓冲区的数据并未真正发给接收端,这样不断的读和发,当缓冲区满后会产生EAGAIN错误(参考man send),同时,不理会这次请求发送的数据.所以,需要封装socket_send()的函数用来处理这种情况,该函数会尽量将数据写完再返回,返回-1表示出错。在socket_send()内部,当写缓冲已满(send()返回-1,且errno为EAGAIN),那么会等待后再重试.这种方式并不很完美,在理论上可能会长时间的阻塞在socket_send()内部,但暂没有更好的办法.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
ssize_t socket_send(int sockfd, const char* buffer, size_t buflen)
{
ssize_t tmp;
size_t total = buflen;
const char *p = buffer;

while(1)
{
tmp = send(sockfd, p, total, 0);
if(tmp < 0)
{
// 当send收到信号时,可以继续写,但这里返回-1.
if(errno == EINTR)
return -1;

// 当socket是非阻塞时,如返回此错误,表示写缓冲队列已满,
// 在这里做延时后再重试.
if(errno == EAGAIN)
{
usleep(1000);
continue;
}

return -1;
}

if((size_t)tmp == total)
return buflen;

total -= tmp;
p += tmp;
}

return tmp;
}

  CSS网页布局,说难,其实很简单。说它容易,往往有很多问题困扰着新手,介绍了非常多的技巧,这些小技巧与知识能事半功倍的给大家以帮助。然而更多的时候,我们往往被一些小问题缠着不能轻松。今天向大家介绍八个技巧,这些技巧显得很有用。

  一、若有疑问立即检测

  在出错时若能对原始代码做简单检测可以省去很多头痛问题。W3C对于XHTML与CSS 都有检测工具可用,请见 http://validator.w3.org 。请注意,在文件开头的错误,可能因为不当的结构等因素造成更多错误;我们建议先修正一些最明显的错误之后重新检测,这样也许会让错误数量爆减。

  二、使用浮动功能时记得适当清除指令

  浮动是个危险的功能,未必会产生您所期望的结果。如果您遇到浮动元素延伸到外围容器的边框或者其他不正常情况,请先确定您的做法是正确的。

  三、边界重合时利用padding或border来避免

  您可能会为了一点不应该出现的空间而焦头烂额,或者您需要一点点空间时,怎样都挤不出来。如果您有用到margin,那么很容易产生边界的重合。

  四、尝试避免同时对元素指定padding/border以及高度或宽度

  Windows版IE经常导致width与height的计算问题。有些方法可以解决此问题,但如果母元素需要指定高度与宽度时,最好能够在母元素之内的子元素套用margin,或者当子元素需要指定高度与宽度时,在母元素套用padding以达效果。

  五、不要依赖min-width/min-height

  Windows版IE并不支援两种语法。但是在某种程度下,windows版IE可以达到相当于min-width/min-height的效果,所以只要对IE做点过滤功能,即可达到您想要的结果。

  六、若有疑问,先减少百分比

  有时候某些错误会使50%+50%成为100.1%,使网页出现问题。这时请尝试将这些值改为49%,甚至49.9%。

  七、记住“TRBL”写法

  border,margin与padding的简写语法有特定顺序,从上方开始顺时针方向转动:top,right,bottom,left. 所以margin:0 1px 3px 5px;的结果是上方无边界,右边1像素,以此类推。记住“TRBL”,您就不会弄错次序了。

  八、只要不是零的值,都要指定单位

  这需要特别注意,很多新手朋友往往忽视这个问题。不止一只强调这个问题了。

如果在用CSS设计布局时遇到BUG,请认真阅读以下内容,非常容易记忆的,不知道哪位高人把CSS BUG编成了顺口溜了!看看好不好记住呢?

一、IE边框若显若无,须注意,定是高度设置已忘记;

二、浮动产生有缘故,若要父层包含住,紧跟浮动要清除,容器自然显其中;

三、三像素文本慢移不必慌,高度设置帮你忙;

四、兼容各个浏览须注意,默认设置行高可能是杀手;

五、独立清除浮动须铭记,行高设无,高设零,设计效果兼浏览;

六、学布局须思路,路随布局原理自然直,轻松驾驭html,流水布局少hack,代码清爽,兼容好,友好引擎喜欢迎。

七、所有标签皆有源,只是默认各不同,span是无极,无极生两仪—内联和块级,img较特殊,但也遵法理,其他只是改造各不同,一个*号全归原,层叠样式理须多练习,万物皆规律。

八、图片链接排版须小心,图片链接文字链接若对齐,padding和vertical-align:middle要设定,虽差微细倒无妨。

九、IE浮动双边距,请用display:inline拘。

十、列表横向排版,列表代码须紧靠,空隙自消须铭记。

标题的意思是:用一套WordPress源程序来驱动多个域名的WordPress博客。( Multiple WordPress Blogs with a Single Installation)

工具:wp-hive插件

好处:1.不用每次WordPress升级时都多个博客分开升级,费时。

2.节省空间,一套wp解压后也要4兆多呢,再加上插件,主题等。

3.充分利用一个数据库。

4.没想好。用这个插件的朋友给我们说说吧。

阅读全文 »

由于工作需要在godaddy上买了最便宜的那款centos系统的VDS,没想到,把预装的apache,mysql,php,sendmail干掉后,居然内存占用仅9M多,真神奇。

下面就说下怎么手动卸载,首先要rpm -qa|grep mysql,这条命令可以找出安装的mysql的包,然后根据返回的信息,一个包一个包的用rpm -e 这条命令卸载即可。

exec函数族的函数说明如下:

1
2
3
4
5
6
7
#include <unistd.h>
int execl(const char *pathname,const char *arg, ...);
int execlp(const char *filename,const char *arg, ...);
int execle(const char *pathname,const char *arg, ... , char *const envp[]);
int execv(const char *pathname,char *const argv[]);
int execvp(const char *filename,char *const argv[]);
int execve(const char *pathname,char *const argv[],char *const envp[]);

函数名中含有字母“ l ”的函数,其参数个数不定。其参数由所谓调用程序的命令行参数列表组成,最后一个NULL表示结束。函数名中含有字母“ v ”的函数,则是使用一个字符串数组指针argv指向参数列表,这一字符串数组和含有“ l ”的函数中的参数列表完全相同,也同样以NULL结束。

函数名中含有字母“ p ”的函数可以自动在环境变量PATH指定的路径中搜索要执行的程序。因此它的第一个参数为filename表示可执行函数的文件名。而其他函数则需要用户在参数列表中指定该程序路径,其第一个参数pathname是路径名。路径的指定可以是绝对路径,也可以是相对路径。但出于对系统安全的考虑,建议使用绝对路径而尽量避免使用相对路径。

函数名中含有字母“ e ”的函数,比其他函数多含有一个参数envp。该参数是字符串数组指针,用于制定环境变量。调用这两个函数时,可以由用户自行设定子进程的环境变量,存放在参数envp所指向的字符串数组中。这个字符串数组也必须由NULL结束。其他函数则是接收当前环境变量。

今天需要在服务器上配置两个ftp账户a和b,两个账户需要在同一个组,并且a允许ssh登陆,而b不允许ssh登陆。两个用户登陆ftp的时候都必须要限制在自己的家目录内。

具体配置不详说,只说一下几个关键点:

1、b用户因为不具备ssh登陆权限,所以在proftpd的配置文件里要加下面这行配置
RequireValidShell off
这个配置的作用就是允许没有ssh登陆权限的用户登陆ftp

2、限制某个用户组只能访问自己家目录的配置:
DefaultRoot ~ groupname
其中 ~指的是家目录,groupname指的是你要限制的用户组。

这是一个基于记事狗微博的插件,
目的在于实现121部落积分商城的功能。
项目地址:https://github.com/ety001/hi121_shop

别的不多说,这是个开源的项目,自己随意编写代码取用吧,但是还是希望大家能共享一下。

如有疑问,请在这个页面下面留言。

这是一个基于记事狗微博的插件,
目的在于实现邀请长时间未登录微博的用户进行回访。

项目地址:https://github.com/ety001/hi121_backhome

注意:请谨慎使用该插件,该插件的使用过程具有危险性。

  • 使用方法:

    1. 把plugin目录下的hi121_backhome复制到你的网站plugin目录下,把templates/default/plugin目录下的hi121_backhome目录复制到你网站的templates/default/plugin目录下;
    2. 进入网站后台插件面板进行安装;
    3. 安装后,第一次使用之前,请先设置插件参数(必须!);
    4. 插件参数配置说明详见设置面板的各参数具体说明,其中发送方式一项当前版本可以不用选,默认使用PHP方式;
    5. 使用测试模式进行测试;
    6. 测试成功后,即可开始正常使用。
  • 参数设置样例:
    发送内容模板地址:/tpl/1/mail.html (默认模板)
    件每批发送人数:25 (测试时可以把该值调大,这样可以减少发送批次)
    间隔时间(天):15
    邮件标题:亲121部落欢迎您再次回来
    发送批次间隔时间:20 (如果你用的是免费邮箱,请尽量调大这里的时间,因为过密集的发送邮件,会导致你使用的免费邮件服务提供商屏蔽你)
    执行方式:php方式执行
    测试模式:开启 (平时不用该插件时,请选择开启项,或者直接关闭这个插件,防止误操作)
    日志名称:test_log (由于该文件记录了用户邮箱地址,防止被他人恶意采集,请使用者输入不易被猜到的名字)

  • 测试方法:

    1. 请在参数设置里,把每批发送人数调大,并把发送批次间隔时间调到0.1,这样可以节省测试时间。
    2. 测试模式一栏选择“开启”,这样插件只会进行一个发送的流程,但不会真正的发信。
    3. 在控制面板里点击“开始”前,请先确认发送总量和发送批次数以及总时间,根据发送总量和你的配置参数自己再计算下总时间看看是否一致,然后点击“开始”即可。
    4. 结束后,请检查核对日志中的发送批次数和开始前程序显示的发送批次数是否一致。如果一致,则说明测试通过,如果不一致则说明插件运转不正常。
    5. 如果不正常,基本上你现在的空间就无法使用该插件了。(另外注意:nginx用户,请查看你的nginx配置文件中的worker_processes 值是否大于1,如果等于1的话,当你执行插件的时候,整个网站将不可访问,因为php进程只有一条,还被插件给sleep掉了)
  • 关于模板:
    请自行制作模板,模板只支持且必须要有用户名和天数的预留位置,并且要先出现用户名,后出现天数。在模板中用%s代替用户名,%d代替天数。请参见自带的一个模板。

如有疑问,请在这个页面留言。

0%