作者归档: 童燕群

Java网络应用程序(Geronimo、Jetty)调试及问题定位方法简介

Java网络应用程序调试及问题定位方法简介

| 1 分2 分3 分4 分5 分 (5.00- 3票) Loading ... Loading ... | 归档目录:Geronimo, Java, Jetty, 实用脚本 | 标签: , , |

[转] 高并发系统设计

一、服务器内部设计

服务器设计涉及Socket的阻塞/非阻塞,操作系统IO的同步和异步(之前被人问到过两次。第一次让我说说知道的网络模型,我说ISO模型和TCP/IP模型,结果被鄙视了。最后人说了解linux epoll吗?不了解呀!汉,回去查资料才知道是这回事。第二次让我说说知道线程模型,汉!这个名词感觉没有听说过,线程?模型?半同步/半异步,领导者/跟随者知道吗。再汉,我知道同步/异步,还有半同步/半异步?啥呀?领导者/跟随者,我现在没有领导。回去一顿恶补,原来是ACE框架里边经常有这样的提法,Reactor属于同步/半同步,PREACTOR属于领导者/跟随者模式。瀑布汗。小插曲一段,这些不懂没关系,下边我慢慢分解),事件分离器,线程池等。内部设计希望通过各个模块的给出一个简单设计,经过您的进一步的组合和打磨,就可以实现一个基本的高并发服务器。

1. Java高并发服务器

Java设计高并发服务器相对比较简单。直接是用ServerSocket或者Channel+selector实现。前者属于同步IO设计,后者采用了模拟的异步IO。为什么说模拟的异步IO呢?记得网上看到一篇文章分析了java的selector。在windows上通过建立一个127.0.0.1到127.0.0.1的连接实现IO的异步通知。在linux上通过建立一个管道实现IO的异步通知。考虑到高并并发系统的要求和java上边的异步IO的限制(通常操作系统同时打开的文件数是有限制的)和效率问题,java的高并发服务器设计不做展开深入的分析,可以参考C高并发服务器的分析做同样的设计。

2. C高并发服务器设计
1) 基本概念

Ø 阻塞和非阻塞socket

所谓阻塞Socket,是指其完成指定的任务之前不允许程序调用另一个函数,在Windows下还会阻塞本线程消息的发送。所谓非阻塞Socket,是指操作启动之后,如果可以立即得到结果就返回结果,否则返回表示结果需要等待的错误信息,不等待任务完成函数就返回。一个比较有意思的问题是accept的Socket是阻塞的还是非阻塞的呢?下边是MSDN上边的一段话:The accept function extracts thefirst connection on the queue of pending connections on socket s. It thencreates and returns a handle to the new socket. The newly created socket is thesocket that will handle the actual connection; it has the same properties assocket s, including the asynchronous events registered with the WSAAsyncSelector WSAEventSelect functions.

Ø 同步/异步IO

有两种类型的文件IO同步:同步文件IO和异步文件IO。异步文件IO也就是重叠IO。 
      在同步文件IO中,线程启动一个IO操作然后就立即进入等待状态,直到IO操作完成后才醒来继续执行。而异步文件IO方式中,线程发送一个IO请求到内核,然后继续处理其他的事情,内核完成IO请求后,将会通知线程IO操作完成了。 
      如果IO请求需要大量时间执行的话,异步文件IO方式可以显著提高效率,因为在线程等待的这段时间内,CPU将会调度其他线程进行执行,如果没有其他线程需要执行的话,这段时间将会浪费掉(可能会调度操作系统的零页线程)。如果IO请求操作很快,用异步IO方式反而还低效,还不如用同步IO方式。 
      同步IO在同一时刻只允许一个IO操作,也就是说对于同一个文件句柄的IO操作是序列化的,即使使用两个线程也不能同时对同一个文件句柄同时发出读写操作。重叠IO允许一个或多个线程同时发出IO请求。异步IO在请求完成时,通过将文件句柄设为有信号状态来通知应用程序,或者应用程序通过GetOverlappedResult察看IO请求是否完成,也可以通过一个事件对象来通知应用程序。高并发系统通常采用异步IO方式提高系统性能。

Ø 事件分离器

事件分离器的概念是针对异步IO来说的。在同步IO的情况下,执行操作等待返回结果,不要事件分离器。异步IO的时候,发送请求后,结果是通过事件通知的。这是产生了事件分离器的需求。事件分离器主要任务是管理和分离不同文件描述符上的所发生的事件,让后通知相应的事件,派发相应的动作。

Ø 线程池

线程池基本上比较简单,实现线程的借入和借出,创建和销毁。最完好可以做到通过一个事件触发一个线程开始工作(注:在epoll中,事件触发又分为边沿触发和水平触发)。

2) 常见的设计模式

根据Socket的阻塞非阻塞,IO的同步和异步。可以分为如下4中情形

阻塞同步 阻塞异步
非阻塞同步 非阻塞异步

阻塞同步方式是原始的方式,也是许多教科书上介绍的方式,因为Socket和IO默认的为阻塞和同步方式。基本流程如下:

