文档库 最新最全的文档下载
当前位置:文档库 › Epoll模型详解

Epoll模型详解

Epoll模型详解
Epoll模型详解

在linux的网络编程中,很长的时间都在使用select来做事件触发。在linux新的内核中,有了一种替换它的机制,就是epoll。

相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。并且,在linux/posix_types.h头文件有这样的声明:

#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被耗尽。

2.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结构如下:

struct epoll_event {

__uint32_t events; /* Epoll events */

epoll_data_t data; /* User data variable */

};

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

EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);EPOLLOUT:表示对应的文件描述符可以写;

EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);

EPOLLERR:表示对应的文件描述符发生错误;

EPOLLHUP:表示对应的文件描述符被挂断;

EPOLLET:将EPOLL设为边缘触发(Edge T riggered)模式,这是相对于水平触发(Level T riggered)来说的。

EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

3.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和L T的具体描述如下

EPOLL事件有两种模型:

Edge Triggered (ET) 边缘触发只有数据到来,才触发,不管缓存区中是否还有数据。

Level Triggered (L T) 水平触发只要有数据都会触发。

假如有这样一个例子:

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 工作模式

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

然后详细解释ET, L T:

L T(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()返回的大小如果等于请求的大小,那么很有可能是缓冲区还有数据未读完,也意味着该次事件还没有处理完,所以还需要再次读取:

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()内部,但暂没有更好的办法.

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;

}

代码:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define MAXLINE 10

#define OPEN_MAX 100

#define LISTENQ 20

#define SERV_PORT 5555

#define INFTIM 1000

//线程池任务队列结构体

struct task

{

int fd; //需要读写的文件描述符

struct task *next; //下一个任务

};

//用于读写两个的两个方面传递参数

struct user_data

{

int fd;

unsigned int n_size;

char line[MAXLINE];

};

//线程的任务函数

void * readtask(void *args);

void * writetask(void *args);

//声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件struct epoll_event ev, events[20];

int epfd;

pthread_mutex_t mutex;

pthread_cond_t cond1;

struct task *readhead = NULL, *readtail = NULL, *writehead = NULL; void setnonblocking(int sock)

{

int opts;

opts = fcntl(sock, F_GETFL);

if (opts < 0)

{

perror("fcntl(sock,GETFL)");

exit(1);

}

opts = opts | O_NONBLOCK;

if (fcntl(sock, F_SETFL, opts) < 0)

{

perror("fcntl(sock,SETFL,opts)");

exit(1);

}

}

int main()

{

int i, maxi, listenfd, connfd, sockfd, nfds;

pthread_t tid1, tid2;

struct task *new_task = NULL;

struct user_data *rdata = NULL;

socklen_t clilen;

pthread_mutex_init(&mutex, NULL);

pthread_cond_init(&cond1, NULL);

//初始化用于读线程池的线程

pthread_create(&tid1, NULL, readtask, NULL);

pthread_create(&tid2, NULL, readtask, NULL);

//生成用于处理accept的epoll专用的文件描述符

epfd = epoll_create(256);

struct sockaddr_in clientaddr;

struct sockaddr_in serveraddr;

listenfd = socket(AF_INET, SOCK_STREAM, 0);

//把socket设置为非阻塞方式

setnonblocking(listenfd);

//设置与要处理的事件相关的文件描述符

ev.data.fd = listenfd;

//设置要处理的事件类型

ev.events = EPOLLIN | EPOLLET;

//注册epoll事件

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

bzero(&serveraddr, sizeof(serveraddr));

serveraddr.sin_family = AF_INET;

char *local_addr = "200.200.200.222";

inet_aton(local_addr, &(serveraddr.sin_addr));//htons(SERV_PORT);

serveraddr.sin_port = htons(SERV_PORT);

bind(listenfd, (sockaddr *) &serveraddr, sizeof(serveraddr));

listen(listenfd, LISTENQ);

maxi = 0;

for (;;)

{

//等待epoll事件的发生

nfds = epoll_wait(epfd, events, 20, 500);

//处理所发生的所有事件

for (i = 0; i < nfds; ++i)

{

if (events[i].data.fd == listenfd)

{

connfd = accept(listenfd, (sockaddr *) &clientaddr, &clilen);

if (connfd < 0)

{

perror("connfd<0");

exit(1);

}

setnonblocking(connfd);

char *str = inet_ntoa(clientaddr.sin_addr);

std::cout << "connec_ from >>" << str << std::endl;

//设置用于读操作的文件描述符

ev.data.fd = connfd;

//设置用于注测的读操作事件

ev.events = EPOLLIN | EPOLLET;

//注册ev

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

} else

if (events[i].events & EPOLLIN)

{

printf("reading!\n");

if ((sockfd = events[i].data.fd) < 0) continue;

new_task = new task();

new_task->fd = sockfd;

new_task->next = NULL;

//添加新的读任务

pthread_mutex_lock(&mutex);

if (readhead == NULL)

{

readhead = new_task;

readtail = new_task;

} else

{

readtail->next = new_task;

readtail = new_task;

}

//唤醒所有等待cond1条件的线程

pthread_cond_broadcast(&cond1);

pthread_mutex_unlock(&mutex);

} else

if (events[i].events & EPOLLOUT)

{

rdata = (struct user_data *) events[i].data.ptr;

sockfd = rdata->fd;

write(sockfd, rdata->line, rdata->n_size);

delete rdata;

//设置用于读操作的文件描述符

ev.data.fd = sockfd;

//设置用于注测的读操作事件

ev.events = EPOLLIN | EPOLLET;

//修改sockfd上要处理的事件为EPOLIN

epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);

}

}

}

}

void * readtask(void *args)

{

int fd = -1;

unsigned int n;

//用于把读出来的数据传递出去

struct user_data *data = NULL;

while (1)

{

pthread_mutex_lock(&mutex);

//等待到任务队列不为空

while (readhead == NULL)

pthread_cond_wait(&cond1, &mutex);

fd = readhead->fd;

//从任务队列取出一个读任务

struct task *tmp = readhead;

readhead = readhead->next;

delete tmp;

pthread_mutex_unlock(&mutex);

data = new user_data();

data->fd = fd;

if ((n = read(fd, data->line, MAXLINE)) < 0)

{

if (errno == ECONNRESET)

{

close(fd);

} else

std::cout << "readline error" << std::endl;

if (data != NULL) delete data;

} else

if (n == 0)

{

close(fd);

printf("Client close connect!\n");

if (data != NULL) delete data;

} else

{

data->n_size = n;

//设置需要传递出去的数据

ev.data.ptr = data;

//设置用于注测的写操作事件

ev.events = EPOLLOUT | EPOLLET;

//修改sockfd上要处理的事件为EPOLLOUT

epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev);

}

}

}

百万用户同时在线游戏服务器架构实现

百万用户在线网络游戏服务器架构实现 一、前言 事实上100万游戏服务器,在面对大量用户访问、高并发请求方面,基本的解决方案集中在这样几个环节:使用高性能的服务器、高效率的编程语言、高性能的数据库、还有高性能的架构模型。但是除了这几个方面,还没法根本解决面临的高负载和高并发问题。 当然用户不断地追求更高的机器性能,而升级单一的服务器系统,往往造成过高的投入和维护成本,性价比大大低于预期。同时全天候的可用性的要求也不能满足要求,如果服务器出现故障则该项服务肯定会终止。所以单独追求高性能的服务器不能满足要求,目前基本的解决方案是使用集群技术做负载均衡,可以把整体性能不高的服务器做成高可扩展性,高可用性,高性能的,满足目前的要求。 目前解决客户端和服务器进行底层通讯的交互的双向I/O模型的服务器的成熟方案。 1.windows下,比较成熟的技术是采用IOCP,完成端口的服务器模型。 2.Linux下,比较成熟的技术是采用Epoll服务器模型, Linux 2.6内核中提供的System Epoll 为我们提供了一套完美的解决方案。 目前如上服务器模型是完全可以达到5K到20K的同时在线量的。但5K这样的数值离百万这样的数值实在相差太大了,所以,百万人的同时在线是单台服务器肯定无法实现的。 而且目前几个比较成熟的开发框架,比如ICE,ACE等。这样,当采用一种新的通信技术来实现通信底层时,框架本身就不用做任何修改了(或修改很少),而功能很容易实现,性能达到最优。目前采用的ace框架个不错的选择方案,可以不受操作系统的影响,移植比较方便。 对于数据库选择可有许多成熟的方案,目前大多数选择的mysql Master/slave模式,以及oracle RAC方案。基本可以满足目前的要求,但具体的瓶颈不是在数据库本身,应该还是硬件磁盘I/O的影响更大些。建议使用盘阵。这有其他成熟的方案,比如采用NAS解决分布数据存储。 其实最为关键的是服务器的架构和实现,数据流量的负载均衡,体系的安全性,关键影响度,共享数据的处理等等多个方面对100万用户的数据处理有影响,所以都要全面的考虑。 二、高性能的服务器 1.网络环境 目前采用Client/Server架构来开发网络游戏,客户端和服务器一般通过TCP/UDP协议进

java技术面试必问:JVM 内存模型讲解

java技术面试必问:JVM 内存模型讲解 今天我们就来聊一聊Java内存模型,面试中面试官会通过考察你对jvm的理解更深入得了解你的水平。在了解jvm内存模型前我们先回顾下,java程序的执行过程: java文件在通过java编译器生产.class 字节码文件,然后由jvm中的类加载器加载各个类中的字节码文件,加载完成后由jvm执行引擎执行,在整个加载过程中,jvm用一段空间来存储程序执行期间需要的数据和相关信息,这个空间就叫做jvm内存。 一、JVM 的重要性 首先你应该知道,运行一个 Java 应用程序,我们必须要先安装 JDK 或者 JRE 。这是因为 Java 应用在编译后会变成字节码,然后通过字节码运行在 JVM 中,而 JVM 是JRE 的核心组成部分。 二、优点 JVM 不仅承担了 Java 字节码的分析(JIT compiler)和执行(Runtime),同时也内置了自动内存分配管理机制。这个机制可以大大降低手动分配回收机制可能带来的内存泄露和内存溢出风险,使 Java 开发人员不需要关注每个对象的内存分配以及回收,从而更专注于业务本身。 三、缺点 这个机制在提升 Java 开发效率的同时,也容易使 Java 开发人员过度依赖于自动化,弱化对内存的管理能力,这样系统就很容易发生 JVM 的堆内存异常、垃圾回收(GC)的不合适以及 GC 次数过于频繁等问题,这些都将直接影响到应用服务的性能。 四、内存模型 JVM 内存模型共分为5个区:堆(Heap)、方法区(Method Area)、程序计数器(Program Counter Register)、虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)。 其中,堆(Heap)、方法区(Method Area)为线程共享,程序计数器(Program Counter Register)、虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)为线程隔离。 五、堆(Heap) 堆是 JVM 内存中最大的一块内存空间,该内存被所有线程共享,几乎所有对象和数组都被分配到了堆内存中。 堆被划分为新生代和老年代,新生代又被进一步划分为 Eden 区和 Survivor 区,最后 Survivor 由 From Survivor 和 To Survivor 组成。

[工作流]建立一个工作流程的简明过程

