漫游ZooKeeper nio通信过程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tt50335971/article/details/79163372

0.引子

最近在看分布式并发工具menagerie,借助ZooKeeper,实现常用的collection、lock。
menagerie 含金量高,可以作为工具储备,也可以借鉴开发技巧。

1.ZooKeeper通信

  • 创建ZooKeeper对象,构造过程中会创建ClientCnxn对象,用于命令的发送、接收
  • ClientCnxn 核心包含两个工作线程:sendThread 和 eventThread
  • ZooKeeper对象构造完成,会内部调用ClientCnxn.start,也就是启动两个核心线程
  • ClientCnxn 负责服务器通信,关键方法submitRequest,sendThread 完成nio的过程
  • 所有的通信以Packet queue的形式体现

2.ClientCnxn.submitRequest分析

上源码:

// 提交操作命令request,结果回填response
public ReplyHeader submitRequest(..., Record request, Record response, ...) {
    ReplyHeader r = new ReplyHeader();
    // Packet 入队列,唤醒select()
    Packet packet = queuePacket(h, r, request, response, null, null, null, watchRegistration);
    // 典型的线程同步操作
    synchronized (packet) {
        while (!packet.finished) {
            packet.wait();
        }
    }
    return r;
}

类似于线程池的设计,任务的提交和处理分开实现,或者称之为生产者消费者的调度。具体的消费、通信实现,参考SendThread 的run方法。

3.SendThread 分析

  • 初始条件,sockKey 是null,触发startConnect(),典型的nio初始化操作
// 初始化SocketChannel
SocketChannel sock = SocketChannel.open();
sock.configureBlocking(false);
sock.socket().setSoLinger(false, -1);
sock.socket().setTcpNoDelay(true);
// 注册至selector
sockKey = sock.register(selector, SelectionKey.OP_CONNECT);
// 发起连接操作
conReq.serialize(boa, "connect");
  • doIO方法 完成服务器读写的调度
  • readResponse方法 完成协议解析,回填response
  • finally, packet.notify,至此,通信完成
// 收尾,线程同步完成
private void finishPacket(Packet p) {
    if (p.watchRegistration != null) {
        p.watchRegistration.register(p.replyHeader.getErr());
    }
    // 如果没有callback,则直接notify,否则加入事件队列,EventThread负责调度
    if (p.cb == null) {
        synchronized (p) {
            p.finished = true;
            p.notifyAll();
        }
    } else {
        p.finished = true;
        eventThread.queuePacket(p);
    }
}

NIO通信问题

06-26

