• Dubbo发布过程中,netty监听服务的启动


    服务的启动时机

    经过前面的分析,dubbo经过url协议的解析,首先在本地发布一个injvm协议的调用者Invoker实例,然后经过本地服务暴露,获取一个exporter;然后获取dubbo协议实例,获取dubbo协议的invoker,然后调用DubboProtocol的export暴露服务,在暴露服务的过程中,同时开启服务监听。

    在本地暴露服务的时候,调用的是InjvmProtocol的export,同时InjvmProtocol是一个Wrapper对象,被3个装饰类进行了包装,包装的时机发生在加载扩展点的过程中,判断构造函数如果有参数,且参数类型是Protocol,则对当前类缓存在ExtensionLoader的cachedWrapperClasses成员变量中

    接下来看一下Dubbo协议的服务暴露过程,其入口如下所示:

    看一下export方法中,具体做了哪些动作:包括获取service key,openServer,在openServer的方法中,调用了createServer方法来创建一个server,如果server存在,会根据url对当前服务接口调用reset方法进行重置操作,进行心跳检测。

    public class DubboProtocol extends AbstractProtocol {
    
        public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
            URL url = invoker.getUrl();
    
            // export service.
            String key = serviceKey(url);
            DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
            exporterMap.put(key, exporter);
    
            //export an stub service for dispatching event
            Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT);
            Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false);
            if (isStubSupportEvent && !isCallbackservice) {
                String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY);
                if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
                    if (logger.isWarnEnabled()) {
                        logger.warn(new IllegalStateException("consumer [" + url.getParameter(Constants.INTERFACE_KEY) +
                                "], has set stubproxy support event ,but no stub methods founded."));
                    }
                } else {
                    stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
                }
            }
    
            openServer(url);
            optimizeSerialization(url);
            return exporter;
        }
    
        private void openServer(URL url) {
            // find server.
            String key = url.getAddress();
            //client can export a service which's only for server to invoke
            boolean isServer = url.getParameter(Constants.IS_SERVER_KEY, true);
            if (isServer) {
                ExchangeServer server = serverMap.get(key);
                if (server == null) {
                    serverMap.put(key, createServer(url));
                } else {
                    // server supports reset, use together with override
                    server.reset(url);
                }
            }
        }
    

    在createServer方法中,默认添加了一个配置参数,如心跳监测间隔为默认60s,服务器类型为netty等,然后通过扩展点加载获取netty传输对象,NettyTransporter。添加编译类型,dubbo。然后调用Exchangers的bind方法,进行端口监听。

        public static final String DEFAULT_REMOTING_SERVER = "netty";
        public static final int DEFAULT_HEARTBEAT = 60 * 1000;
        private ExchangeServer createServer(URL url) {
    
            // send readonly event when server closes, it's enabled by default
            url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
            // enable heartbeat by default
            url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
            String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);
    
            if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))
                throw new RpcException("Unsupported server type: " + str + ", url: " + url);
    
            url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
            ExchangeServer server;
            try {
                server = Exchangers.bind(url, requestHandler);
            } catch (RemotingException e) {
                throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
            }
            str = url.getParameter(Constants.CLIENT_KEY);
            if (str != null && str.length() > 0) {
                Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
                if (!supportedTypes.contains(str)) {
                    throw new RpcException("Unsupported client type: " + str);
                }
            }
            return server;
        }
    }
    

    Exchangers.bind服务监听

    根据默认DEFAULT_EXCHANGER类型,获取到HeaderExchanger。调用HeaderExchanger的bind方法

    public class Exchangers {
        public static final String DEFAULT_EXCHANGER = "header";
        public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
            if (url == null) {
                throw new IllegalArgumentException("url == null");
            }
            if (handler == null) {
                throw new IllegalArgumentException("handler == null");
            }
            url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
            return getExchanger(url).bind(url, handler);
        }
    
        public static Exchanger getExchanger(URL url) {
            String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);
            return getExchanger(type);
        }
        public static Exchanger getExchanger(String type) {
            return ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);
        }
    }
    

    调用bind方法,内部首先调用了Transporters.bind方法,new DecodeHandler(new HeaderExchangeHandler(handler))方法对传进来的Handler进行了包装,添加了DecodeHandler、HeaderExchangeHandler处理器。Transporters.bind返回一个NettyServer,然后包装成一个HeaderExchangeServer对象。

    public class HeaderExchanger implements Exchanger {
    
        public static final String NAME = "header";
    
        @Override
        public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
            return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);
        }
    
        @Override
        public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
            return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
        }
    
    }
    

    Transporters

    此处的Transporters 是一个门面模式。getTransporter获取的实例对象为NettyTransporter。调用NettyTransporter的bind方法

    /**
     * Transporter facade. (API, Static, ThreadSafe)
     */
    public class Transporters {
    
        public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
            if (url == null) {
                throw new IllegalArgumentException("url == null");
            }
            if (handlers == null || handlers.length == 0) {
                throw new IllegalArgumentException("handlers == null");
            }
            ChannelHandler handler;
            if (handlers.length == 1) {
                handler = handlers[0];
            } else {
                handler = new ChannelHandlerDispatcher(handlers);
            }
            return getTransporter().bind(url, handler);
        }
    
        public static Transporter getTransporter() {
            return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
        }
    }
    

    NettyTransporter

    public class NettyTransporter implements Transporter {
    
        public static final String NAME = "netty4";
    
        @Override
        public Server bind(URL url, ChannelHandler listener) throws RemotingException {
            return new NettyServer(url, listener);
        }
    
        @Override
        public Client connect(URL url, ChannelHandler listener) throws RemotingException {
            return new NettyClient(url, listener);
        }
    }
    

    NettyServer开启NettyServer

    public class NettyServer extends AbstractServer implements Server {
    
        public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
            super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
        }
    
        protected void doOpen() throws Throwable {
            NettyHelper.setNettyLoggerFactory();
    
            bootstrap = new ServerBootstrap();
    
            bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("NettyServerBoss", true));
            workerGroup = new NioEventLoopGroup(getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
                    new DefaultThreadFactory("NettyServerWorker", true));
    
            final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
            channels = nettyServerHandler.getChannels();
    
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
                    .childOption(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
                    .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) throws Exception {
                            NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
                            ch.pipeline()//.addLast("logging",new LoggingHandler(LogLevel.INFO))//for debug
                                    .addLast("decoder", adapter.getDecoder())
                                    .addLast("encoder", adapter.getEncoder())
                                    .addLast("handler", nettyServerHandler);
                        }
                    });
            // bind
            ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
            channelFuture.syncUninterruptibly();
            channel = channelFuture.channel();
    
        }
    }
    
    public abstract class AbstractServer extends AbstractEndpoint implements Server {
        public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
            super(url, handler);
            localAddress = getUrl().toInetSocketAddress();
    
            String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());
            int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort());
            if (url.getParameter(Constants.ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {
                bindIp = NetUtils.ANYHOST;
            }
            bindAddress = new InetSocketAddress(bindIp, bindPort);
            this.accepts = url.getParameter(Constants.ACCEPTS_KEY, Constants.DEFAULT_ACCEPTS);
            this.idleTimeout = url.getParameter(Constants.IDLE_TIMEOUT_KEY, Constants.DEFAULT_IDLE_TIMEOUT);
            try {
                doOpen();
                if (logger.isInfoEnabled()) {
                    logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
                }
            } catch (Throwable t) {
                throw new RemotingException(url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName()
                        + " on " + getLocalAddress() + ", cause: " + t.getMessage(), t);
            }
            //fixme replace this with better method
            DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension();
            executor = (ExecutorService) dataStore.get(Constants.EXECUTOR_SERVICE_COMPONENT_KEY, Integer.toString(url.getPort()));
        }
        protected abstract void doOpen() throws Throwable;
    
        protected abstract void doClose() throws Throwable;
    }
    

    HeaderExchangeServer头部交换服务器

    根据nettyServer,包装成一个HeaderExchangeServer服务器。heartbeat 默认601000s;heartbeatTimeout 为3heartbeat ;成员变量赋值之后,开启了一个心跳检测定时器。

    public class HeaderExchangeServer implements ExchangeServer {
        private ScheduledFuture<?> heartbeatTimer;
        // heartbeat timeout (ms), default value is 0 , won't execute a heartbeat.
        private int heartbeat;
        private int heartbeatTimeout;
        public HeaderExchangeServer(Server server) {
            if (server == null) {
                throw new IllegalArgumentException("server == null");
            }
            this.server = server;
            this.heartbeat = server.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);
            this.heartbeatTimeout = server.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);
            if (heartbeatTimeout < heartbeat * 2) {
                throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
            }
            startHeartbeatTimer();
        }
    
        private void startHeartbeatTimer() {
            stopHeartbeatTimer();
            if (heartbeat > 0) {
                heartbeatTimer = scheduled.scheduleWithFixedDelay(
                        new HeartBeatTask(new HeartBeatTask.ChannelProvider() {
                            @Override
                            public Collection<Channel> getChannels() {
                                return Collections.unmodifiableCollection(
                                        HeaderExchangeServer.this.getChannels());
                            }
                        }, heartbeat, heartbeatTimeout),
                        heartbeat, heartbeat, TimeUnit.MILLISECONDS);
            }
        }
    
        private void stopHeartbeatTimer() {
            try {
                ScheduledFuture<?> timer = heartbeatTimer;
                if (timer != null && !timer.isCancelled()) {
                    timer.cancel(true);
                }
            } catch (Throwable t) {
                logger.warn(t.getMessage(), t);
            } finally {
                heartbeatTimer = null;
            }
        }
    }
    
  • 相关阅读:
    SpringBoot Maven项目 Helloworld 测试
    Oracle client安装教程
    quartz定时任务时间设置
    maven导出项目依赖的jar包
    Java 集合类
    Webservice客户端动态调用服务端功能方法
    使用Eclipse自带的Axis1插件生成Web Service服务端客户端
    SpringBatch Sample (五)(复合格式文件的读、多文件的写)
    Mysql性能分析
    设置nginx中文件上传的大小限制度
  • 原文地址:https://www.cnblogs.com/nangonghui/p/15796048.html
Copyright © 2020-2023  润新知