建立一个工作流程的简明过程 引言:有很多刚接触通达OA的用户在刚开始使用OA 工作流时会问,我怎样把这个模块用起来呢,或者诸如,从哪里开始入手,表单如何建立,流程怎么设计,咋新建工作模块怎么就看不到流程等等。为此,我总结了建立一个简单流程雏形的必要项目,在基于工作流使用详解的基础上,再讲解一下从表单—流程—建立工作的简明过程。 图1 1.设计表单 在系统管理—工作流设置—设计表单,填写表单名称和所属部门,保存。 注:刚建立的的表单是在未分类下的,这点您不用怀疑,或者也不用想把它移到那个分类下,它会在之后建立的流程调用表单时,随流程的分类而分类。 A.表单可以用其他软件制作好,保存成TXT、HTM或HTML格式文件导入,或是在智能设计器新建。 B.添加控件,进入智能设计器右侧一列是工作流表单基本元素---控件,这些都需要根据需要手动添加,填写控件名称、基本属性确定,保存表单,此后您还可以点击预览表单参看效果。

图2 至于具体每个控件的说明参照《通达2010版工作流使用详解》。 2设计流程 在系统管理—工作流设置—设计流程,填写名称、选择所属部门,表单和分类等项,保 存。之后您就可以在该分类下找到流程进入流程设计器 图3 打开流程设计器,有两种视图方式,图形视图和列表视图可切换。 图形视图下的操作都是通过鼠标右击来选择。(如果这里没有显示出彩色图形,请设置 您的IE 安全相关项)

图4 列表视图下的操作可以直接按显示选择。 图5 A.依次新建步骤,选择下一步骤来控制流程走向(默认是按照建立的顺序依次排序),保存。 B.经办权限:建立步骤之后最好先把经办权限设置好,这关系到最后走工作流时能否顺利选人转交,如果因为没有设置经办权限而不能转交,还要返回到设计阶段岂不是影响工作效率?当然如果是自由流程就另当别论,但是有一点不管是自由流程还是固定流程都不能例外,那就是第一步的经办权限,一定要设置,否则在新建工作的时候是看不到该流程的。 C.可写字段:要想在实际办理工作的时候对表单进行一些填写审批的操作,那就一定是

Windows Socket五种IO模型——代码全攻略

Windows Socket五种I/O模型——代码全攻略 Winsock 的I/O操作: 1、两种I/O模式 阻塞模式:执行I/O操作完成前会一直进行等待,不会将控制权交给程序。套接字默认为阻塞模式。可以通过多线程技术进行处理。 非阻塞模式:执行I/O操作时,Winsock函数会返回并交出控制权。这种模式使用起来比较复杂,因为函数在没有运行完成就进行返回,会不断地返回WSAEWOULDBLOCK错误。但功能强大。 为了解决这个问题,提出了进行I/O操作的一些I/O模型,下面介绍最常见的三种: Windows Socket五种I/O模型——代码全攻略 如果你想在Windows平台上构建服务器应用,那么I/O模型是你必须考虑的。Windows操作系统提供了选择(Select)、异步选择(WSAAsyncSelect)、事件选择(WSAEventSelect)、重叠I/O(Overlapped I/O)和完成端口(Completion Port)共五种I/O模型。每一种模型均适用于一种特定的应用场景。程序员应该对自己的应用需求非常明确,而且综合考虑到程序的扩展性和可移植性等因素,作出自己的选择。 我会以一个回应反射式服务器(与《Windows网络编程》第八章一样)来介绍这五种I/O模型。 我们假设客户端的代码如下(为代码直观,省去所有错误检查,以下同): #include #include #define SERVER_ADDRESS "137.117.2.148" #define PORT 5150 #define MSGSIZE 1024 #pragma comment(lib, "ws2_32.lib") int main() { WSADA TA wsaData; SOCKET sClient; SOCKADDR_IN server; char szMessage[MSGSIZE]; int ret; // Initialize Windows socket library WSAStartup(0x0202, &wsaData);

工作流引擎讲解

什么是工作流引擎,工作流引擎有什么作用,为什么需要工作流管理系统,在这里我们主要研究它的好处,你想要理解它的好处,就得知道不使用它会带来什么样的坏处。 现在我们来讲工作流,什么是工作流?所谓的工作流就是通常所说的业务流程,那么所谓的业务流程换句话来讲就是多个人在一起去完成一件事情。这就可以称之为工作流。流程的本质就是一个参与者参与到一个过程里面来 那么现在我们就想为什么需要工作流管理系统,工作流管理系统能给我们带来什么好处。我们就从这个角度出发来了解JBPM 工作流引擎 下面我们就来看关于为什么需要工作流管理系统,以及它给我们带来的好处。 实际上它带来的好处就是使某些容易变化的东西抽象出去,我们能够通过某种方式改变它,然后你就可以对你的某些核心部分不需要做什么变动 现在就通过一个小例子来讲这个工作流引擎到底是一个什么东西 举个请假流程的例子 一个请假的过程 重点讲解UML 里面的内容,确定UML 里面流程图的讲解顺序 请假流程 现在只看左边的内容,右边的后面再讲,我是方便讲解就将这点东西放到这个空白的地方 一个简单的流程图Main 模拟出请假的过程