listen_fd = socket( AF_INET,SOCK_STREAM,0 )

bind( listen_fd, (struct sockaddr*)&my_addr, sizeof(struct sockaddr_in))

listen( listen_fd,1 )

accept( listen_fd, (struct sockaddr*)&remote_addr,&addr_len )

recv( accept_fd ,&in_buf ,1024 ,0 )

close(accept_fd)

阻塞异步方式有所改进,但是Socket的阻塞方式,前一个连接没有处理完成,下一个连接不能接入,是高并发服务器所不可接收的方式。只不过在上边阻塞同步方式的基础上使用select(严格来说select是一种IO多路服用技术。因为linux尚没有完整的实现异步IO,而winsock实在理解socket没有linux上面那么直观。,这里为了方便,没有做严格的区分)或者其它异步IO方式。

非阻塞同步方式,通过设置socket选项为NONBLOCK,可以很快的接收连接,但是处理采用同步IO方式,服务器处理性能也比较差。

上边三种方式不做深入介绍。下边主要从非阻塞异步IO方式介绍。

非阻塞异步IO方式中,由于异步IO方式在同一系统可能有多种实现,不同系统也有不同实现,下边介绍几种常见的IO方式和服务器框架。

Ø Select

Select采用轮训注册的fd方式。是一种比较老的IO多路服用实现方式,效率相对要差一些。Select方式在windows和linux上都支持。

基本框架如下:


socket( AF_INET,SOCK_STREAM,0 )
fcntl(listen_fd, F_SETFL,flags|O_NONBLOCK);
bind( listen_fd, (structsockaddr *)&my_addr,sizeof(struct sockaddr_in))
listen( listen_fd,1 )
FD_ZERO( &fd_sets );
FD_SET(listen_fd,&fd_sets);
for(k=0; k<=i; k++){

FD_SET(accept_fds[k],&fd_sets);
}
events = select( max_fd + 1,&fd_sets, NULL, NULL, NULL );
if(FD_ISSET(listen_fd,&fd_sets) ){
accept_fd = accept( listen_fd, (structsockaddr
*)&remote_addr,&addr_len );
}
for( j=0; j<=i; j++ ){
if(
FD_ISSET(accept_fds[j],&fd_sets) ){

recv( accept_fds[j] ,&in_buf ,1024 ,0 );
}
}

Ø Epoll

Epoll是linux2.6内核以后支持的一种高性能的IO多路服用技术。服务器框架如下:

socket( AF_INET,SOCK_STREAM,0 )
fcntl(listen_fd, F_SETFL,flags|O_NONBLOCK);
bind( listen_fd, (structsockaddr *)&my_addr,sizeof(struct sockaddr_in))
listen( listen_fd,1 )
epoll_ctl(epfd,EPOLL_CTL_ADD,listen_fd,&ev);
ev_s = epoll_wait(epfd,events,20,500 );
for(i=0; i<ev_s;i++){

if(events[i].data.fd==listen_fd){

accept_fd = accept( listen_fd,(structsockaddr *)&remote_addr,&addr_len
);

fcntl(accept_fd, F_SETFL,flags|O_NONBLOCK);

epoll_ctl(epfd,EPOLL_CTL_ADD,accept_fd,&ev);

}

else if(events[i].events&EPOLLIN){

recv( events[i].data.fd ,&in_buf,1024 ,0 );

}
}

Ø AIO

在windows上微软实现了异步IO,通过AIO可以方便的实现高并发的服务器。框架如下:

WSAStartup( 0x0202 , & wsaData)
CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL, 0 , 0 )
WSASocket(AF_INET,SOCK_STREAM, 0 , NULL, 0 , WSA_FLAG_OVERLAPPED)
bind(Listen, (PSOCKADDR) & InternetAddr, sizeof
(InternetAddr))
listen(Listen, 5 )
WSAAccept(Listen, NULL, NULL,NULL, 0 )
PerHandleData =(LPPER_HANDLE_DATA) GlobalAlloc(GPTR, sizeof
(PER_HANDLE_DATA)
CreateIoCompletionPort((HANDLE)Accept, CompletionPort, (DWORD) PerHandleData,
0 )
PerIoData= (LPPER_IO_OPERATION_DATA)GlobalAlloc(GPTR, sizeof
(PER_IO_OPERATION_DATA))
WSARecv(Accept,&(PerIoData->DataBuf),1,&RecvBytes,&Flags,&(PerIoData->Overlapped),
NULL)
(GetQueuedCompletionStatus(CompletionPort, & BytesTransferred,
(LPDWORD) &
PerHandleData,(LPOVERLAPPED * ) & PerIoData, INFINITE)
if (PerIoData -> BytesRECV >PerIoData ->
BytesSEND){
WSASend(PerHandleData-> Socket, & (PerIoData
->DataBuf), 1 , & SendBytes, 0 ,

& (PerIoData ->Overlapped), NULL)
}

3) 引入线程池和事件分离器后

由于上边只是单纯的使用非阻塞Socket和异步IO的方式。提高了接收连接和处理的速度。但是还是不能解决两个客户端同时连接的问题。这时就需要引入多线程机制。引入多线程后,又有许多策略。Linux上通常采用主进程负责接收连接,之后fork子进程处理连接。Windows通常采用线程池方式,避免线程创建和销毁的开销,当然linux上也可以采用线程池方式。采用多进程和多线程方式后。事件处理也可以再优化,定义一个简单的事件处理器,把所有事件放入一个队列,各个线程去事件队列取相应的事件,然后自己开始工作。这就是我上边提到的半同步/半异步方式了。如果线程工作的时候是接收到连接后,自己处理后续的发送和接收,然后选出另外一个线程作为领导继续接收连接,其它线程作为追随者。这就是领导者/追随者模式了。具体可以参考ACE的Reactor和Preactor的具体实现。半同步和/半异步网上也有很多的讨论,可以自己深入研究。代码就比较复杂了,这里就不给出代码了。

二、分布式系统设计

前面讲述了分布式系统中的核心的服务器的实现。可以是http服务器,缓存服务器,分布式文件系统等的内部实现。下边主要从一个高并发的大型网站出发,看一个高并发系统的设计。下边是一个高并发系统的逻辑结构:

分布式系统设计

主要是参考这篇文章http://www.chinaz.com/web/2010/0310/108211.shtml。下边主要想从这个架构的各个部分的实现展开。

1. 缓存系统

缓存是每一个高并发,高可用系统不可或缺的模块。下边就几个常见缓存系统系统进行介绍。

Squid

Squid作为一个前端缓存,通常部署在网络的离用户最近的地方,通过缓存网站的页面,使用户不必每次都跑到服务器去取数据,提高系统响应和性能。实现应该比较简单:一个带有存储功能的代理。用户访问页面的时候,由它代理,然后存储请求结果,下次再访问的时候,查看是否需要更新,有更新就去服务器取新数据,否则直接返回用户页面。

Ehcache

Ehcache是一个对象缓存系统。通常在J2EE中配合Hibernate使用,这里请原谅作者本人之前是做J2EE开发的,其它使用方式暂不是很了解。应用查询数据库,对经常需要查询,却更新不频繁的数据,可以放入ehcache缓存,提高访问速度。Ehcahe支持内存缓存和硬盘两种方式,支持分布式缓存。数据缓存的基本原理就是:为需要缓存的对象建立一个map,临时对象放入map,查询的时候先查询map,没有找到再查找数据库。关机时可以序列化到硬盘。分布式缓存没有研究过。

页面缓存和动态页面静态化

在大型网站经常使用的一种缓存技术就是动态页面的缓存。由于动态页面经常更新,上边的缓存就不起作用了。通常会采用SSI(Server side include)等技术将动态页面的或者页面片段进行缓存。

还有一种就是动态页面静态化。

2. 负载均衡系统

Ø 负载均衡策略

负载均衡策略有随机分配,平均分配,分布式一致性hash等。随机分配就是通过随机数选择一个服务器来服务。平均分配就是一次循环分配一次。分布式一致性hash算法,比较负载,把资源和节点映射到一个换上,然后通过一定的算法资源对应到节点上,使得添加和去掉服务器变得非常容易,减少对其它服务器的影响。很有名的一个算法,据说是P2P的基础。了解不是很深,就不详细说了,要露马脚了。

Ø 软件负载均衡

软件负载均衡可以采用很多方案,常见的几个方案有:

基于DNS的负载均衡,通过DNS正向区域的配置,将一个域名根据一定的策略解析到多个ip地址,实现负载均衡,这里需要DNS服务器的配合。
基于LVS的负载均衡。LVS可以将多个linux服务器做成一个虚拟的服务器,对外提供服务器,实现负载均衡。
基于Iptables的负载均衡。Iptables可以通过做nat,对外提供一个虚拟IP,对内映射到多个服务器实现负载均衡。基本上可以和硬件均衡方案一致了,这里的linux服务器相当于一台路由器。

Ø 硬件负载均衡

基于路由器的负载均衡,在路由器上配置nat实现负载均衡。对外网一个虚拟IP,内网映射几个内网IP。一些网络设备厂商也提供了一些负载均衡的设备,如F5,不过价格不菲哦。数据库的负载均衡数据库的负载均衡可以是数据库厂商提供的集群方案。

云计算

转载信息:

作者:周顺利
原文链接:http://blog.csdn.net/shatty/article/details/6629896
注:本文大多数观点和代码都是从网上或者开源代码中抄来的,为了疏理和组织这片文章,作者也费了不少心血,为了表示对我劳动的尊重,请转载时注明作者和出处。

| 1 分2 分3 分4 分5 分 (4.50- 6票) Loading ... Loading ... | 归档目录:C/C++, IO编程, 多线程编程 | 标签: , , , , , |

DRBD源码分析(二)——内核模块网络配置和启动

在上一篇里面分析到了基于netlink的connector,connector正是内核态与用户态配置命令交互的通道。用户通过调用用户态的工具,发送相应的命令参数,用户态工具将命令参数转换成相应的消息包,内核态解析消息后得到相应的指令,继续转换成函数调用,最后得以执行。

首先仔细看一下上一节提到的创建connector时注册的收数据的回调函数:

#ifdef KERNEL_HAS_CN_SKB_PARMS
STATIC void drbd_connector_callback(struct cn_msg *req, struct netlink_skb_parms *nsp)
{
#else
STATIC void drbd_connector_callback(void *data)
{
struct cn_msg *req = data;
#endif
struct drbd_nl_cfg_req *nlp = (struct drbd_nl_cfg_req *)req->data;
struct cn_handler_struct *cm;
struct cn_msg *cn_reply;
struct drbd_nl_cfg_reply *reply;
struct drbd_conf *mdev;
int retcode, rr;
int reply_size = sizeof(struct cn_msg)
+ sizeof(struct drbd_nl_cfg_reply)
+ sizeof(short int);

if (!try_module_get(THIS_MODULE)) {
printk(KERN_ERR "drbd: try_module_get() failed!\n");
return;
}

#ifdef KERNEL_HAS_CN_SKB_PARMS
if (!cap_raised(nsp->eff_cap, CAP_SYS_ADMIN)) {
retcode = ERR_PERM;
goto fail;
}
#endif

mdev = ensure_mdev(nlp);
if (!mdev) {
retcode = ERR_MINOR_INVALID;
goto fail;
}

trace_drbd_netlink(req, 1);

if (nlp->packet_type >= P_nl_after_last_packet) {
retcode = ERR_PACKET_NR;
goto fail;
}
printk("packet_type is %d\n", nlp->packet_type);
cm = cnd_table + nlp->packet_type;

/* This may happen if packet number is 0: */
if (cm->function == NULL) {
retcode = ERR_PACKET_NR;
goto fail;
}

reply_size += cm->reply_body_size;

/* allocation not in the IO path, cqueue thread context */
cn_reply = kmalloc(reply_size, GFP_KERNEL);
if (!cn_reply) {
retcode = ERR_NOMEM;
goto fail;
}
reply = (struct drbd_nl_cfg_reply *) cn_reply->data;

reply->packet_type =
cm->reply_body_size ? nlp->packet_type : P_nl_after_last_packet;
reply->minor = nlp->drbd_minor;
reply->ret_code = NO_ERROR; /* Might by modified by cm->function. */
/* reply->tag_list; might be modified by cm->function. */

rr = cm->function(mdev, nlp, reply);

cn_reply->id = req->id;
cn_reply->seq = req->seq;
cn_reply->ack = req->ack + 1;
cn_reply->len = sizeof(struct drbd_nl_cfg_reply) + rr;
cn_reply->flags = 0;

trace_drbd_netlink(cn_reply, 0);
rr = cn_netlink_send(cn_reply, CN_IDX_DRBD, GFP_KERNEL);
if (rr && rr != -ESRCH)
printk(KERN_INFO "drbd: cn_netlink_send()=%d\n", rr);

kfree(cn_reply);
module_put(THIS_MODULE);
return;
fail:
drbd_nl_send_reply(req, retcode);
module_put(THIS_MODULE);
}

值得注意的是:

rr=cm->function(mdev,nlp,reply);

这一句,这里相当于是一个多态,function绑定到哪一个方法由消息包中携带的包类型决定:


cm=cnd_table+nlp->packet_type;

系统在初始化时级生成了一个全局的静态函数表,类似P_primary的标识符是在编译时动态生成的宏。表示其所在的元素的下标,同时也月包类型相对应。

static struct cn_handler_struct cnd_table[] = {
[ P_primary ] = { &drbd_nl_primary, 0 },
[ P_secondary ] = { &drbd_nl_secondary, 0 },
[ P_disk_conf ] = { &drbd_nl_disk_conf, 0 },
[ P_detach ] = { &drbd_nl_detach, 0 },
[ P_net_conf ] = { &drbd_nl_net_conf, 0 },
[ P_disconnect ] = { &drbd_nl_disconnect, 0 },
[ P_resize ] = { &drbd_nl_resize, 0 },
[ P_syncer_conf ] = { &drbd_nl_syncer_conf, 0 },
[ P_invalidate ] = { &drbd_nl_invalidate, 0 },
[ P_invalidate_peer ] = { &drbd_nl_invalidate_peer, 0 },
[ P_pause_sync ] = { &drbd_nl_pause_sync, 0 },
[ P_resume_sync ] = { &drbd_nl_resume_sync, 0 },
[ P_suspend_io ] = { &drbd_nl_suspend_io, 0 },
[ P_resume_io ] = { &drbd_nl_resume_io, 0 },
[ P_outdate ] = { &drbd_nl_outdate, 0 },
[ P_get_config ] = { &drbd_nl_get_config,
sizeof(struct syncer_conf_tag_len_struct) +
sizeof(struct disk_conf_tag_len_struct) +
sizeof(struct net_conf_tag_len_struct) },
[ P_get_state ] = { &drbd_nl_get_state,
sizeof(struct get_state_tag_len_struct) +
sizeof(struct sync_progress_tag_len_struct) },
[ P_get_uuids ] = { &drbd_nl_get_uuids,
sizeof(struct get_uuids_tag_len_struct) },
[ P_get_timeout_flag ] = { &drbd_nl_get_timeout_flag,
sizeof(struct get_timeout_flag_tag_len_struct)},
[ P_start_ov ] = { &drbd_nl_start_ov, 0 },
[ P_new_c_uuid ] = { &drbd_nl_new_c_uuid, 0 },
};

比如,在一次完整的用户态与内核态的交互中,用户态会多次发出P_get_state消息,该消息的包类型码为17。

类似cn_handler_struct这样的函数表,在drbd的代码中随处可见,无论是内核态还是用户态,这样一致的风格,应该非常利于扩展和维护。看代码的人也会觉得非常轻松,不至于无章可循。

DRBD的配置信息、虚拟设备、网络通信端口、对端信息等都是通过drbdsetup或者drbdadm工具以netlink消息包发送到内核态的。

在收到5号消息包时,drbd_nl_net_conf会被调用。在该函数中,会启动worker内核线程,该线程监控一个等待队列,当有事件到来时,即取出处理:

int drbd_worker(struct drbd_thread* thi)
{
...
w = NULL;
spin_lock_irq(&mdev->data.work.q_lock);
ERR_IF(list_empty(&mdev->data.work.q))
{
/* something terribly wrong in our logic.
* we were able to down() the semaphore,
* but the list is empty... doh.
*
* what is the best thing to do now?
* try again from scratch, restarting the receiver,
* asender, whatnot? could break even more ugly,
* e.g. when we are primary, but no good local data.
*
* I'll try to get away just starting over this loop.
*/
spin_unlock_irq(&mdev->data.work.q_lock);
continue;
}
w = list_entry(mdev->data.work.q.next, struct drbd_work, list);
list_del_init(&w->list);
spin_unlock_irq(&mdev->data.work.q_lock);

if (!w->cb(mdev, w, mdev->state.conn < C_CONNECTED))
{
/* dev_warn(DEV, "worker: a callback failed! \n"); */
if (mdev->state.conn >= C_CONNECTED)
drbd_force_state(mdev, NS(conn, C_NETWORK_FAILURE));
}
...
}

启动了worker线程之后,几乎所有的内核态的事务都会交给这个线程来处理。

继续回到drbd_nl_net_conf方法中,在初始化完worker线程后,会继续执行如下语句:

retcode=_drbd_request_state(mdev,NS(conn,C_UNCONNECTED),CS_VERBOSE);

这里既是与对端协商确定当前谁是主节点。在该方法中会向等待队列中放入一个事务,该事务为启动一个receiver线程,receiver线程会使用配置文件中指定的端口和IP信息建立tcp socket监听,等待对端的链接。此时,如果对端一直未有连接过来,本端尝试与对端连接也一直无法建立,则会根据配置等待指定的超时时间,之后会将本段置为Standalone状态。这也就是我们常见的两台服务器同时重启时,会发现一端的启动过程卡在drbd的等待上面。

| 1 分2 分3 分4 分5 分 (4.83- 6票) Loading ... Loading ... | 归档目录:C/C++, DRBD | 标签: |

迷宫营救公主算法

今天下班前在公司的技术题库中看到这道题目,思路很快就有了,递归遍历每一条可能的路径,然后找出最短的路径。回家把代码写出来了。发现算法效率实在是太低了,在矩阵较小的时候还好,当矩阵稍微大一点时根本算不过来,感觉复杂度像是O((N*M)^(N*M))这个数值太庞大了,也完全没有意义。公主等到花儿都谢啦。。

貌似这位同学的算法还不错,还没来得及研究,先做个链接:http://blog.csdn.net/joseph_1118/article/details/9390301

应付较小的矩阵下面的方法还是能蒙混过关的。先发到这里,后面再继续研究优化。题目和代码都在一起:

/**
* 公主被魔王抓走了,王子需要拯救出美丽的公主。他进入了魔王的城堡,魔王的城堡是一座很大的迷宫。
* 为了使问题简单化,我们假设这个迷宫是一个N*M的二维方格。迷宫里有一些墙,王子不能通过。王子只
* 能移动到相邻(上下左右四个方向)的方格内,并且一秒只能移动一步。地图由’S’,’P’,’.’,’*’
* 四种符号构成,’.’表示王子可以通过,’*’表示墙,王子不能通过;’S’表示王子的位置;’P’表示公主
* 的位置;T表示公主存活的剩余时间,王子必须在T秒内到达公主的位置,才能救活公主。如下图所示:
*/

阅读全文 »

| 1 分2 分3 分4 分5 分 (5.00- 9票) Loading ... Loading ... | 归档目录:Java, 算法数据结构 | 标签: , |

彻底解决WordPress博客垃圾评论的问题

本来这里很少有评论,但是来自日语或者英语的垃圾评论却非常多,可以用wordpress自带的过滤功能禁止其中的完全英语的垃圾评论,但是没有办法禁止日语的,甚至其他各种语言的,在网络上面发现有人使用计算加法作校验的方式来屏蔽来解决。因为垃圾评论都是用“机器人”发出的,他们不可能去分析评论的额外接口。利用wordpress提供的comments的两个回调即可实现。

在网上下载了插件的代码,稍作了修改。

修改步骤:

1、将如下文件解压缩后放到wordpress的如下目录中:$wordpress-root-path\wp-content\plugins,形成$wordpress-root-path\wp-content\plugin\comment_capatcha.php的目录结构。粗体部分替换成你的wordpress安装的根目录。这个过程实际上相当于安装了一个新的插件。

下载链接:http://codefine.site/wp-content/uploads/2013/08/comment_capatcha.zip

这里也把代码贴出来:

<?php

/*
Plugin Name: 简单算术题评论验证码插件
Plugin URI: http://www.utubon.com/wordpress-plugin-comment-capatcha-figure/
Description: 提交评论之前必须写出简单的算术题答案
Version: 1.0
Author: 否子戈
Author URI: http://www.utubon.com
*/

if(!class_exists('comment_capatcha')) {
class comment_capatcha {
function __construct() {
add_action('comment_form', array(& $this, 'print_capatcha'));
add_filter('preprocess_comment', array(& $this, 'preprocess_comment'));
}
function print_capatcha() {
if(!is_user_logged_in()) {
global $post;
session_start();
$rand_1 = mt_rand(1,6);
$rand_2 = mt_rand(1,6);
$_SESSION['capatcha_'.$post->ID] = $rand_1 + $rand_2;

$str = '<div id="capatcha-area"><label>';
$str .= "{$rand_1} + {$rand_2} = ".'<input type="text" size="2" name="capatcha" id="capatcha" />';
$str .= 'Result<span class="required">*</span>';
$str .= '</label></div>';
$str .= '<br>';
echo $str;
}
}
function preprocess_comment($commentdata) {
if(!is_user_logged_in()) {
session_start();
$post_id = isset($_POST['comment_post_ID']) ? $_POST['comment_post_ID'] : 0;
if(!$post_id){
wp_die('数据来源非法。');
}
$capatcha = $_SESSION['capatcha_'.$post_id];
if($capatcha != $_POST['capatcha']){
wp_die( __('请返回后重新输入算数题答案!') );
}
unset($_SESSION['capatcha_'.$post_id]);
}
return $commentdata;
}
}

}

if( !isset($comment_capatcha) ) {
$comment_capatcha =& new comment_capatcha();
}

?>

2、修改算术计算行所放的位置

我的主题默认是放在提交按钮之上,评论内容之后,可以移动到评论内容之前,作如下修改即可:(图片较小,点击后可以在新窗口查看大图)

compare

移动doaction函数的调用点即可修改算术框的位置。

3、到插件管理页面开启插件:简单算术题评论验证码插件

本站修改实现之后的效果为:

modify_posting_commeont

| 1 分2 分3 分4 分5 分 (5.00- 2票) Loading ... Loading ... | 归档目录:建站技术 | 标签: , |

写过的真正意义上的第一个C++程序

博客的发布记录为:
By YANQUN | Published: 二月 24, 2008 | Edit
 
记得当时刚过完年回到学校,看了一个寒假的书,迫不及待的想实践一下,于是参照《Exceptional C++》上面反复拿出来举例的Complex类,自己重新写了一个,虽然不是独创的。首先规划好了想要实现哪些接口,然后一条一条地到《C++ Primer》中去找相应的注意事项和一般约定。整个代码几乎是自己独立写出的。后来在面试过程中,还重新将这份代码又写了一遍提交给考官,呵呵,当时给他的感觉估计还好,但是他哪知道我只会这几行代码 :-)
 
感觉现在写代码的风格几乎都是那个时候定型的。喜欢比较整洁的风格,看到乱七八糟的代码总有重构的冲动。

阅读全文 »

| 1 分2 分3 分4 分5 分 (4.75- 4票) Loading ... Loading ... | 归档目录:C/C++, 生活札记, 软件技术 | 标签: , , |

不一样的童年,不一样的快乐

最近几乎把拍成电影的宫崎骏作品全部看完了,但是看过之后几乎都记不住具体的故事情节,只感觉每一部都很感人,都很美好。而且宫崎骏先生的作品大多有一个共同的特点,一个有着一般人没有的特殊能力的女主角,一段特殊的经历,引发观者对环境破坏问题或者人性贪婪的思考,大多套用这样的模式,现在对片名与故事情节的时候都会弄混淆,因此想写一点观后感就更困难了,必须得看第二遍才行。虽然模式相近,但是每一部都有不同的精彩。

就从最近看这一部《魔女宅急便》开始吧。看这部电影的时候,我更多的想到了自己的童年,大概有多少年没有回忆过自己的童年了?正好有机会写一下,不至于以后慢慢的都想不起来了。

魔女宅急便_1989_BD

生活在魔女家庭应该是一件非常幸福和令人羡慕的事情,然而却有“修行”这件事,在我现在看来这将是一段比较难熬的时光,但是剧中的主角琪琪却不这么想,她是很期待的。也许在我十三岁的时候也会无比期待能够自己一个人到外面独闯天下。十三岁刚好是小学毕业准备上初中的时候,我的初中是在一个离家比较远的地方上的,寄居在大伯父家里,虽然伯父伯母对我都很好,但是还是没有在自家自在,很多在农村的不好的生活习惯会与相对发达的城镇的生活习惯相冲突,从我自身的意义上面来说,也可以把这段经历比作是一次修行。在去上初中之前,我也同样怀着一种无比期待和想往的心情,同时也有一点小小的顾虑,担心自己能不能做好这段“修行”。现在回想起来,这段经历还是非常快乐和充实的。由于自己的性格的原因,期间有不少令人哭笑不得的尴尬事情。

我的童年有一小半的时间是在上学放学的路上度过的,家里离学校比较远,每天要翻过几座小山丘,走上1.5公里的山路、田埂路,那个时候就能体会到生活的艰辛,最艰难的时候就是春秋时节,必须穿着鞋子上学,但是田埂上的长草沾满了露水,鞋子会被湿透,在教室里等到鞋子快干的时候,又到了早读放学时间,这时路上的露水还没有完全干,又经受一次洗礼。不过也有很多乐趣,最高兴的时候是晚上放学,大概是下午5点多钟,一路小跑着赶回家中看《圣斗士》,看完动画片吃饭,写作业,每天最开心的时候就是这段时光了。遇到夏天,偶尔天气晴朗的时候,借着月光,还会有三五个小伙伴聚集在我家门前的空地上玩游戏。那时候农村还经常停电,一到晚上,没有电风扇根本睡不着,经常会在屋外用长凳、门板和蚊帐搭一个简易帐篷,躺在床上,听着蚊帐外面的蚊子嗡嗡声,数着天上的星星,不觉就睡着了,等到快转钟的时候,气温下降了一些,有点微凉了,被妈妈叫起来搬进屋里睡,美梦被中断了,这个时候是极不情愿的回到屋里。

魔女

小魔女出场的时候,因为自己是飞在天上的,引起了接上面的人群的羡慕,因为成为大家瞩目的焦点,不觉有一点得意忘形了,最后撞上了大巴车,还被警察逮住了,幸好有“蜻蜓”机智地解围,相信每个小孩都有过这样类似的闯祸经历。后面上街买生活必需品,对着橱窗里漂亮的鞋子久久凝视,不愿离去,可以体会到那份渴望而自己又非常懂事,知道钱应该花在应该花的地方的矛盾心情。小时候的我也总是非常羡慕其他小朋友,总渴望着有一天自己也能有一身帅气的衣服。把不同面额的纸币分堆放,以方便得出总数,自己似乎也这么做过。记得跟着妈妈上街卖自家养的母鸡生的蛋,帮着计算价格,算数学得好,反应也快,卖完了鸡蛋,在街上吃早点。回家了还要再计算一遍是不是有弄错的地方。

人生短暂,每一段时光都应该快乐度过,没有理由让任何阶段在煎熬中徘徊。希望自己能时时保持一颗童心,时时不忘微笑面对坎坷,找回那份本该属于自己的快乐生活。

| 1 分2 分3 分4 分5 分 (5.00- 4票) Loading ... Loading ... | 归档目录:生活札记, 观影随想 | 标签: , , , |

让进程在后台可靠运行的几种方法

几年前在developerWorks上面看到的文章,感觉非常实用,又简单整理了一下,转到这里,希望给看到的人带来一些帮助。文中提到的nohup和subshell方式一直在使用。

我们经常会碰到这样的问题,用 telnet/ssh 登录了远程的 Linux 服务器,运行了一些耗时较长的任务, 结果却由于网络的不稳定导致任务中途失败。如何让命令提交后不受本地关闭终端窗口/网络断开连接的干扰呢?下面举了一些例子, 您可以针对不同的场景选择不同的方式来处理这个问题。

如果只是临时有一个命令需要长时间运行,什么方法能最简便的保证它在后台稳定运行呢?

解决方法:

1.nohup
我们知道,当用户注销(logout)或者网络断开时,终端会收到HUP(hangup)信号从而关闭其所有子进程。因此,我们的解决办法就有两种途径:要么让进程忽略 HUP 信号,要么让进程运行在新的会话里从而成为不属于此终端的子进程。

HANGUP名称的来由,在Unix的早期版本中,每个终端都会通过modem 和系统通讯。当用户 logout 时,modem 就会挂断(hang up)电话。 同理,当modem断开连接时,就会给终端发送hangup信号来通知其关闭所有子进程。

nohup 无疑是我们首先想到的办法。顾名思义,nohup 的用途就是让提交的命令忽略 hangup 信号。让我们先来看一下 nohup 的帮助信息:

NOHUP(1)                        User Commands                        NOHUP(1)
NAME
nohup – run a command immune to hangups, with output to a non-tty

SYNOPSIS
nohup COMMAND [ARG]…
nohup OPTION

DESCRIPTION
Run COMMAND, ignoring hangup signals.
–help display this help and exit
–version output version information and exit

可见,nohup 的使用是十分方便的,只需在要处理的命令前加上 nohup 即可,标准输出和标准错误缺省会被重定向到 nohup.out 文件中。一般我们可在结尾加上“&”来将命令同时放入后台运行,也可用”>filename 2>&1″来更改缺省的重定向文件名。

nohup 示例

[root@pvcent107 ~]# nohup ping www.ibm.com &; 
[1] 3059 nohup: appending output to `nohup.out' 
[root@pvcent107 ~]# ps -ef |grep 3059 
root 3059 984 0 21:06 pts/3 00:00:00 ping www.ibm.com 
root 3067 984 0 21:06 pts/3 00:00:00 grep 3059 
[root@pvcent107 ~]#

 

2.setsid

nohup 无疑能通过忽略 HUP 信号来使我们的进程避免中途被中断,但如果我们换个角度思考,如果我们的进程不属于接受 HUP 信号的终端的子进程,那么自然也就不会受到 HUP 信号的影响了。setsid 就能帮助我们做到这一点。让我们先来看一下 setsid 的帮助信息:

SETSID(8)                 Linux Programmer’s Manual                 SETSID(8)

NAME

setsid – run a program in a new session

SYNOPSIS

setsid program [ arg … ]

DESCRIPTION

setsid runs a program in a new session.

可见 setsid 的使用也是非常方便的,也只需在要处理的命令前加上 setsid 即可。

setsid 示例

[root@pvcent107 ~]# setsid ping www.ibm.com 
[root@pvcent107 ~]# ps -ef |grep www.ibm.com 
root 31094 1 0 07:28 ? 00:00:00 ping www.ibm.com 
root 31102 29217 0 07:29 pts/4 00:00:00 grep www.ibm.com 
[root@pvcent107 ~]#

 

值得注意的是,上例中我们的进程 ID(PID)为31094,而它的父 ID(PPID)为1(即为 init 进程 ID),并不是当前终端的进程 ID。请将此例与nohup 例中的父 ID 做比较。

3.&符号(命令简单易记,推荐使用。)

这里还有一个关于 subshell 的小技巧。我们知道,将一个或多个命令包含在“()”中就能让这些命令在子 shell 中运行中,从而扩展出很多有趣的功能,我们现在要讨论的就是其中之一。

当我们将”&”也放入“()”内之后,我们就会发现所提交的作业并不在作业列表中,也就是说,是无法通过jobs来查看的。让我们来看看为什么这样就能躲过 HUP 信号的影响吧。

subshell 示例

[root@pvcent107 ~]# (ping www.ibm.com &;) 

[root@pvcent107 ~]# ps -ef |grep www.ibm.com 
root 16270 1 0 14:13 pts/4 00:00:00 ping www.ibm.com 
root 16278 15362 0 14:13 pts/4 00:00:00 grep www.ibm.com 
[root@pvcent107 ~]#

 

从上例中可以看出,新提交的进程的父 ID(PPID)为1(init 进程的 PID),并不是当前终端的进程 ID。因此并不属于当前终端的子进程,从而也就不会受到当前终端的 HUP 信号的影响了。

我们已经知道,如果事先在命令前加上 nohup 或者 setsid 就可以避免 HUP 信号的影响。但是如果我们未加任何处理就已经提交了命令,该如何补救才能让它避免 HUP 信号的影响呢?

解决方法:

 

阅读全文 »

| 1 分2 分3 分4 分5 分 (5.00- 2票) Loading ... Loading ... | 归档目录:实用脚本, 软件技术 | 标签: , , , , |

12款强大的Chrome插件

Chrome功能强大,也得益于其拥有丰富的扩展资源库。Chrome Web Store里有各种各样的插件,Google Store可以满足你使用Chrome时的各种要求。和Firefox一样,Chrome的扩展非常容易安装,而且非常容易卸载。与Firefox不同,Chrome的扩展不需要重新启动,并且不会有扩展插件会减小你的网页面积。在这里我总结出2013年 Chrome 的12款非常强大的扩展程序,供大家挑选分享。这些插件能不同程度地提升效率。诸如Turn off the light这些非常常用的我就不介绍了。

阅读全文 »

| 1 分2 分3 分4 分5 分 (5.00- 2票) Loading ... Loading ... | 归档目录:移动互联 | 标签: , |

Windows XP不能向VMware虚拟机传输大文件的问题分析和解决

不知道从什么时候开始,XP不能向虚拟机传送大文件了,包括借助于vmtools、sftp、samba和ftp都不行,无论哪一段作服务端,都是一样。samba最后的提示是“文件路径太深”。显然不是因为这个原因。使用sftp或者ftp,一般是文件名创建成功后,数据传不出去。如下图:

阅读全文 »

| 1 分2 分3 分4 分5 分 (5.00- 4票) Loading ... Loading ... | 归档目录:软件技术 | 标签: , , , |
返回顶部