深入浅出Netty之三 Server请求处理

来源:互联网 发布:软件模块化设计 编辑:程序博客网 时间:2024/05/20 22:29

Server bind之后,就可以对外提供服务了。Netty使用了reactor模式来提升服务的并发处理能力。boss线程负责监听新的连接请求,当有新的连接进来时,将对应的channel指派一个worker线程来处理。Worker线程负责对该Channel的读写操作。

一.Boss线程

1.阻塞Select

Java代码 复制代码 收藏代码
  1. for (;;) {
  2. try {
  3. // Boss线程专门负责监听新入连接,所以阻塞select
  4. selector.select();
  5. // 如果有新连接,先把key清掉
  6. selector.selectedKeys().clear();
  7. // 循环请求队列,处理连接
  8. for (;;) {
  9. SocketChannel acceptedSocket = channel.socket.accept();
  10. if (acceptedSocket == null) {
  11. break;
  12. }
  13. registerAcceptedChannel(acceptedSocket, currentThread);
  14. }
  15. ......
2.注册新连接
Java代码 复制代码 收藏代码
  1. private void registerAcceptedChannel(SocketChannel acceptedSocket, Thread currentThread) {
  2. ......
  3. //根据用户自定义的的PipelineFactory创建pipeline
  4. ChannelPipeline pipeline =
  5. channel.getConfig().getPipelineFactory().getPipeline();
  6. //hash分配worker线程,默认使用递增循环worker数组方式
  7. NioWorker worker = nextWorker();
  8. //将新的连接注册到worker线程,让worker线程负责后续读写
  9. //新的channel是主channel的子channel,而PipelineSink和主channel是同一个
  10. worker.register(new NioAcceptedSocketChannel(
  11. channel.getFactory(), pipeline, channel,
  12. NioServerSocketPipelineSink.this, acceptedSocket,
  13. worker, currentThread), null);
  14. ......
  15. }
Java代码 复制代码 收藏代码
  1. void register(AbstractNioChannel<?> channel, ChannelFuture future) {
  2. synchronized (startStopLock) {
  3. ......
  4. //创建注册通道的任务
  5. Runnable registerTask = createRegisterTask(channel, future);
  6. //提交任务到阻塞队列
  7. boolean offered = registerTaskQueue.offer(registerTask);
  8. //唤醒selector
  9. if (wakenUp.compareAndSet(false,true)) {
  10. selector.wakeup();
  11. }
  12. }
  13. }

3.创建注册任务

Java代码 复制代码 收藏代码
  1. protected Runnable createRegisterTask(AbstractNioChannel<?> channel, ChannelFuture future) {
  2. boolean server = !(channel instanceof NioClientSocketChannel);
  3. return new RegisterTask((NioSocketChannel) channel, future, server);
  4. }

二.worker线程

worker线程负责对应channel的读写操作,一个worker对应一个selector,会同时处理多个channel的读写。

1.主循环

Java代码 复制代码 收藏代码
  1. for (;;) {
  2. wakenUp.set(false);
  3. ......
  4. if (wakenUp.get()) {
  5. wakenupFromLoop = true;
  6. selector.wakeup();
  7. } else {
  8. wakenupFromLoop = false;
  9. }
  10. cancelledKeys = 0;
  11. //处理注册通道的任务
  12. processRegisterTaskQueue();
  13. //处理异步事件,比如writeComplete事件
  14. processEventQueue();
  15. //处理写数据任务,如果业务线程有异步写的时候,会有WriteTask放入队列
  16. processWriteTaskQueue();
  17. //处理IO准备好的那些channel
  18. processSelectedKeys(selector.selectedKeys());
  19. ......
  20. }