对提交请假单进行分析 用一个用户来表示普通用户和审批者,只不过他们的权限不同,他们都能够登录到这个系统 现在我们来看用户和请假单,分析他们之间的关系,用户和请假单之间的联系有请假,用户填了一个请假单就创建了一个请假单对象,他们之该是一对多的关系。因为某一个用户可以请多次假 对吧(其实一般是一个请假单对应一个请假者,这个需求就应该得到客户的确定,客户说了算)那么用户和这个请假单之间还有没有其他联系? 接下来是提交请假单。我首先将请假单提交给张三,那么张三就能够看到这个请假单,如果用户将请假单提交给张三,那么就可以在张三和请假单之间建立一个待审关系 他们之间的关系也是一对多的关系,因为张三可以同时审核几个请假单,就是这意思,一个请假单等待的用户是一个,从现在的需求来看。那么两者之间还有另外一个联系那就是已审,一个用户可以审批过多个请假单,请假单也可以被多个用户审批 比如张三审批以后交给李四审批,李四审批以后交给王五审批,其实这个已审就是记录审批信息的,比如审批时间,审批意见,把它放在审批关联里边 这个就是一个基本的概念,了解这个概念之后我们就考虑它的设计,JBPM 实际上就是协助我们把这个请假单从一个用户手上转递到另一个用户手上。当把这个模型分析清楚了我们就要去实现它。 这里重点分析提交,怎样去提交,在SSH 架构体下,提交请假单这个业务逻辑,你可能就需要这样一个业务逻辑类,里边可能有这么一个方法专门来进行提交操作的,那么这个方法怎样设计,以及这个方法怎样去实现。了解这点你就可以了解JBPM 干什么的,能给我们带来什么好处 (用自己的话说明一下提交请假单的过程 <读一下那段伪代码>) 在这个过程里边写这些代码是比较麻烦的,现在还只是一个固定的流程,假设我现在在这里边变化一下 那么整个方案都要变动。 我现在希望有一个会签的功能 比如我现在要将这个这样的功能,把这个请假单同时提交给多个审批者审批。 那这个时候你就不能够在请假单中间增加一个外键, 把它整成审批者什么的,

epoll精髓

epoll精髓 在linux的网络编程中,很长的时间都在使用select来做事件触发。在linux新的内核中,有了一种替换它的机制,就是epoll。 相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。并且,在linux/posix_types.h头文件有这样的声明: #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被耗尽。 2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:

精选大厂java多线程面试题50题

Java多线程50题 1)什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒。 2)线程和进程有什么区别? 线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。更多详细信息请点击这里。 3)如何在Java中实现线程? https://www.wendangku.net/doc/8812564468.html,ng.Thread类的实例就是一个线程但是它需要调用https://www.wendangku.net/doc/8812564468.html,ng.Runnable接口来执行,由于线程类本身就是调用的 Runnable接口所以你可以继承https://www.wendangku.net/doc/8812564468.html,ng.Thread类或者直接调用Runnable接口来重写run()方法实现线程。 4)Thread类中的start()和run()方法有什么区别? 这个问题经常被问到,但还是能从此区分出面试者对Java线程模型的理解程度。start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你

调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。 5)Java中Runnable和Callable有什么不同? Runnable和Callable都代表那些要在不同的线程中执行的任务。Runnable从JDK1.0开始就有了,Callable是在JDK1.5增加的。它们的主要区别是Callable的call()方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结果的Future对象。 6)Java内存模型是什么? Java内存模型规定和指引Java程序在不同的内存架构、CPU 和操作系统间有确定性地行为。它在多线程的情况下尤其重要。 Java内存模型对一个线程所做的变动能被其它线程可见提供了保证,它们之间是先行发生关系。 ●线程内的代码能够按先后顺序执行,这被称为程序次序 规则。 ●对于同一个锁,一个解锁操作一定要发生在时间上后发 生的另一个锁定操作之前,也叫做管程锁定规则。 ●前一个对Volatile的写操作在后一个volatile的读操作之 前,也叫volatile变量规则。 ●一个线程内的任何操作必需在这个线程的start()调用之 后,也叫作线程启动规则。 ●一个线程的所有操作都会在线程终止之前,线程终止规

Activiti工作流入门详解完整教学教程

Activiti入门教程详解完整教程 1.A ctiviti介绍 Activiti是由Alfresco软件在2010年5月17日发布的业务流程管理(BPM)框架,它是覆盖了业务流程管理,工作流,服务协作等领域的一个开源,灵活的,易扩展的可执行流程语言框架。 Activiti基于Apache许可的开源BPM平台,创始人Tom Baeyens是JBoss JBPM的项目架构师,它的特色是提供了eclipse插件,开发人员可以通过插件直接绘画出业务流程图。 1.1工作流引擎 ProcessEngine对象,这是Activiti工作的核心。负责生成流程运行时的各种实例及数据,监控和管理流程的运行。 1.2BPMN 业务流程建模与标注(Business Process Model and Notation,BPMN),描述流程的基本符号,包括这些图元如何组合成一个业务流程图(Business Process Diagram)

2.准备环境 2.1Activiti软件环境 1)JDK1.6或者更高版本 2)支持的数据库有:h2,mysql,oracle,mysql,db2等 3)支持Activiti运行的jar包,可以通过maven依赖引入 4)开发环境为Eclipse3.7或者以上版本,myeclipse为8.6版本2.2安装流程设计器(eclipse插件) 1)打开Help →Install New Software →Add 输入Name: Activiti Designer Location: https://www.wendangku.net/doc/8812564468.html,/designer/update/ 输入完成后,单击OK按钮等待下载完成后安装。 安装完成后在菜单选项中会出现Activiti的目录选项

15个Java多线程面试题及答案

15个Java多线程面试题及答案 1)现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行? 这个线程问题通常会在第一轮或电话面试阶段被问到,目的是检测你对”join”方法是否熟悉。这个多线程问题比较简单,可以用join方法实现。 2)在Java中Lock接口比synchronized块的优势是什么?你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它? lock接口在多线程和并发编程中最大的优势是它们为读和写分别提 供了锁,它能满足你写像ConcurrentHashMap这样的高性能数据结构和有条件的阻塞。Java线程面试的问题越来越会根据面试者的回答来提问。芯学苑老师强烈建议在你在面试之前认真读一下Locks,因为当前其大量用于构建电子交易终统的客户端缓存和交易连接空间。 3)在java中wait和sleep方法的不同?

