• zookeeper服务端


      服务端启动时,就启动线程通过NIO监听网络端口。每个连接都会有一个上下文环境对象,当接收到请求后,会在上下文环境对象中进行处理。

    服务端启动线程,监听网络端口,(NIOServerCnxn.Factory):

      1 static public class Factory extends Thread {
      2         static {
      3             //设置全局的异常处理
      4             Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
      5                 public void uncaughtException(Thread t, Throwable e) {
      6                     LOG.error("Thread " + t + " died", e);
      7                 }
      8             });
      9             /**
     10              * jvm早期的nullpoint bug
     11              * http://bugs.sun.com/view_bug.do?bug_id=6427854
     12              */
     13             try {
     14                 Selector.open().close();
     15             } catch(IOException ie) {
     16                 LOG.error("Selector failed to open", ie);
     17             }
     18         }
     19         //服务器通道
     20         final ServerSocketChannel ss;
     21         //选择器
     22         final Selector selector = Selector.open();
     23         ZooKeeperServer zks;
     24         
     25         final ByteBuffer directBuffer = ByteBuffer.allocateDirect(64 * 1024);
     26         //所有的上下文环境
     27         final HashSet<NIOServerCnxn> cnxns = new HashSet<NIOServerCnxn>();
     28         //ip对应的上下文环境
     29         final HashMap<InetAddress, Set<NIOServerCnxn>> ipMap =
     30             new HashMap<InetAddress, Set<NIOServerCnxn>>( );
     31         int maxClientCnxns = 10;
     32         
     33         public Factory(InetSocketAddress addr, int maxcc) throws IOException {
     34             setDaemon(true);
     35             //单个client链接最大数
     36             maxClientCnxns = maxcc;
     37           //创建服务器通道
     38             this.ss = ServerSocketChannel.open();
     39             ss.socket().setReuseAddress(true);
     40           //绑定端口
     41             ss.socket().bind(addr);
     42           //设置通道为非阻塞通道
     43             ss.configureBlocking(false);
     44           //把通道注册到选择器中
     45             ss.register(selector, SelectionKey.OP_ACCEPT);
     46         }
     47         public void run() {
     48             while (!ss.socket().isClosed()) {
     49                 try {
     50                     //选择一组键
     51                     selector.select(1000);
     52                     Set<SelectionKey> selected;
     53                     synchronized (this) {
     54                         selected = selector.selectedKeys();
     55                     }
     56                     ArrayList<SelectionKey> selectedList = new ArrayList<SelectionKey>(
     57                             selected);
     58                     Collections.shuffle(selectedList);
     59                     for (SelectionKey k : selectedList) {
     60                         //如果通道已经准备好接收套接字
     61                         if ((k.readyOps() & SelectionKey.OP_ACCEPT) != 0) {
     62                             SocketChannel sc = ((ServerSocketChannel) k
     63                                     .channel()).accept();
     64                             InetAddress ia = sc.socket().getInetAddress();
     65                             //判断最大连接数
     66                             int cnxncount = getClientCnxnCount(ia);
     67                             if (maxClientCnxns > 0 && cnxncount >= maxClientCnxns){
     68                                 sc.close();
     69                             } else {
     70                                 // 配置为非阻塞
     71                                 sc.configureBlocking(false);
     72                               //把通道注册到选择器中
     73                                 SelectionKey sk = sc.register(selector,
     74                                         SelectionKey.OP_READ);
     75                                 NIOServerCnxn cnxn = createConnection(sc, sk);
     76                                 //给该通道附带一个上下文环境
     77                                 sk.attach(cnxn);
     78                                 addCnxn(cnxn);
     79                             }
     80                         } else if ((k.readyOps() & (SelectionKey.OP_READ | SelectionKey.OP_WRITE)) != 0) {
     81                             //通过上线文件来进行读写
     82                             NIOServerCnxn c = (NIOServerCnxn) k.attachment();
     83                             c.doIO(k);
     84                         } else {
     85                             if (LOG.isDebugEnabled()) {
     86                                 LOG.debug("Unexpected ops in select "
     87                                           + k.readyOps());
     88                             }
     89                         }
     90                     }
     91                     selected.clear();
     92                  catch (Exception e) {
     93                     LOG.warn("Ignoring exception", e);
     94                 }
     95             }
     96             clear();
     97         }
     98         //关闭
     99         public void shutdown() {
    100                 try {
    101                     ss.close();
    102                     clear();
    103                     this.interrupt();
    104                     this.join();
    105                 } catch (Exception e) {
    106                     LOG.warn("Ignoring unexpected exception during shutdown", e);
    107                 }
    108                 try {
    109                     selector.close();
    110                 } catch (IOException e) {
    111                     LOG.warn("Selector closing", e);
    112                 }
    113                 if (zks != null) {
    114                     zks.shutdown();
    115                 }
    116         }
    117         synchronized public void clear() {
    118             selector.wakeup();
    119             HashSet<NIOServerCnxn> cnxns;
    120             synchronized (this.cnxns) {
    121                 cnxns = (HashSet<NIOServerCnxn>)this.cnxns.clone();
    122             }
    123             // got to clear all the connections that we have in the selector
    124             for (NIOServerCnxn cnxn: cnxns) {
    125                 try {
    126                     // don't hold this.cnxns lock as deadlock may occur
    127                     cnxn.close();
    128                 } catch (Exception e) {
    129                     LOG.warn("Ignoring exception closing cnxn sessionid 0x"
    130                             + Long.toHexString(cnxn.sessionId), e);
    131                 }
    132             }
    133         }
    134     }
    View Code

      上下文环境主要通过NIO读取请求数据,首先会读取4个字节的请求数据,该数据分为两种情况,一种是真正的数据包长度,一种是命令。如果是真正的数据包长度会按长度读取数据报,进行处理。如果是命令,会根据命令进行处理。

    服务端通道上下文,通过NIO进行读写(NIOServerCnxn.doIO):

     1 void doIO(SelectionKey k) throws InterruptedException {
     2         try {
     3             //如果通道可以读取数据
     4             if (k.isReadable()) {
     5                 //读取数据到缓存中
     6                 int rc = sock.read(incomingBuffer);
     7                 //如果读满缓存
     8                 if (incomingBuffer.remaining() == 0) {
     9                     boolean isPayload;
    10                     //如果第一次读取,则先读取长度内容,相应分配缓存;否则读取指定长度的数据内容
    11                     if (incomingBuffer == lenBuffer) {
    12                         incomingBuffer.flip();
    13                         isPayload = readLength(k);
    14                         incomingBuffer.clear();
    15                     } else {
    16                         isPayload = true;
    17                     }
    18                     if (isPayload) {
    19                         //读取数据,初始化、读取请求数据封装成packet
    20                         readPayload();
    21                     }
    22                 }
    23             }
    24           //如果通道可以读写数据
    25             if (k.isWritable()) {
    26                 //缓存中有数据需要写入
    27                 if (outgoingBuffers.size() > 0) {
    28                     //创建bytebuffer
    29                     ByteBuffer directBuffer = factory.directBuffer;
    30                     directBuffer.clear();
    31                     //从队列中读取数据知道缓存读满
    32                     for (ByteBuffer b : outgoingBuffers) {
    33                         if (directBuffer.remaining() < b.remaining()) {
    34                             b = (ByteBuffer) b.slice().limit(
    35                                     directBuffer.remaining());
    36                         }
    37                         int p = b.position();
    38                         directBuffer.put(b);
    39                         b.position(p);
    40                         if (directBuffer.remaining() == 0) {
    41                             break;
    42                         }
    43                     }
    44                     //将数据写入通道
    45                     directBuffer.flip();
    46                     int sent = sock.write(directBuffer);
    47                     ByteBuffer bb;
    48                     //从缓存中已经发送的删除数据
    49                     while (outgoingBuffers.size() > 0) {
    50                         bb = outgoingBuffers.peek();
    51                         int left = bb.remaining() - sent;
    52                         if (left > 0) {
    53                             bb.position(bb.position() + sent);
    54                             break;
    55                         }
    56                         sent -= bb.remaining();
    57                         outgoingBuffers.remove();
    58                     }
    59                 }
    60 
    61                 synchronized(this.factory){
    62                     if (outgoingBuffers.size() == 0) {
    63                         sk.interestOps(sk.interestOps()
    64                                 & (~SelectionKey.OP_WRITE));
    65                     } else {
    66                         sk.interestOps(sk.interestOps()
    67                                 | SelectionKey.OP_WRITE);
    68                     }
    69                 }
    70             }
    71         }catch (IOException e) {
    72             close();
    73         }
    74     }
    75     private void readPayload() throws IOException, InterruptedException {
    76         if (incomingBuffer.remaining() != 0) { // have we read length bytes?
    77             int rc = sock.read(incomingBuffer); // sock is non-blocking, so ok
    78         }
    79         if (incomingBuffer.remaining() == 0) { 
    80             //重置缓存
    81             incomingBuffer.flip();
    82             //如果没有进行初始化,首先要初始化;如果已经链接,则读取请求数据,封装成packet
    83             if (!initialized) {
    84                 readConnectRequest();
    85             } else {
    86                 readRequest();
    87             }
    88           //重置
    89             lenBuffer.clear();
    90             incomingBuffer = lenBuffer;
    91         }
    92     }
    View Code

    服务端通道上下文,读取长度,如果是命令,另起线程处理命令操作(NIOServerCnxn.readLength)

     1 private boolean readLength(SelectionKey k) throws IOException {
     2         //如果是请求数据,根据长度分配缓存;如果是命令,执行相应命令。
     3         int len = lenBuffer.getInt();
     4         if (!initialized && checkFourLetterWord(k, len)) {
     5             return false;
     6         }
     7         if (len < 0 || len > BinaryInputArchive.maxBuffer) {
     8             throw new IOException("Len error " + len);
     9         }
    10         if (zk == null) {
    11             throw new IOException("ZooKeeperServer not running");
    12         }
    13         incomingBuffer = ByteBuffer.allocate(len);
    14         return true;
    15     }
    16     private boolean checkFourLetterWord(final SelectionKey k, final int len)
    17             throws IOException
    18             {
    19                 //获取命令
    20                 String cmd = cmd2String.get(len);
    21                 /** cancel the selection key to remove the socket handling
    22                  * from selector. This is to prevent netcat problem wherein
    23                  * netcat immediately closes the sending side after sending the
    24                  * commands and still keeps the receiving channel open. 
    25                  * The idea is to remove the selectionkey from the selector
    26                  * so that the selector does not notice the closed read on the
    27                  * socket channel and keep the socket alive to write the data to
    28                  * and makes sure to close the socket after its done writing the data
    29                  */
    30                 if (k != null) {
    31                     try {
    32                         k.cancel();
    33                     } catch(Exception e) {
    34                         LOG.error("Error cancelling command selection key ", e);
    35                     }
    36                 }
    37                 //根据命令类型,执行相应内容
    38                 final PrintWriter pwriter = new PrintWriter(
    39                         new BufferedWriter(new SendBufferWriter()));
    40                 if (len == ruokCmd) {
    41                     RuokCommand ruok = new RuokCommand(pwriter);
    42                     ruok.start();
    43                     return true;
    44                 } else if (len == getTraceMaskCmd) {
    45                     TraceMaskCommand tmask = new TraceMaskCommand(pwriter);
    46                     tmask.start();
    47                     return true;
    48                 } else if (len == setTraceMaskCmd) {
    49                     int rc = sock.read(incomingBuffer);
    50                     if (rc < 0) {
    51                         throw new IOException("Read error");
    52                     }
    53 
    54                     incomingBuffer.flip();
    55                     long traceMask = incomingBuffer.getLong();
    56                     ZooTrace.setTextTraceLevel(traceMask);
    57                     SetTraceMaskCommand setMask = new SetTraceMaskCommand(pwriter, traceMask);
    58                     setMask.start();
    59                     return true;
    60                 } else if (len == enviCmd) {
    61                     EnvCommand env = new EnvCommand(pwriter);
    62                     env.start();
    63                     return true;
    64                 } else if (len == confCmd) {
    65                     ConfCommand ccmd = new ConfCommand(pwriter);
    66                     ccmd.start();
    67                     return true;
    68                 } else if (len == srstCmd) {
    69                     StatResetCommand strst = new StatResetCommand(pwriter);
    70                     strst.start();
    71                     return true;
    72                 } else if (len == crstCmd) {
    73                     CnxnStatResetCommand crst = new CnxnStatResetCommand(pwriter);
    74                     crst.start();
    75                     return true;
    76                 } else if (len == dumpCmd) {
    77                     DumpCommand dump = new DumpCommand(pwriter);
    78                     dump.start();
    79                     return true;
    80                 } else if (len == statCmd || len == srvrCmd) {
    81                     StatCommand stat = new StatCommand(pwriter, len);
    82                     stat.start();
    83                     return true;
    84                 } else if (len == consCmd) {
    85                     ConsCommand cons = new ConsCommand(pwriter);
    86                     cons.start();
    87                     return true;
    88                 } else if (len == wchpCmd || len == wchcCmd || len == wchsCmd) {
    89                     WatchCommand wcmd = new WatchCommand(pwriter, len);
    90                     wcmd.start();
    91                     return true;
    92                 }
    93                 return false;
    94             }
    View Code

    服务端通道上下文,如果是请求数据,处理请求数据(NIOServerCnxn.readRequestNIOServerCnxn.readConnectRequest):

     1 private void readRequest() throws IOException {
     2         //反序列化请求数据
     3         InputStream bais = new ByteBufferInputStream(incomingBuffer);
     4         BinaryInputArchive bia = BinaryInputArchive.getArchive(bais);
     5         RequestHeader h = new RequestHeader();
     6         h.deserialize(bia, "header");
     7         incomingBuffer = incomingBuffer.slice();
     8         if (h.getType() == OpCode.auth) {
     9             //如果是认证请求
    10             AuthPacket authPacket = new AuthPacket();
    11             ZooKeeperServer.byteBuffer2Record(incomingBuffer, authPacket);
    12             String scheme = authPacket.getScheme();
    13             //进行认证
    14             AuthenticationProvider ap = ProviderRegistry.getProvider(scheme);
    15             if (ap == null
    16                     || (ap.handleAuthentication(this, authPacket.getAuth())
    17                             != KeeperException.Code.OK)) {
    18                 // 认证失败,返回失败内容
    19                 ReplyHeader rh = new ReplyHeader(h.getXid(), 0,
    20                         KeeperException.Code.AUTHFAILED.intValue());
    21                 sendResponse(rh, null, null);
    22                 //关闭链接
    23                 sendCloseSession();
    24                 disableRecv();
    25             } else {
    26                 //认证成功,返回成功内容
    27                 ReplyHeader rh = new ReplyHeader(h.getXid(), 0,
    28                         KeeperException.Code.OK.intValue());
    29                 sendResponse(rh, null, null);
    30             }
    31             return;
    32         } else {
    33             //如果是请求,提交到zk处理
    34             Request si = new Request(this, sessionId, h.getXid(), h.getType(), incomingBuffer, authInfo);
    35             si.setOwner(ServerCnxn.me);
    36             zk.submitRequest(si);
    37         }
    38     }
    39     
    40     private void readConnectRequest() throws IOException, InterruptedException {
    41         //反序列化链接请求对象
    42         BinaryInputArchive bia = BinaryInputArchive
    43                 .getArchive(new ByteBufferInputStream(incomingBuffer));
    44         ConnectRequest connReq = new ConnectRequest();
    45         connReq.deserialize(bia, "connect");
    46         if (connReq.getLastZxidSeen() > zk.getZKDatabase().getDataTreeLastProcessedZxid()) {
    47             throw new CloseRequestException(msg);
    48         }
    49         sessionTimeout = connReq.getTimeOut();
    50         byte passwd[] = connReq.getPasswd();
    51         //初始化session
    52         disableRecv();
    53         if (connReq.getSessionId() != 0) {
    54             long clientSessionId = connReq.getSessionId();
    55             factory.closeSessionWithoutWakeup(clientSessionId);
    56             setSessionId(clientSessionId);
    57             zk.reopenSession(this, sessionId, passwd, sessionTimeout);
    58         } else {
    59             zk.createSession(this, passwd, sessionTimeout);
    60         }
    61         initialized = true;
    62     }
    View Code

    服务端通道上下文,写返回数据(NIOServerCnxn.sendResponse)

     1 synchronized public void sendResponse(ReplyHeader h, Record r, String tag) {
     2         try {
     3             //序列化返回结果,
     4             ByteArrayOutputStream baos = new ByteArrayOutputStream();
     5             BinaryOutputArchive bos = BinaryOutputArchive.getArchive(baos);
     6             try {
     7                 baos.write(fourBytes);
     8                 bos.writeRecord(h, "header");
     9                 if (r != null) {
    10                     bos.writeRecord(r, tag);
    11                 }
    12                 baos.close();
    13             } catch (IOException e) {
    14                 LOG.error("Error serializing response");
    15             }
    16             //写入数据长度
    17             byte b[] = baos.toByteArray();
    18             ByteBuffer bb = ByteBuffer.wrap(b);
    19             bb.putInt(b.length - 4).rewind();
    20             //
    21             sendBuffer(bb);
    22          } catch(Exception e) {
    23             LOG.warn("Unexpected exception. Destruction averted.", e);
    24          }
    25     }
    26     void sendBuffer(ByteBuffer bb) {
    27         try {
    28             if (bb != closeConn) {
    29                 //直接发送数据
    30                 if ((sk.interestOps() & SelectionKey.OP_WRITE) == 0) {
    31                     try {
    32                         sock.write(bb);
    33                     } catch (IOException e) {
    34                         // we are just doing best effort right now
    35                     }
    36                 }
    37                 if (bb.remaining() == 0) {
    38                     packetSent();
    39                     return;
    40                 }
    41             }
    42             //写入缓存中。
    43             synchronized(this.factory){
    44                 sk.selector().wakeup();
    45                 outgoingBuffers.add(bb);
    46                 if (sk.isValid()) {
    47                     sk.interestOps(sk.interestOps() | SelectionKey.OP_WRITE);
    48                 }
    49             }
    50             
    51         } catch(Exception e) {
    52             LOG.error("Unexpected Exception: ", e);
    53         }
    54     }
    View Code

     

  • 相关阅读:
    滑动窗口法与剑指offer:和为S的连续正数数列 与 和为S的两个数字
    数组中的逆序对与归并中的分治思想
    重读STL源码剖析:迭代器
    重读深度探索C++对象模型:函数
    FreeMarker笔记 前言&第1章 入门
    分享我的PL/SQL的优化设置,为开发全面提速
    迅影QQ视频查看v2.0 源码
    Invalid encoding name "UTF8". 报错 XML
    [HNOI2003]消防局的设立
    We need water!
  • 原文地址:https://www.cnblogs.com/zhangwanhua/p/9182544.html
Copyright © 2020-2023  润新知