Bolt服务器的核心类是RpcServer,启动的时候调用父类AbstractRemotingServer的startup方法。

com.alipay.remoting.AbstractRemotingServer#startup
@Override
public void startup() throws LifeCycleException {
super.startup(); try {
doInit(); logger.warn("Prepare to start server on port {} ", port);
if (doStart()) {
logger.warn("Server started on port {}", port);
} else {
logger.warn("Failed starting server on port {}", port);
throw new LifeCycleException("Failed starting server on port: " + port);
}
} catch (Throwable t) {
this.shutdown();// do stop to ensure close resources created during doInit()
throw new IllegalStateException("ERROR: Failed to start the Server!", t);
}
}

这里主要做了三件事

  1. 调用父类的startup()方法设置状态为启动

     com.alipay.remoting.AbstractLifeCycle#startup
    @Override
    public void startup() throws LifeCycleException {
    if (isStarted.compareAndSet(false, true)) {
    return;
    }
    throw new LifeCycleException("this component has started");
    }
  2. 调用实现类的doInit()进行实际的初始化工作

    	com.alipay.remoting.rpc.RpcServer#doInit
    
    	@Override
    protected void doInit() {
    if (this.addressParser == null) {
    this.addressParser = new RpcAddressParser();
    }
    if (this.switches().isOn(GlobalSwitch.SERVER_MANAGE_CONNECTION_SWITCH)) {
    // in server side, do not care the connection service state, so use null instead of global switch
    ConnectionSelectStrategy connectionSelectStrategy = new RandomSelectStrategy(null);
    this.connectionManager = new DefaultServerConnectionManager(connectionSelectStrategy);
    this.connectionManager.startup(); this.connectionEventHandler = new RpcConnectionEventHandler(switches());
    this.connectionEventHandler.setConnectionManager(this.connectionManager);
    this.connectionEventHandler.setConnectionEventListener(this.connectionEventListener);
    } else {
    this.connectionEventHandler = new ConnectionEventHandler(switches());
    this.connectionEventHandler.setConnectionEventListener(this.connectionEventListener);
    }
    initRpcRemoting();
    this.bootstrap = new ServerBootstrap();
    this.bootstrap.group(bossGroup, workerGroup)
    .channel(NettyEventLoopUtil.getServerSocketChannelClass())
    .option(ChannelOption.SO_BACKLOG, ConfigManager.tcp_so_backlog())
    .option(ChannelOption.SO_REUSEADDR, ConfigManager.tcp_so_reuseaddr())
    .childOption(ChannelOption.TCP_NODELAY, ConfigManager.tcp_nodelay())
    .childOption(ChannelOption.SO_KEEPALIVE, ConfigManager.tcp_so_keepalive()); // set write buffer water mark
    initWriteBufferWaterMark(); // init byte buf allocator
    if (ConfigManager.netty_buffer_pooled()) {
    this.bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
    .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
    } else {
    this.bootstrap.option(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT)
    .childOption(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT);
    } // enable trigger mode for epoll if need
    NettyEventLoopUtil.enableTriggeredMode(bootstrap); final boolean idleSwitch = ConfigManager.tcp_idle_switch();
    final int idleTime = ConfigManager.tcp_server_idle();
    final ChannelHandler serverIdleHandler = new ServerIdleHandler();
    final RpcHandler rpcHandler = new RpcHandler(true, this.userProcessors);
    this.bootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Override
    protected void initChannel(SocketChannel channel) {
    ChannelPipeline pipeline = channel.pipeline();
    pipeline.addLast("decoder", codec.newDecoder());
    pipeline.addLast("encoder", codec.newEncoder());
    if (idleSwitch) {
    pipeline.addLast("idleStateHandler", new IdleStateHandler(0, 0, idleTime,
    TimeUnit.MILLISECONDS));
    pipeline.addLast("serverIdleHandler", serverIdleHandler);
    }
    pipeline.addLast("connectionEventHandler", connectionEventHandler);
    pipeline.addLast("handler", rpcHandler);
    createConnection(channel);
    } /**
    * create connection operation<br>
    * <ul>
    * <li>If flag manageConnection be true, use {@link DefaultConnectionManager} to add a new connection, meanwhile bind it with the channel.</li>
    * <li>If flag manageConnection be false, just create a new connection and bind it with the channel.</li>
    * </ul>
    */
    private void createConnection(SocketChannel channel) {
    Url url = addressParser.parse(RemotingUtil.parseRemoteAddress(channel));
    if (switches().isOn(GlobalSwitch.SERVER_MANAGE_CONNECTION_SWITCH)) {
    connectionManager.add(new Connection(channel, url), url.getUniqueKey());
    } else {
    new Connection(channel, url);
    }
    channel.pipeline().fireUserEventTriggered(ConnectionEventType.CONNECT);
    }
    });
    }

    这里的代码看似很复杂,其实主要是配置Netty服务器。Netty服务器的可配置选项通过ConfigManager来获取,Netty的业务处理在childHandler里面。

    protected void initChannel(SocketChannel channel) {
    ChannelPipeline pipeline = channel.pipeline();
    pipeline.addLast("decoder", codec.newDecoder());
    pipeline.addLast("encoder", codec.newEncoder());
    if (idleSwitch) {
    pipeline.addLast("idleStateHandler", new IdleStateHandler(0, 0, idleTime,
    TimeUnit.MILLISECONDS));
    pipeline.addLast("serverIdleHandler", serverIdleHandler);
    }
    pipeline.addLast("connectionEventHandler", connectionEventHandler);
    pipeline.addLast("handler", rpcHandler);
    createConnection(channel);
    }

    初始化channel的方法里面有6个channelHandler

    1. decoder 解码器

    2. encoder 编码器

    3. idleStateHandler

      Netty自带的空闲处理器,用于触发IdleStateEvent。在这里配置了总空闲时间idleTime(默认值是90000),即idleTime时间内如果通道没有发生读写操作,将出发一个IdleStateEvent事件。

    4. serverIdleHandler

      配合idleStateHandler使用,用来处理IdleStateEvent。当触发该时间后,关闭客户端连接

          @Override
      public void userEventTriggered(final ChannelHandlerContext ctx, Object evt) throws Exception {
      if (evt instanceof IdleStateEvent) {
      try {
      logger.warn("Connection idle, close it from server side: {}",
      RemotingUtil.parseRemoteAddress(ctx.channel()));
      ctx.close();
      } catch (Exception e) {
      logger.warn("Exception caught when closing connection in ServerIdleHandler.", e);
      }
      } else {
      super.userEventTriggered(ctx, evt);
      }
      }
    5. connectionEventHandler

      connect事件处理器,类型由枚举类ConnectionEventType定义

      public enum ConnectionEventType {
      CONNECT, CLOSE, EXCEPTION;
      }
      • CONNECT

        connect事件在RpcServer.createConnection()方法里触发了一次

      • CLOSE

        close事件在连接断开时会触发

        com.alipay.remoting.ConnectionEventHandler
        
        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        String remoteAddress = RemotingUtil.parseRemoteAddress(ctx.channel());
        infoLog("Connection channel inactive: {}", remoteAddress);
        super.channelInactive(ctx);
        Attribute attr = ctx.channel().attr(Connection.CONNECTION);
        if (null != attr) {
        // add reconnect task
        if (this.globalSwitch != null
        && this.globalSwitch.isOn(GlobalSwitch.CONN_RECONNECT_SWITCH)) {
        Connection conn = (Connection) attr.get();
        if (reconnectManager != null) {
        reconnectManager.reconnect(conn.getUrl());
        }
        }
        // trigger close connection event
        onEvent((Connection) attr.get(), remoteAddress, ConnectionEventType.CLOSE);
        }
        }
      • EXCEPTION

        exception异常事件在源代码里面没有触发的地方,应该是预留的。

      无论是什么事件,最终都调用onEvent方法,转发给ConnectionEventListener,最终由用户自定义的ConnectionEventProcessor来处理具体逻辑

      com.alipay.remoting.ConnectionEventHandler#onEvent
      
      private void onEvent(final Connection conn, final String remoteAddress,
      final ConnectionEventType type) {
      if (this.eventListener != null) {
      this.eventExecutor.onEvent(new Runnable() {
      @Override
      public void run() {
      ConnectionEventHandler.this.eventListener.onEvent(type, remoteAddress, conn);
      }
      });
      }
      } com.alipay.remoting.ConnectionEventListener#onEvent
      public void onEvent(ConnectionEventType type, String remoteAddress, Connection connection) {
      List<ConnectionEventProcessor> processorList = this.processors.get(type);
      if (processorList != null) {
      for (ConnectionEventProcessor processor : processorList) {
      processor.onEvent(remoteAddress, connection);
      }
      }
      }
    6. handler

      具体业务处理器,注册类为RpcHandler,调用流程图如下

      对于客户端请求的处理交给UserProcessor, 可以调用RpcServer类的registerUserProcessor注册自定义的业务。

  3. 调用dostart启动服务器

    com.alipay.remoting.rpc.RpcServer#doStart
    
    @Override
    protected boolean doStart() throws InterruptedException {
    this.channelFuture = this.bootstrap.bind(new InetSocketAddress(ip(), port())).sync();
    return this.channelFuture.isSuccess();
    }

