ZeroMQ(java)Socket之Dealer
来源:互联网 发布:乐创圣衣神话淘宝 编辑:程序博客网 时间:2024/04/29 11:28
好像也有一段时间没有写博客了。。。
ZeroMQ到现在,其实基本的原理也都了解的还算差不多了,剩下的就是一些细节的了。。。
我们知道ZeroMQ中定义了很多类型的Socket,其实这个里面最为简单的就是Dealer类型的socket看了,它基本没有做太多额外的处理。。。
在看具体的Dealer之前,先来看看两个类型,其实是两个工具类,用于维护在pipe上面执行的读写操作。。。
首先是FQ类型,它用于维护Socket与Session之间的pipe,socket在pipe上的读操作:
//用于维护pipe上面的读取操作public class FQ { // Inbound pipes. private final List<Pipe> pipes; //所有绑定的pipe private int active; //激活的pipe的数量,所有激活的pipe都放在最前面 private int current; //下一个可以读取的pipe的下标 private boolean more; //如果是ture的话,表示只是接受了一部分,还有别的需要接受 //构造函数 public FQ () { active = 0; current = 0; more = false; pipes = new ArrayList<Pipe>(); //用于保存所有的关联的pipe } //这里其实很简单,直接保存pipe就好了,并且好更新pipe的数量,这里加进来的pipe一开始是需要放到活动的pipe部分的 public void attach (Pipe pipe_) { pipes.add (pipe_); Utils.swap (pipes, active, pipes.size () - 1); active++; } //终止一个pipe,这里还是比较的简单吧,直接去除就好了,不过这里需要更新一些标志位 public void terminated (Pipe pipe_) { final int index = pipes.indexOf (pipe_); //当前pipe居然还在活动的部分中,需要现将其放到活动的pipe的最后去, if (index < active) { active--; Utils.swap (pipes, index, active); if (current == active) current = 0; //更新current下标 } pipes.remove (pipe_); //移除 } //激活一个pipe,其实这里是将当前的pipe放到活动的pipe的那里去,这里active数量还要加1 public void activated (Pipe pipe_) { // Move the pipe to the list of active pipes. Utils.swap(pipes, pipes.indexOf (pipe_), active); active++; } //这里从pipe里面读取数据 public Msg recv(ValueReference<Integer> errno) { return recvpipe(errno, null); } public Msg recvpipe(ValueReference<Integer> errno, ValueReference<Pipe> pipe_) { // Round-robin over the pipes to get the next message. while (active > 0) { //有活动的pipe // Try to fetch new message. If we've already read part of the message // subsequent part should be immediately available. Msg msg_ = pipes.get(current).read(); //从当前的current下标读取 boolean fetched = msg_ != null; //判断是否有msg读取出来 // Note that when message is not fetched, current pipe is deactivated // and replaced by another active pipe. Thus we don't have to increase // the 'current' pointer. if (fetched) { if (pipe_ != null) { pipe_.set(pipes.get(current)); } more = msg_.has_more(); //判断当前的msg是否还有急需要读的 if (!more) { //如果没有more了,那么需要更新current下标 current = (current + 1) % active; //更新current下标 } return msg_; //返回msg } //到这里了,说明没有读取到数据 // Check the atomicity of the message. // If we've already received the first part of the message // we should get the remaining parts without blocking. assert (!more); active--; //表示当前的活动的pipe数量已经少了一个, Utils.swap (pipes, current, active); //这里其实是将刚刚读取的pipe从活动部分去除 if (current == active) //有可能需要更新current下标 current = 0; } // No message is available. Initialise the output parameter // to be a 0-byte message. errno.set(ZError.EAGAIN); //到这里了,说明确实没得数据可以读取了 return null; } //用于判断当前的底层的pipe是否有可以读取的数据 public boolean has_in () { // There are subsequent parts of the partly-read message available. if (more) //如果有标志位的话,那么肯定可以了 return true; // Note that messing with current doesn't break the fairness of fair // queueing algorithm. If there are no messages available current will // get back to its original value. Otherwise it'll point to the first // pipe holding messages, skipping only pipes with no messages available. //在当前的活动pipe部分去找,直到找到有数据可以读取的 while (active > 0) { if (pipes.get(current).check_read ()) return true; // Deactivate the pipe. active--; //表示当前的这个没有数据可以读取,那么需要将其从活动的pipe部分移除 Utils.swap (pipes, current, active); if (current == active) //如果已经到了尾部,那么讲current下标设置为最开始 current = 0; } return false; }}
还蛮简单的吧,基本上注释都已经说的比较清楚了。。。首先有一个list,用于维护当前所有的pipe,然后有一个active下标,然后还有一个current下标:
这里在Active前面的pipe就是当前活动的pipe,也就是可能有数据可以读取的pipe,current就是下一个要读取的pipe的下标。。。应该够简单的吧,这里还通过current的移动,实现在pipe上面数据的循环读取。。。防止只在某一个pipe上读取数据。。。、
然后还有另外一个工具类,LB,它与LQ相反,它是用于维护写操作的,不知道为啥取这两个奇怪的名字,囧:
//用于维护pipe上的写操作public class LB { // List of outbound pipes. private final List<Pipe> pipes; //用于保存所有关联的pipe private int active; //当前所有的活动pipe的下标,前面的都是活动的 private int current; //current下标 private boolean more; //是否还有数据要写 private boolean dropping; //如果是true的话,那么丢弃当前的数据 public LB() { active = 0; current = 0; more = false; dropping = false; pipes = new ArrayList<Pipe>(); //创建arraylist } public void attach (Pipe pipe_) { pipes.add (pipe_); //将当前的pipe放到列表 activated (pipe_); //将其移动到活动的pipe部分,也就是active前面 } //终止一个pipe public void terminated(Pipe pipe_) { int index = pipes.indexOf (pipe_); //获取这个pipe的下标 // If we are in the middle of multipart message and current pipe // have disconnected, we have to drop the remainder of the message. if (index == current && more) //如果就是当前的current,而且还有数据,那么将dropping标志位设置为true dropping = true; // Remove the pipe from the list; adjust number of active pipes // accordingly. if (index < active) { //如果在active部分,那么这里还要更新一下 active--; Utils.swap (pipes, index, active); if (current == active) current = 0; } pipes.remove (pipe_); } //将这个pipe移动到active部分,而且增加active计数 public void activated(Pipe pipe_) { // Move the pipe to the list of active pipes. Utils.swap (pipes, pipes.indexOf (pipe_), active); active++; } //用于向pipe写数据 public boolean send(Msg msg_, ValueReference<Integer> errno) { // Drop the message if required. If we are at the end of the message // switch back to non-dropping mode. if (dropping) { //如果有dropping标志位的话,那么直接丢弃就好了 more = msg_.has_more(); dropping = more; msg_.close(); return true; } while (active > 0) { //如果有底层的pipe可以写 if (pipes.get(current).write (msg_)) //想当前的current的pipe写 break; //写进去了,那么直接break就好了 assert (!more); active--; //这里表示这个pipe已经满了,那么需要将其从active部分移除 if (current < active) Utils.swap (pipes, current, active); else current = 0; } // If there are no pipes we cannot send the message. if (active == 0) { //实在没有pipe可以写了 errno.set(ZError.EAGAIN); return false; } // If it's final part of the message we can fluch it downstream and // continue round-robinning (load balance). more = msg_.has_more(); //判断当前数据是否还有接下来要写的 if (!more) {//如果没有的话,那么flush,而且要更新current pipes.get(current).flush (); if (active > 1) current = (current + 1) % active; } return true; } //用于看底层的pipe有没有哪一个可以写 public boolean has_out() { // If one part of the message was already written we can definitely // write the rest of the message. if (more) return true; //遍历,直到找到可以写的pipe,这里还要将无法写的pipe从队列里面移除以及更新current下标 while (active > 0) { // Check whether a pipe has room for another message. if (pipes.get(current).check_write ()) return true; // Deactivate the pipe. active--; Utils.swap (pipes, current, active); if (current == active) current = 0; } return false; }}
其实实现跟FQ基本一样,也就是这个是写操作。。。。。
好了,接下来来看看具体的Dealer类型的实现吧:
//这个应该最简单的socket类型了public class Dealer extends SocketBase { public static class DealerSession extends SessionBase { //这里其实对Session也没有扩展,就是直接用SessionBase public DealerSession(IOThread io_thread_, boolean connect_, SocketBase socket_, final Options options_, final Address addr_) { super(io_thread_, connect_, socket_, options_, addr_); } } // Messages are fair-queued from inbound pipes. And load-balanced to // the outbound pipes. private final FQ fq; //用于维护读取操作 private final LB lb; //用于维护写操作 // Have we prefetched a message. private boolean prefetched; //是否有预读取的msg private Msg prefetched_msg; //指向预读取的数据 //构造函数,第一个参数是其所属的CTX,第二个参数是tid,这里其实并没有生存在某个IO线程中,所以这个ID并没有与任何IO线程关联,因为生存在用户线程 public Dealer(Ctx parent_, int tid_, int sid_) { super(parent_, tid_, sid_); prefetched = false; options.type = ZMQ.ZMQ_DEALER; //当前socket的类型 fq = new FQ(); lb = new LB(); // TODO: Uncomment the following line when DEALER will become true DEALER // rather than generic dealer socket. // If the socket is closing we can drop all the outbound requests. There'll // be noone to receive the replies anyway. // options.delay_on_close = false; options.recv_identity = true; } @Override protected void xattach_pipe(Pipe pipe_, boolean icanhasall_) { assert (pipe_ != null); fq.attach (pipe_); //放到维护读取的 lb.attach (pipe_); //放到维护写的 } @Override protected boolean xsend(Msg msg_) { return lb.send(msg_, errno); //直接调用lb来发送 msg,这里会遍历的底层活动的pipe,直接直到将msg写到pipe为止 } @Override protected Msg xrecv() { return xxrecv(); } private Msg xxrecv() { Msg msg_ = null; // If there is a prefetched message, return it. if (prefetched) { //如果有预接收的msg,那么直接返回它就行了 msg_ = prefetched_msg; prefetched = false; prefetched_msg = null; return msg_; } // DEALER socket doesn't use identities. We can safely drop it and while (true) { //这里不断的从底层的pipe里面读取数据 msg_ = fq.recv(errno); //调用fq来接收,其实是从底层的活动的pipe里面去接收数据,它会遍历底层所有活动的pipe,直到接收到数据为止 if (msg_ == null) //确实没有数据 return null; if ((msg_.flags() & Msg.identity) == 0) //dealer发送的msg没有标志位 break; } return msg_; } @Override //用于判断底层的pipe是否有可以读取的msg protected boolean xhas_in () { // We may already have a message pre-fetched. if (prefetched) //已经有预接收的数据了 return true; // Try to read the next message to the pre-fetch buffer. prefetched_msg = xxrecv(); //这里接收到预接收 if (prefetched_msg == null) return false; prefetched = true; return true; } @Override //检查是否有pipe可以写数据 protected boolean xhas_out () { return lb.has_out (); } @Override //当底层的pipe有数据可以读取的时候,这个其实将其放到的pipe列表的活动pipe部分,这个方法会在socketBase里面调用,当pipe接收到session传上来的数据的时候 protected void xread_activated (Pipe pipe_) { fq.activated (pipe_); } @Override //将这个pipe放到活动部分,也就是可以向这个pipe里面写数据了 protected void xwrite_activated (Pipe pipe_) { lb.activated (pipe_); } @Override //用于终止一个关联的pipe protected void xterminated(Pipe pipe_) { fq.terminated (pipe_); lb.terminated (pipe_); }}
好吧,dealer的实现真的是太简单了,它继承自SocketBase,也就扩展了几个读写操作,其实具体的还是上面的两个工具类型对象完成的。。。
这里好像直接跳过SocketBase的实现直接来看具体的Socket类型的实现有点不太好,。。。不过其实也都蛮简单的。。。就不细说了。。。
这里有一点需要提醒的就是,在一般情况下对象都会关联一个IO线程,用于执行命令啥的,不过Socket并没有关联某个具体的IO线程,当然不是说它不需要执行命令,而是它直接依赖用户线程。。。
- ZeroMQ(java)Socket之Dealer
- ZeroMQ(java)之router/dealer
- ZeroMQ(java)之Router/Dealer模式
- ZeroMQ(java)之Router/Dealer模式
- ZeroMQ(java)之Router/Dealer模式
- ZeroMQ(java)之Router/Dealer模式
- ZeroMQ(java)之Router与Dealer运行原理
- ZeroMQ(java)之Router与Dealer运行原理
- ZeroMQ(java)之Router与Dealer运行原理
- ZeroMQ(java)之Router与Dealer运行原理
- ZeroMQ(java)Socket之Req
- ZeroMQ(java)中监控Socket
- ZeroMQ(java)中监控Socket
- ZeroMQ(java)中监控Socket
- zeromq源代码分析6-3------ROUTER和DEALER
- 005 ZeroMQ REQ-<ROUTER-Dealer>-REP代理应答与请求
- ZeroMQ(java)之负载均衡
- ZeroMQ(java)之负载均衡
- 网站流量的高低究竟与alexa排名有什么关系呢?
- 我在南大的七年
- rescue grub解决方案
- centos6.5下hadoop2.2.0的8节点配置兼动态添加节点测试
- PuTTY 中文教程
- ZeroMQ(java)Socket之Dealer
- maven学习笔记
- 【php】下载文件
- 基于 格子Boltzman方法的poiseuille流模拟
- SSH框架的用户登录小实例
- Android自定义View的实现方法,带你一步步深入了解View(四)
- Ruby on Rails总结(五)
- oled屏选型资料2
- NYOJ 96 n-1位数