通常会在电话面试中经常被问到的Java线程面试问题。最大的不同是在等待时wait会释放锁,而sleep一直持有锁。Wait通常被用于线程间交互,sleep通常被用于暂停执行。 4)用Java实现阻塞队列。 这是一个相对艰难的多线程面试问题,它能达到很多的目的。第一,它可以检测侯选者是否能实际的用Java线程写程序;第二,可以检测侯选者对并发场景的理解,并且你可以根据这个问很多问题。如果他用wait()和notify()方法来实现阻塞队列,你可以要求他用最新的Java 5中的并发类来再写一次。 5)用Java写代码来解决生产者——消费者问题。 与上面的问题很类似,但这个问题更经典,有些时候面试都会问下面的问题。在Java中怎么解决生产者——消费者问题,当然有很多解决方法,我已经分享了一种用阻塞队列实现的方法。有些时候他们甚至会问怎么实现哲学家进餐问题。 6)用Java编程一个会导致死锁的程序,你将怎么解决?

OA工作流使用详解

信达OA工作流使用详解 概念篇 随着企业管理信息化进程的不断深入,协同应用软件的概念已深入人心,而工作流系统正是协同应用软件的核心。通过应用IT技术来规范工作流程、提高工作的执行效率和准确度,是工作流系统主要解决的问题。 信达OA的工作流功能正是为这一需求而设计,可实现业务或公文的申请、审批、会签、登记、操作等环节的管理,可将工作过程进行记录,便于日后检查。并实现数据的规范化录入、查询、统计和存档。 简单来说,工作流就是把一项工作化解为多个步骤,由多人协同来完成。这里所指的工作是形形色色的,在办公室里进行的很多不同类型的工作,都可以用工作流系统来表达,信达OA的工作流由表单和流程这2个重要因素构成:表单:在工作流系统中,与工作相关的数据都可以通过“表单”来体现,“表单”是数据的载体,相当于现实工作中的各类工作单;在表单以外,还可以通过附件文件来传递数据与信息。 流程:在工作流系统中,工作过程都可以通过“流程”的定义来体现,“流程”是工作过程的描述,代表了一种制度或规范。 工作流系统相当于一个“万能模块”,通过合理的流程设置,规范化运作,可对我们的日常办公和企业管理提供极大的便利。 信达OA工作流的一些概念: 1、工作流就是几个人协同完成一项工作,简单而言,就是几个人按次序填写同一张“表单”,填写会签意见和传递附件。 2、表单可以由软件用户自行设计(一般由管理员设计好)。 3、信达OA的表单格式可以用网页设计工具或word等设计,设计好后复制、粘贴到“表单智能设计器”中,再添加表单控件就可以了。 4、每个流程对应一个表单,不同流程可以共用同一个表单。 5、流程分为固定流程和自由流程两种,固定流程由固定步骤组成,用户事先需定义好,自由流程无需定义流程步骤。 6、固定流程的每个步骤都需要设定经办人、可写字段、下一步骤等信息。 7、固定流程第一个步骤设定的经办人,才有权新建该流程(道理可想而知)。 8、执行中的工作和已完成的工作,都可以通过工作查询功能进行查询。 9、任何流程都可以指定监控人员和查询人员,监控人员可随时转交下步或终止流程(自由流程),监控人员和查询人员都可以查询该流程全部工作。

心通达OA工作流使用说明书

心通达OA工作流使用说明书 【工作流使用详解】 2018-3-21 北京高速波软件有限公司

版权说明 本文件中出现的任何文字叙述、文档格式、插图、照片、方法、过程等内容,除非另有特别注明,版权均属北京高速波软件有限公司所有,受到有关产权及版权等法律保护。任何个人、机构未经北京高速波软件有限公司的书面授权许可,不得复制、引用或传播本文件的任何片断,无论通过电子形式或非电子形式。

目录 工作流设计 (5) 1.1分类设置 (5) 1.1.1表单分类 (5) 1.1.2流程分类 (6) 1.2自动编号设置 (8) 1.2.1自动编号设置 (8) 1.2.2自动编号预览 (9) 1.2.3编号管理 (10) 1.3设计表单 (10) 1.3.1新建表单 (10) 1.3.2编辑表单 (11) 1.3.3预览 (12) 1.3.4删除 (12) 1.3.5导入 (12) 1.3.6导出 (13) 1.3.7表单智能设计器 (13) 1.3.8表单控件 (14)

设计流程 (32) 1.4.1流程管理 (32) 1.5新建工作 (53) 1.6我的工作 (55) 1.7工作查询 (56) 1.8工作监控 (56)

工作流设计 1.1分类设置 1.1.1表单分类 进入工作流-工作流设置-分类设置, 建立表单分类。表单分类更方便了表单的管理,把不同性质的表单放在不同的分类下,也方便了表单的查找。同时根据表单分类的所属部门,实现了表单分类按部门进行独立管理的目的。 新建表单分类:首先点击【新建】按钮,根据具体需求选择表单父分类,填写相应的表单分类排序号,表单分类名称,以及所属部门后保存。

阿里P7笔试题

1.junit 用法,before,beforeClass,after, afterClass 的执行顺序 2.分布式锁 3.nginx 的请求转发算法,如何配置根据权重转发 4.用hashmap 实现redis 有什么问题(死锁,死循环,可用 ConcurrentH ashmap) 5.线程的状态 6.线程的阻塞的方式 7.sleep 和wait 的区别 8.hashmap 的底层实现 9.一万个人抢100 个红包,如何实现(不用队列),如何保证2 个人不 能抢 到同一个红包,可用分布式锁 10.java 内存模型,垃圾回收机制,不可达算法 11.两个Integer 的引用对象传给一个swap 方法在方法内部交换引用,返 回 后,两个引用的值是否会发现变化 12.aop 的底层实现,动态代理是如何动态,假如有100 个对象,如何动 态 的为这100 个对象代理 13.是否用过maven install。maven test。git(make install 是安装本 地jar 包) 14.tomcat 的各种配置,如何配置docBase 15.spring 的bean 配置的几种方式 16.web.xml 的配置 17.spring 的监听器。 18.zookeeper 的实现机制,有缓存,如何存储注册服务的 19.IO 会阻塞吗?readLine 是不是阻塞的 20.用过spring 的线程池还是java 的线程池? 21.字符串的格式化方法(20,21 这两个问题问的太低级了) 22.时间的格式化方法 23.定时器用什么做的 24.线程如何退出结束 25.java 有哪些锁?乐观锁悲观锁synchronized 可重入锁读写锁,用过r eentrantlock 吗?reentrantlock 与synmchronized 的区别 26.ThreadLocal 的使用场景 27.java 的内存模型,垃圾回收机制 28.为什么线程执行要调用start 而不是直接run(直接run,跟普通方法 没 什么区别,先调start,run 才会作为一个线程方法运行) 29.qmq 消息的实现机制(qmq 是去哪儿网自己封装的消息队列) 30.遍历hashmap 的三种方式 31.jvm 的一些命令 32.memcache 和redis 的区别