sofa-bolt源码阅读(1)-服务端的启动的更多相关文章

  1. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  2. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  3. Netty 4源码解析:服务端启动

    Netty 4源码解析:服务端启动 1.基础知识 1.1 Netty 4示例 因为Netty 5还处于测试版,所以选择了目前比较稳定的Netty 4作为学习对象.而且5.0的变化也不像4.0这么大,好 ...

  4. Netty源码阅读(一) ServerBootstrap启动

    Netty源码阅读(一) ServerBootstrap启动 转自我的Github Netty是由JBOSS提供的一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速 ...

  5. TeamTalk源码分析之服务端描述

    TTServer(TeamTalk服务器端)主要包含了以下几种服务器: LoginServer (C++): 登录服务器,分配一个负载小的MsgServer给客户端使用 MsgServer (C++) ...

  6. netty4.1.6源码2-------创建服务端的channel

    1. netty在哪里调用jdk底层的socket去创建netty服务端的socket. 2. 在哪里accept连接. 服务端的启动: 1. 调用jdk底层的api去创建jdk的服务端的channe ...

  7. 4. 源码分析---SOFARPC服务端暴露

    服务端的示例 我们首先贴上我们的服务端的示例: public static void main(String[] args) { ServerConfig serverConfig = new Ser ...

  8. Netty源码分析之服务端启动过程

    一.首先来看一段服务端的示例代码: public class NettyTestServer { public void bind(int port) throws Exception{ EventL ...

  9. Spring Cloud系列(三):Eureka源码解析之服务端

    一.自动装配 1.根据自动装配原理(详见:Spring Boot系列(二):Spring Boot自动装配原理解析),找到spring-cloud-starter-netflix-eureka-ser ...

随机推荐

  1. Python语言学习:列表常用的方法

    python 列表常用的方法 1.append( ):用于在列表末尾添加新的对象 list.appent(obj) #obj:添加到列表末尾的对象 #!/usr/bin/python aList = ...

  2. vitual box 虚拟机调整磁盘大小 resize partiton of vitual os

    key:vitual box, 虚拟机,调整分区大小 引用:http://derekmolloy.ie/resize-a-virtualbox-disk#prettyPhoto 1. 关闭虚拟机,找到 ...

  3. 吴裕雄--天生自然python学习笔记:python 用 Open CV抓取摄像头视频图像

    Open CV 除了可以读取.显示静态图片外 , 还可 以加载及播放动态影片, 以 及 读取内置或外接摄像头的图像信息 . 很多笔记本电脑都具有摄像头 , OpenCV 可通过 VideoC aptu ...

  4. RDD(七)——依赖

    概述 RDD只支持粗粒度转换,即在大量记录上执行的单个操作.将创建RDD的一系列Lineage(血统)记录下来,以便恢复丢失的分区.RDD的Lineage会记录RDD的元数据信息和转换行为,当该RDD ...

  5. VisualStudioAddin2016Setup.rar

    本工具是用于Visual Studio 2010 /2012 的外接程序. 功能不太多,常用代码,引用管理等. 动态图: 下载地址: VisualStudioAddin2016Setup.rar

  6. 奇点云 x 阿里云 | 联合发布综合体数字化转型与数据创新解决方案

    2019年7月25日下午,在阿里云峰会上海站,奇点云入选阿里云首批联合解决方案合作伙伴,并联合发布了“综合体数字化转型与数据创新解决方案”,共同探索综合体的智能服务. 关于综合体的数字化转型,奇点云联 ...

  7. vscode中的live-server配置https?

    json文件中使用绝对路径添加证书 "liveServer.settings.https": { "enable": true, "cert" ...

  8. 吴裕雄--天生自然Android开发学习:魅蓝3开启USB调试

    打开手机点击:设置 选择:关于手机 在详情里面找到:版本号,然后不断地点击那个版本号. 然后返回一步 再选择设置里面的:辅助功能 再选择辅助功能里面的:开发者选项 进入开发者选项后,选择打开两项:一是 ...

  9. 吴裕雄--天生自然python学习笔记:Python uWSGI 安装配置

    本文主要介绍如何部署简单的 WSGI 应用和常见的 Web 框架. 以 Ubuntu/Debian 为例,先安装依赖包: apt-get install build-essential python- ...

  10. OpenCV 腐蚀与膨胀(Eroding and Dilating)

    #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #i ...