这几天一直在研究这东西,然后自己照着例子写了点东东。不过一直测试都有问题,在这里贴上代码,,,希望好心人能够耐心解答。感激不尽!rnrn以下是服务端代码,我只想先实现收信息功能,能够在服务端的JTextArea上显示出来。rn[code=Java]rnpackage server;rnrnimport java.io.IOException;rnimport java.net.InetSocketAddress;rnimport java.nio.ByteBuffer;rnimport java.nio.channels.SelectionKey;rnimport java.nio.channels.Selector;rnimport java.nio.channels.ServerSocketChannel;rnimport java.nio.channels.SocketChannel;rnimport java.util.Iterator;rnimport java.util.Set;rnrnimport javax.swing.*;rnrnimport common.*;rnrnpublic class TetrisServer extends JFrame implements Constantsrn ServerSocketChannel serverSocket;rn SocketChannel socket;rn Selector selector;rn rn// SelectionKey [][] tables=new SelectionKey[10][2];rn rn JTextArea jtaMessage=new JTextArea();rn rn rn public TetrisServer()rn setSize(300, 400);rn setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);rn add(new JScrollPane(jtaMessage));rn setVisible(true);rn rn try rn serverSocket=ServerSocketChannel.open();rn selector=Selector.open();rn serverSocket.socket().setReuseAddress(true);rn serverSocket.socket().bind(new InetSocketAddress(8000));rn jtaMessage.append("start server successfully!\n");rn rn catch (IOException e) rn // TODO Auto-generated catch blockrn e.printStackTrace();rn rn rn rn public void accept() rn for(;;)rn try rn socket=serverSocket.accept();rn socket.configureBlocking(false);rn jtaMessage.append("client linked:"+socket.socket().getInetAddress()+":"+socket.socket().getPort()+"\n");rn// ByteBuffer buffer=ByteBuffer.allocate(1024);rn Message message=new Message();rn synchronized (gate) rn selector.wakeup();rn socket.register(selector, SelectionKey.OP_READ, message); rn rn catch (IOException e) rn // TODO Auto-generated catch blockrn e.printStackTrace();rn rn rn rn private Object gate=new Object();rn public void service() throws IOExceptionrn for(;;)rn synchronized (gate) rn int n=selector.select();rn if(n==0) continue;rn rn Set readyKeys=selector.selectedKeys();rn Iterator iterator=readyKeys.iterator();rn while(iterator.hasNext())rn SelectionKey key=null;rn try rn key=iterator.next();rn iterator.remove();rn if(key.isReadable())rn jtaMessage.append("server read");rn receive(key);rn rn rn if(key.isWritable())rn send(key);rn jtaMessage.append("server write");rn rn catch (IOException e) rn // TODO: handle exceptionrn catch (ClassNotFoundException e) rn // TODO: handle exceptionrn rn rn rn rn rn public void receive(SelectionKey key) throws IOException,ClassNotFoundExceptionrn ByteBuffer readBuffer=ByteBuffer.allocate(1024);rn SocketChannel socket=(SocketChannel) key.channel();rn socket.read(readBuffer);rn readBuffer.flip();rn jtaMessage.append(new String(readBuffer.array())+"\n");rn rn// Message playerMessage=null;rn// Message sendMessage=null;rn// SelectionKey opponentKey=null;rnrn rn public void send(SelectionKey key) throws IOExceptionrn// ByteBuffer sendBuffer=(ByteBuffer)key.attachment();rn Message sendMessage=(Message)key.attachment();rn SocketChannel socket=(SocketChannel)key.channel();rn socket.write(ByteUtil.getByteBuffer(sendMessage));rn rn public boolean checkPlayMessage(String name,String password)rn return true;rn rn /**rn * @param argsrn */rn public static void main(String[] args) rn // TODO Auto-generated method stubrn rn final TetrisServer frameServer=new TetrisServer();rn Thread accept=new Thread()rn public void run()rn frameServer.accept();rn rn ;rn accept.start();rn rn try rn frameServer.service();rn catch (IOException e) rn // TODO Auto-generated catch blockrn e.printStackTrace();rn rn rn rn rnrnrnrn[/code]rnrn以下是客户端代码,我想有个面板输入信息,通过button触发事件来发出信息。但是,事实却是要发出一条信息必须要点击button 不定次数,几次到十几次不等= =。。。才能够在服务端显示出该信息。看了很久不知道问题出在哪,求解答。。。rn[code=Java]rnpackage client;rnimport java.io.IOException;rnimport java.net.InetAddress;rnimport java.net.InetSocketAddress;rnimport java.nio.ByteBuffer;rnimport java.nio.CharBuffer;rnimport java.nio.channels.ClosedChannelException;rnimport java.nio.channels.SelectionKey;rnimport java.nio.channels.Selector;rnimport java.nio.channels.SocketChannel;rnimport java.nio.charset.Charset;rnimport java.util.Iterator;rnimport java.util.Set;rnrnimport common.*;rnrnpublic class TetrisClient implements Constantsrn SocketChannel socket;rn Selector selector;rn ByteBuffer sendBuffer=ByteBuffer.allocate(1024);rn ByteBuffer receiveBuffer=ByteBuffer.allocate(1024);rn private Charset charset=Charset.forName("GBK");rn rn public TetrisClient()rn try rn socket=SocketChannel.open();rn socket.connect(new InetSocketAddress(InetAddress.getLocalHost(), 8000));rn socket.configureBlocking(false);rn System.out.println("与服务器的连接建立成功");rn selector=Selector.open();rn rn// talkToServer();rn rn catch (IOException e) rn // TODO Auto-generated catch blockrn e.printStackTrace();rn rn rn public void packMessage(String name,String password,int position,int state)rn Message message=new Message();rn message.position=position;rn message.state=state;rn message.playerName=name;rn message.password=password;rn// try rn// sendBuffer=ByteUtil.getByteBuffer(message);rn sendBuffer=ByteBuffer.wrap(name.getBytes());rn// catch (IOException e) rn// // TODO Auto-generated catch blockrn// e.printStackTrace();rn// rn rn public void talkToServer() throws IOExceptionrn socket.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);rn rn while(selector.select()>0)rn Set readyKeys=selector.selectedKeys();rn Iterator iterator=readyKeys.iterator();rn rn while(iterator.hasNext())rn SelectionKey key=(SelectionKey)iterator.next();rn iterator.remove();rn if(key.isReadable())rn receive(key);rn rn if(key.isWritable())rn send(key);rn rn rn// System.out.println("talk to server");rn rn rn public void send(SelectionKey key)throws IOExceptionrn SocketChannel socketChannel=(SocketChannel)key.channel();rn synchronized(sendBuffer)rn sendBuffer.flip(); //把极限设为位置rn socketChannel.write(sendBuffer);rn sendBuffer.compact();rn// System.out.println("send");rn rn rn public void receive(SelectionKey key)throws IOExceptionrn SocketChannel socketChannel=(SocketChannel)key.channel();rn socketChannel.read(receiveBuffer);rn receiveBuffer.flip();rn String receiveData=decode(receiveBuffer);rnrn if(receiveData.indexOf("\n")==-1)return;rnrn String outputData=receiveData.substring(0,receiveData.indexOf("\n")+1);rn System.out.print(outputData);rn if(outputData.equals("echo:bye\r\n"))rn key.cancel();rn socketChannel.close();rn System.out.println("关闭与服务器的连接");rn selector.close();rn System.exit(0);rn rnrn ByteBuffer temp=encode(outputData);rn receiveBuffer.position(temp.limit());rn receiveBuffer.compact();rn rnrn public String decode(ByteBuffer buffer) //解码rn CharBuffer charBuffer= charset.decode(buffer);rn return charBuffer.toString();rn rn public ByteBuffer encode(String str) //编码rn return charset.encode(str);rn rnrnrnrn[/code]rnrn以下是客户端的main函数。。。rn[code=Java]rnpackage client;rnrnimport java.awt.FlowLayout;rnimport java.awt.event.ActionEvent;rnimport java.awt.event.ActionListener;rnimport java.io.IOException;rnrnimport javax.swing.JButton;rnimport javax.swing.JFrame;rnimport javax.swing.JTextField;rnimport javax.swing.SwingUtilities;rnrnpublic class Login extends JFramern JTextField jtfName=new JTextField(10);rn JTextField jtfPassword=new JTextField(10);rn JButton jbtLogin=new JButton("Login");rn static TetrisClient client;rn public Login()rn setLayout(new FlowLayout());rn add(jtfName);rn add(jtfPassword);rn add(jbtLogin);rn setSize(200, 300);rn setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);rn setVisible(true);rn rn jbtLogin.addActionListener(new LoginListener());rn rn rn public class LoginListener implements ActionListenerrnrn @Overridern public void actionPerformed(ActionEvent e) rn // TODO Auto-generated method stubrn System.out.println("login");rn client.packMessage(jtfName.getText(), jtfPassword.getText(), client.LOGIN, client.LOGIN_EXIST);rn rn rn rn /**rn * @param argsrn */rn public static void main(String[] args) rn // TODO Auto-generated method stubrn rn Login login=new Login();rn client= new TetrisClient();rn Thread thread=new Thread(new Runnable() rn rn @Overridern public void run() rn // TODO Auto-generated method stubrn try rn client.talkToServer();rn catch (IOException e) rn // TODO Auto-generated catch blockrn e.printStackTrace();rn rn rn );rn thread.start();rn rnrnrnrn[/code]rnrn最后再提个问题:可以看到我的客户端里有个Message 对象,虽然还没用上,那是我准备将一个对象整个传过去的。请问,可以将对象转化为ByteBuffer后传给服务端,在再服务端转化回来,然后输出对象里面的信息吗?rn

没有更多推荐了,返回首页

私密
私密原因:
请选择设置私密原因
  • 广告
  • 抄袭
  • 版权
  • 政治
  • 色情
  • 无意义
  • 其他
其他原因:
120
出错啦
系统繁忙,请稍后再试