web应用框架-活字格工作流功能详解(下)

概述 本章节讲述了实际应用场景下如何灵活使用活字格工作流,包括工作流流程条的使用,以及工作流命令与批量工作流命令的设置。 业务场景描述 为了让大家能够在实际应用场景中理解工作流中的状态,普通流程,审批流程,以请休假管理模块为例,企业员工在提交请假申请单时,根据部门的不同判断是否需要经过人力资源部核准剩余年假,然后根据请假天数提交部门领导审批,部门领导审批状态为审批流程:小于3天,提交到部门副经理,小于5天,提交到部门经理,小于7天,公司副经理,小于10天,公司经理,大于等于10天,集团董事长,部门领导审批结束后提交人力资源部扣除年假流程结束。在流程流转到人资部结算时,设置提醒,要求提交给人资部结算1个小时以内每20分钟提醒一次,直到人资部结算提交流程。管理员可以随意将流程撤转到对应的环节上。 流程图如下: 请休假申请流程(包含普通流程+部门领导审批流程)

部门领导审批流程

工作流设置

通过上一章节流程功能点的讲解,配合流程图,我们可以将请休假流程在活字格中得以体现。在学习应用场景时,建议大家先学习了解下工作流中所有的功能,那样更方便大家针对特定流程去设计。 表和页面的创建在这里我就不为大家一一介绍了,直入主题,开启请休假表的工作流。在请休假表开启工作流时,表名后会带一个工作流的小图标。操作步骤如下: 1. 新建状态,整个工作流包含了起草,人资部核准,部门领导审批,人资部结算以及结束5个状态。其中起草,人资部核准,人资部结算,结束均为普通流程,部门领导审批为审批流程。

2. 如流程图所示,起草环节根据创建者的部门区分流转环节,若创建者部门=活字格开发部,提交人资部核准,担当者为人力资源部经理,,若创建者部门=活字格业务部,提交部门领导审批。此时就需要在条件中添加不同的分支。条件中选择创建者的扩展属性部门。 3. 当创建者属于活字格开发部时,提交给人资部核准,人资部正常提交给部门领导审批。普通流程后跟审批流程,不用设置担当者。

JIRA工作流详解文档

JIRA 工作流介绍 "工作流"是一个问题经过其生命周期的若干步骤和阶段的变迁。工作流通常代表着实际的业务处理流程。JIRA通过一定数量的状态对问题进行处理来模拟问题的生命周期。 每个状态代表了生命周期的一个阶段,通常由一个有意义的名字来表示。本文将介绍如何创建或定制一个工作流,同时对相应的概念进行解释。如果你已经定义了一个工作流,请参见激活工作流一节来进行使用。 JIRA所有的版本都带有一个默认的工作流。需要说明的是JIRA标准版中只有一个默认工作流,且不能被编辑,但是在JIRA企业版和专业版中,您可以增加新的工作流来定制问题的生命周期。 ?JIRA企业版中,支持多个活动的工作流。每一个工作流可以与某一个特定项目和特定的问题类型相关联。这样就可以实现不同的项目(或者一个项目中的不同问题类型)采用不同的工作流。 ?JIRA专业版仅支持一个活动的工作流。即在JIRA专业版中用户可以定义任意多个工作流,但在某一时刻,系统中的所有问题只能使用其中的一个工作流。 注意:处于非激活状态的工作流才能够被编辑。如果您想修改一个处于激活状态的工作流的话,可以拷贝这个工作流(参考下面介绍 的’Createing a Workflow’),然后修改这个拷贝的工作流,修改之后再激活使用它。 关于工作流步骤和变迁 一个工作流是由步骤和变迁组成。 ?一个工作流的步骤标识工作流中一个问题的一个阶段或者说是’状态’。在某一时刻一个问题只能存在于一个在步骤中。每个工作流步骤都相应有一个"连接的" 状态。每个工作流必须有至少一个步骤,该步骤叫做初始步骤。当一个问题被移动到某一步骤时,它的的’状态’被更新为该步骤"连接的"状态。 第 1 页共 23 页

epoll IO多路复用技术

一、编目1 1. Epoll 是何方神圣? Epoll 可是当前在Linux 下开发大规模并发网络程序的热门人选,Epoll 在Linux2.6 内核中正式引入,和select 相似,其实都I/O 多路复用技术而已,并没有什么神秘的。 其实在Linux 下设计并发网络程序,向来不缺少方法,比如典型的Apache 模型(Process Per Connection ,简称PPC ),TPC (Thread Per Connection )模型,以及select 模型和poll 模型,那为何还要再引入Epoll 这个东东呢?那还是有得说说的… 2. 常用模型的缺点 如果不摆出来其他模型的缺点,怎么能对比出Epoll 的优点呢。 2.1 PPC/TPC 模型 这两种模型思想类似,就是让每一个到来的连接一边自己做事去,别再来烦我。只是PPC 是为它开了一个进程,而TPC 开了一个线程。可是别烦我是有代价的,它要时间和空间啊,连接多了之后,那么多的进程/ 线程切换,这开销就上来了;因此这类模型能接受的最大连接数都不会高,一般在几百个左右。 2.2 select 模型 1. 最大并发数限制,因为一个进程所打开的FD (文件描述符)是有限制的,由 FD_SETSIZE 设置,默认值是1024/2048 ,因此Select 模型的最大并发数就被相应限制了。自己改改这个FD_SETSIZE ?想法虽好,可是先看看下面吧… 2. 效率问题,select 每次调用都会线性扫描全部的FD 集合,这样效率就会呈现线性下降,把FD_SETSIZE 改大的后果就是,大家都慢慢来,什么?都超时了??!! 3. 内核/ 用户空间内存拷贝问题,如何让内核把FD 消息通知给用户空间呢?在这个问题上select 采取了内存拷贝方法。 2.3 poll 模型 基本上效率和select 是相同的,select 缺点的 2 和 3 它都没有改掉。 3. Epoll 的提升 把其他模型逐个批判了一下,再来看看Epoll 的改进之处吧,其实把select 的缺点反过来那就是Epoll 的优点了。 3.1. Epoll 没有最大并发连接的限制,上限是最大可以打开文件的数目,这个数字一般远大于2048, 一般来说这个数目和系统内存关系很大,具体数目可以cat /proc/sys/fs/file-max 察看。 3.2. 效率提升,Epoll 最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll 的效率就会远远高于select 和poll 。 3.3. 内存拷贝,Epoll 在这点上使用了“共享内存”,这个内存拷贝也省略了。

JAVA经典面试题:Java内存模型

JAVA经典面试题:Java内存模型 因为Java内存模型不仅是java重点要学习的技术知识,还是面试的时候经典面试题,希望引起同学们的重视,今天千锋小编就来分享一下java内存模型的相关技术知识。 不同的渠道,内存模型是不一样的,但是jvm的内存模型标准是一致的。其实java的多线程并发问题都会反映在java的内存模型上,所谓线程安全无非是要操控多个线程对某个资源的有序拜访或修改。总结java的内存模型,要解决两个首要的问题:可见性和有序性。 可见性:多个线程之间是不能相互传递数据通信的,它们之间的交流只能经过同享变量来进行。Java内存模型(JMM)规定了jvm有主内存,主内存是多个线程同享的。当new一个目标的时分,也是被分配在主内存中,每个线程都有自己的作业内存,作业内存存储了主存的某些目标的副本,当然线程的作业内存大小是有限制的。当线程操作某个目标时,履行次序如下: (1) 从主存仿制变量到当前作业内存(read and load) (2) 履行代码,改动同享变量值(use and assign) (3) 用作业内存数据改写主存相关内容(store and write)

当一个同享变量在多个线程的作业内存中都有副本时,如果一个线程修改了这个同享变量,那么其他线程应该可以看到这个被修改后的值,这就是多线程的可见性问题。 有序性:线程在引证变量时不能直接从主内存中引证,如果线程作业内存中没有该变量,则会从主内存中复制一个副本到作业内存中,完成后线程会引证该副本。当同一线程再度引证该字段时,有可能从头从主存中获取变量副本(read-load-use),也有可能直接引证本来的副本(use),也就是说read,load,use次序可以由JVM完成体系决议。 线程不能直接为主存中字段赋值,它会将值指定给作业内存中的变量副本(assign),完成后这个变量副本会同步到主存储区(store- write),至于何时同步往昔,依据JVM完成体系决议。有该字段,则会从主内存中将该字段赋值到作业内存中,这个进程为read-load,完成后线程会引证该变量副本。 知识就财富,这句话再IT行业显示的尤其现实残酷,懂就是懂,不懂就是不懂,所以各位同学,你的努力与否与你财富直接挂钩。一起加油吧!更多java 技术经典面试题欢迎关注千锋小编。

Activiti6.0工作流使用说明文档V1.0

Activiti工作流使用说明文档 V1.0 2019年8月9日

目录 一、模型设计器 (4) 1.1任务 (5) 1.1.1 用户任务 (5) 1.1.2 服务任务 (5) 1.1.3 脚本任务 (6) 1.1.4 业务规则任务 (7) 1.1.5 接收任务 (7) 1.1.6 手动任务 (7) 1.1.7 邮件任务 (8) 1.1.8 Camel任务 (8) 1.1.9 Mule任务 (8) 1.1.10 决策任务 (9) 1.2构造 (10) 1.2.1 子流程 (10) 1.2.2 事件子流程 (11) 1.3泳道列表 (11) 1.4分支 (12) 1.4.1 排他分支 (12) 1.4.2 并行分支 (12) 1.4.3 包容分支 (13) 1.4.4 事件分支 (13) 1.5事件 (14) 1.5.1 定时器事件 (14) 1.5.2 信号事件 (14) 1.5.3 消息事件 (16) 1.5.4 错误事件 (16) 1.5.5 边界事件 (17) 1.5.6 中间事件 (18)

1.5.7 开始事件 (18) 1.5.8 结束事件 (18) 1.6属性元素 (18) 1.6.1 异步 (18) 1.6.2 排他 (19) 1.6.3 补偿 (19) 1.6.4 中断任务 (20) 1.6.5 分配 (21) 1.6.6 到期时间 (22) 1.6.7 表单 (22) 1.6.8 表达式 (24) 1.6.9 多实例 (25) 1.7监听器配置 (28) 1.7.1 执行监听器 (29) 1.7.2 任务监听器 (30) 1.7.3 事件监听器 (32) 1.7.4 全局事件监听器 (33) 二、workflow-service (34) 2.1数据更新逻辑 (34) 2.2方法表达式配置 (35) 2.3自定义外置表单 (36) 2.4事件监听器配置 (37) 2.5方法调用说明 (37) 三、常见问题 (37)

EPOLL的ET和LT模式

