`

多种IO设计模式

阅读更多
Java通信的几种IO设计
[size=large]阻塞IO


同步阻塞最常用的一种用法,使用也是最简单的,但是 I/O 性能一般很差,CPU 大部分在空闲状态。下面是一个简单的基于TCP的同步阻塞的Socket服务端例子:
@Test
    public void testBlockIoSocket() throws Exception
    {
        ServerSocket serverSocket = new ServerSocket(10002);
        Socket socket = null;
        try
        {
            while (true)
            {
                socket = serverSocket.accept();
                System.out.println("socket连接:" + socket.getRemoteSocketAddress().toString());
                BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                while(true)
                {
                    String readLine = in.readLine();
                    System.out.println("收到消息" + readLine);
                    if("end".equals(readLine))
                    {
                        break;
                    }
                    //客户端断开连接
                    socket.sendUrgentData(0xFF);
                }
            }
        }
        catch (SocketException se)
        {
            System.out.println("客户端断开连接");
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            System.out.println("socket关闭:" + socket.getRemoteSocketAddress().toString());
            socket.close();
        }
}
设计分析:
由于服务器端是单线程的,在第一个连接的客户端阻塞了线程后,第二个客户端必须等待第一个断开后才能连接。
所有的客户端连接在请求服务端时都会阻塞住,等待前面的完成。即使是使用短连接,数据在写入 OutputStream 或者从 InputStream 读取时都有可能会阻塞。这在大规模的访问量或者系统对性能有要求的时候是不能接受的。



阻塞IO + 每个请求创建线程/线程池

通常解决这个问题的方法是使用多线程技术,一个客户端一个处理线程,出现阻塞时只是一个线程阻塞而不会影响其它线程工作;为了减少系统线程的开销,采用线程池的办法来减少线程创建和回收的成本。模式如下图:


每当来一个客户端的连接的时候,我们服务器就new 一个线程来处理它。在服务器的主程序是不阻塞的,阻塞的只是这个线程。这样也是我目前最常用的模式。

在单个线程处理中,我人为的使单个线程read后阻塞5秒,就像前面说的,出现阻塞也只是在单个线程中,没有影响到另一个客户端的处理。
  这种阻塞IO的解决方案在大部分情况下是适用的,在出现NIO之前是最通常的解决方案,Tomcat里阻塞IO的实现就是这种方式。但是如果是大量的长连接请求呢?不可能创建几百万个线程保持连接。再退一步,就算线程数不是问题,如果这些线程都需要访问服务端的某些竞争资源,势必需要进行同步操作,这本身就是得不偿失的。



非阻塞IO + IO multiplexing  Java从1.4开始提供了NIO工具包,这是一种不同于传统流IO的新的IO方式,使得Java开始对非阻塞IO支持;NIO并不等同于非阻塞IO,只要设置Blocking属性就可以控制阻塞非阻塞。至于NIO的工作方式特点原理这里一概不说,以后会写。模式如下图:



public class NioNonBlockingSelectorTest
{
    Selector selector;
    private ByteBuffer receivebuffer = ByteBuffer.allocate(1024);
   
    @Test
    public void testNioNonBlockingSelector()
        throws Exception
    {
        selector = Selector.open();
        SocketAddress address = new InetSocketAddress(10002);
        ServerSocketChannel channel = ServerSocketChannel.open();
        channel.socket().bind(address);
        channel.configureBlocking(false);
        channel.register(selector, SelectionKey.OP_ACCEPT);
       
        while(true)
        {
            selector.select();
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {         
                SelectionKey selectionKey = iterator.next(); 
                iterator.remove(); 
                handleKey(selectionKey); 
            } 
        }
    }
   
    private void handleKey(SelectionKey selectionKey) throws IOException
    {
        ServerSocketChannel server = null;
        SocketChannel client = null;
        if(selectionKey.isAcceptable())
        {
            server = (ServerSocketChannel)selectionKey.channel();
            client = server.accept();
            System.out.println("客户端: " + client.socket().getRemoteSocketAddress().toString());
            client.configureBlocking(false);
            client.register(selector, SelectionKey.OP_READ);
        }
        if(selectionKey.isReadable())
        {
            client = (SocketChannel)selectionKey.channel();
            receivebuffer.clear(); 
            int count = client.read(receivebuffer);  
            if (count > 0) { 
                String receiveText = new String( receivebuffer.array(),0,count); 
                System.out.println("服务器端接受客户端数据--:" + receiveText); 
                client.register(selector, SelectionKey.OP_READ); 
            }
        }
    }
   
}

Java NIO提供的非阻塞IO并不是单纯的非阻塞IO模式,而是建立在Reactor模式上的IO复用模型;在IO multiplexing Model中,对于每一个socket,一般都设置成为non-blocking,但是整个用户进程其实是一直被阻塞的。只不过进程是被select这个函数阻塞,而不是被socket IO给阻塞,所以还是属于非阻塞的IO。

网络IO优化

  对于网络IO有一些基本的处理规则如下:

  1。减少交互的次数。比如增加缓存,合并请求。

  2。减少传输数据大小。比如压缩后传输、约定合理的数据协议。

  3。减少编码。比如提前将字符转化为字节再传输。

  4。根据应用场景选择合适的交互方式,同步阻塞,同步非阻塞,异步阻塞,异步非阻塞。

[/size]
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics