记得前段时间面试,有些主攻java的公司总会问“你对网络编程比较熟,那java的AIO BIO NIO”知道么,答曰,确实对java不熟。在陈硕muduo中多次提到与netty都是用的one loop per thread
,于是找了一个做Java后台的大佬借了一本Netty权威指南,开始学习。
代码:https://github.com/NearXdu/NettyLearn
1. BIO
B阻塞,传统的同步阻塞式IO,在UNP中,这种IO是 one connection per thread/process
,在Java中似乎Listen Fd 和 Accept Fd 分别有各自的封装ServerSocket
和Socket
.编程框架也是比较简单的:
server = new ServerSocket(port);
while (true){
socket = server.accept();
new Thread(new ServerHandler(socket)).start();
}
//
ServerHandler implements Runnable {
private Socket socket;
public void run(){
//...
}
}
主线程在loop中阻塞在accept
调用,当新连接的到来的时候,将connfd扔进handle线程中,主线程继续等待连接.
2.伪异步
不用每次的创建线程,因为这样开销很大,用任务队列+线程池的方式,实现伪异步。
具体:
将Socket封装成一个Task,该Task实现Runnable接口,投递到任务队列中,后端的线程池进行处理。线程池中的线程有限,当任务数比较多时,将阻塞在任务队列里面(blocking queue),同样当任务队列为空时,线程池的线程也将挂起。这些java都封装好了,用起来还是很方便的:
executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),
maxPoolSize,//线程池大小
120L,
TimeUnit.SECONDS,//最大空闲实现
new ArrayBlockingQueue<java.lang.Runnable>(queueSize));//blocking Queue Size
while(true)
{
socket=server.accept();
executor.execute(new task(socket));
}
3.NIO编程
也就是Reactor,在netty中,有几个重要的组件:Buffer
读写缓冲,Channel
对描述符的封装,Selector
多路复用选择器。
使用NIO编程的步骤如下:
//监听套接字
ServerSocketChannel acceptorSvr=ServerSocketChannel.open();
bind;
acceptorSvr.configureBlocking(false);
//创建selecto并启动线程
Selector selector = Selector.open();
new Thread(new ReactorTask()).start();
//将监听套接字注册到selector上
SelectionKey key=acceptorSvr.register(selector,OP_ACCEPT,ioHandler);
//在Reactor线程中轮询
int num=selector.select();
Set selectedKey=selector.selectedKeys();
Iterator it = selectdKey.iterator();
while(it.hasNext()){
SelectionKey key = (SelectionKey)it.next();
//...
}
//accept新连接
SocketChannel channel= svrChannel.accept();
//为已连接套接字注册IO事件
SelectionKey key=socketChannel.register(selector,SelectionKey.OP_READ,ioHandler)
4.AIO
异步编程,有点类似boost.asio,每次需要注册完成回调,并在完成回调中调用异步操作来驱动,最终形
成一个循环。需要程序员实现一个CompletionHandler接口,相比同步Selector,AIO是真正的异步调用
例如
public class AcceptCompletionHandler implements
CompletionHandler<AsynchronousSocketChannel, AsyncTimeServerHandler> {
@Override
public void completed(AsynchronousSocketChannel asynchronousSocketChannel, AsyncTimeServerHandler asyncTimeServerHandler) {
asyncTimeServerHandler.asynchronousServerSocketChannel.accept();
ByteBuffer buffer = ByteBuffer.allocate(1024);
asynchronousSocketChannel.read(buffer,buffer,new ReadCompletionHandler(asynchronousSocketChannel));
}
@Override
public void failed(Throwable throwable, AsyncTimeServerHandler asyncTimeServerHandler) {
throwable.printStackTrace();
asyncTimeServerHandler.latch.countDown();
}
}