EPOLL的ET和LT模式 EPOLL的ET和LT模式 近日又继续学习了一下EPOLL的工作模式,这会基本上搞清楚了,因而撰写了此篇文档进行描述。先来一段网上的介绍文档: EPOLL事件分发系统可以运转在两种模式下:Edge Triggered (ET)、Level Triggered (LT)。LT是缺省的工作方式,并且同时支持block和no-block socket;在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll 都是这种模型的代表。 ET是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知。 后面才是我想说的内容,既然ET模式是高速模式,那我们进行服务器开发是一定要使用的了,可是查遍文档,也没有找到ET模式的设置方法,到底如何设置和使用呢?通过反复测试,终于搞明白“EPOLLET”就是ET模式的设置了,也许是我太笨所以才迷惑这么久了,以下就是将TCP套接字hSocket和epoll关联起来的代码: struct epoll_event struEvent; struEvent.events = EPOLLIN | EPOLLOUT | EPOLLET; struEvent.data.fd = hSocket; epoll_ctl(m_hEpoll, EPOLL_CTL_ADD, hSocket, &struEvent); 如果将监听套接字m_hListenSocket和epoll关联起来,则代码如下: struct epoll_event struEvent; struEvent.events = EPOLLIN | EPOLLET; struEvent.data.fd = m_hListenSocket; epoll_ctl(m_hEpoll, EPOLL_CTL_ADD, m_hListenSocket, &struEvent); 如果想使用LT模式,直接把事件的赋值修改为以下即可,也许这就是缺省的意义吧。struEvent.events = EPOLLIN | EPOLLOUT; //用户TCP套接字 struEvent.events = EPOLLIN; //监听TCP套接字 不过,通过我的测试确定,这两种模式的性能差距还是非常大的,最大可以达到10倍。100个连接的压力测试,其他环境都相同,LT模式CPU消耗99%、ET模式15%。

java内存模型

12.Java内存模型 (原本准备把内存模型单独放到某一篇文章的某个章节里面讲解,后来查阅了国外很多文档才发现其实JVM内存模型的内容还蛮多的,所以直接作为一个章节的基础知识来讲解,可能该章节概念的东西比较多。一个开发Java的开发者,一旦了解了JVM内存模型就能够更加深入地了解该语言的语言特性,可能这个章节更多的是概念,没有太多代码实例,所以希望读者谅解,有什么笔误来Email告知:silentbalanceyh@https://www.wendangku.net/doc/8812564468.html,,本文尽量涵盖所有Java语言可以碰到的和内存相关的内容,同样也会提到一些和内存相关的计算机语言的一些知识,为草案。因为平时开发的时候没有特殊情况不会进行内存管理,所以有可能有笔误的地方比较多,我用的是Windows平台,所以本文涉及到的与操作系统相关的只是仅仅局限于Windows平台。不仅仅如此,这一个章节牵涉到的多线程和另外一些内容并没有讲到,这里主要是结合JVM内部特性把本章节作为核心的概念性章节来讲解,这样方便初学者深入以及彻底理解Java 语言) 本文章节: 1.JMM简介 2.堆和栈 3.本机内存 4.防止内存泄漏 1.JMM简介 i.内存模型概述 Java平台自动集成了线程以及多处理器技术,这种集成程度比Java以前诞生的计算机语言要厉害很多,该语言针对多种异构平台的平台独立性而使用的多线程技术支持也是具有开拓性的一面,有时候在开发Java同步和线程安全要求很严格的程序时,往往容易混淆的一个概念就是内存模型。究竟什么是内存模型?内存模型描述了程序中各个变量(实例域、静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和从内存中取出变量这样的底层细节,对象最终是存储在内存里面的,这点没有错,但是编译器、运行库、处理器或者系统缓存可以有特权在变量指定内存位置存储或者取出变量的值。【JMM】(Java Memory Model的缩写)允许编译器和缓存以数据在处理器特定的缓存(或寄存器)和主存之间移动的次序拥有重要的特权,除非程序员使用了final或synchronized明确请求了某些可见性的保证。 1)JSR133:

工作流表单手写控件使用说明

工作流表单手写控件使用说明Office Anywhere的工作流在表单的设计中,有多种控件可供选择和使用, 这些控件的使用,极大地扩展了工作流表单的应用范围,以面对更加丰富的用户需求。这一篇章主要介绍工作流表单手写控件。 一.手写控件的安装和印章的制作 安装步骤:首先安装Office Anywhere工作流表单手写控件,此控件的安装方法是:将文件名为“WebSign.dll”的文件放置于OA服务器的安装目录下,默认路径为D:\MYOA\webroot\module\websign下,请根据您的软件安装路径来确定。在使用包含印章控件的工作流时,如果有的客户端电脑不能自动下载并安装该控件,则使用“WebSign_Setup.exe”单独在该电脑上安装。 如果遇到有的客户曾经使用过试用版,在服务器替换过正式版文件后依旧有试用字样,请到客户端的“C:\WINDOWS\Downloaded Program Files”路径下,找到一个名为“Webobject Class”这个文件,在所有IE窗口关闭的情况下,删除该文件,并重新进入工作流下载控件,或直接使用正式用户包中所带的独立安装包进行安装。 印章的制作:制作印章前,先准备好印章的图片,必须是“.bmp”格式的图片,然后使用提供的印章制作工具“MakeSeal.exe”。双击打开制作工具,并选择等待作为印章的图片: 自己设定密码, 在加盖印章时 需要输入 背景消除后印 章显示为透明。

点击“写入”后即保存为一个“测试印章.sel”的印章文件。 二.工作流表单设计 工作流表单的设计见《工作流使用详解》,这个章节主要说明手写控件的设计。进入表单设计中,如下图: 填写控件的名称,然后确定并保存表单,如此控件就在表单中添加了。接下来需要设置流程,设定在具体的流程步骤中允许使用该控件。 进入工作流设计,然后找到需要使用手写控件的步骤,按照下图所示操作:

相关文档