dubbo使用版本为2.7.6。

一、介绍

本文主要基于dubbo提供的demo运行过程介绍一些dubbo服务方的启动过程,后面会结合服务消费方对整个调用过程进行一次整合。

二、服务介绍

dubbo应用怎么启动的 dubbo启动方式_提供方

 

以官方提供的demo为例子,我们选择dubbo-demo-api,其中有两个module,分别是提供方和消费方,这两个demo采用的是api的方式注册和调用服务的,而不是大家比较熟悉的xml方式。

如下是服务提供方的服务:

public class DemoServiceImpl implements DemoService {
    private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);

    @Override
    public String sayHello(String name) {
        logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
        return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
    }

    @Override
    public CompletableFuture<String> sayHelloAsync(String name) {
        return null;
    }

}

三、服务提供方启动过程

 1.启动类

public class Application {
    public static void main(String[] args) throws Exception {
        if (isClassic(args)) {
            startWithExport();
        } else {
            startWithBootstrap();
        }
    }

    private static boolean isClassic(String[] args) {
        return args.length > 0 && "classic".equalsIgnoreCase(args[0]);
    }

   //调用该方法
    private static void startWithBootstrap() {
        ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>();
        service.setInterface(DemoService.class);
        service.setRef(new DemoServiceImpl());

        DubboBootstrap bootstrap = DubboBootstrap.getInstance();
        bootstrap.application(new ApplicationConfig("dubbo-demo-api-provider"))//应用名称
                .registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))//zk注册地址
                .service(service)//制定要注册的服务
                .start()//提供方启动
                .await();
    }

    private static void startWithExport() throws InterruptedException {
        ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>();
        service.setInterface(DemoService.class);
        service.setRef(new DemoServiceImpl());
        service.setApplication(new ApplicationConfig("dubbo-demo-api-provider"));
        service.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
        service.export();

        System.out.println("dubbo service started");
        new CountDownLatch(1).await();
    }
}

如上所示,在设定了相关必要信息之后,调用了DubboBootstrap.start()方法开始启动服务提供方了:

public DubboBootstrap start() {
        if (started.compareAndSet(false, true)) {
            ready.set(false);
            initialize();//主要初始化配置中心和获取远程配置
            if (logger.isInfoEnabled()) {
                logger.info(NAME + " is starting...");
            }
            // 1. export Dubbo Services
            exportServices();//服务暴露,并启动开启服务

            // Not only provider register
            if (!isOnlyRegisterProvider() || hasExportedServices()) {
                // 2. export MetadataService
                exportMetadataService();
                //3. Register the local ServiceInstance if required
                registerServiceInstance();
            }

            referServices();//服务引用,这里消费方那里会重点介绍
            if (asyncExportingFutures.size() > 0) {
                new Thread(() -> {
                    try {
                        this.awaitFinish();
                    } catch (Exception e) {
                        logger.warn(NAME + " exportAsync occurred an exception.");
                    }
                    ready.set(true);
                    if (logger.isInfoEnabled()) {
                        logger.info(NAME + " is ready.");
                    }
                }).start();
            } else {
                ready.set(true);
                if (logger.isInfoEnabled()) {
                    logger.info(NAME + " is ready.");
                }
            }
            if (logger.isInfoEnabled()) {
                logger.info(NAME + " has started.");
            }
        }
        return this;
    }

我们来到exportServices()方法:

private void exportServices() {
        configManager.getServices().forEach(sc -> {
            // TODO, compatible with ServiceConfig.export()
            ServiceConfig serviceConfig = (ServiceConfig) sc;
            serviceConfig.setBootstrap(this);

            if (exportAsync) {
                ExecutorService executor = executorRepository.getServiceExporterExecutor();
                Future<?> future = executor.submit(() -> {
                    sc.export();
                    exportedServices.add(sc);
                });
                asyncExportingFutures.add(future);
            } else {
                sc.export();//没有配置exportAsync,这里直接调用ServiceConfig的export()方法
                exportedServices.add(sc);
            }
        });
    }
查看ServiceConfig的export()方法:
public synchronized void export() {
        if (!shouldExport()) {
            return;
        }

        if (bootstrap == null) {
            bootstrap = DubboBootstrap.getInstance();
            bootstrap.init();
        }

        checkAndUpdateSubConfigs();

        //init serviceMetadata
        serviceMetadata.setVersion(version);
        serviceMetadata.setGroup(group);
        serviceMetadata.setDefaultGroup(group);
        serviceMetadata.setServiceType(getInterfaceClass());
        serviceMetadata.setServiceInterfaceName(getInterface());
        serviceMetadata.setTarget(getRef());

        if (shouldDelay()) {
            DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
        } else {
            doExport();
        }

        exported();
    }

 上面封装了serviceMetadata(元数据),可以后面注册到元数据中心,这里不细说了,接下来调用的是doExport()-->doExportUrls():

private void doExportUrls() {
        ServiceRepository repository = ApplicationModel.getServiceRepository();
        ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
     //服务信息存储到内存中
        repository.registerProvider(
                getUniqueServiceName(),
                ref,
                serviceDescriptor,
                this,
                serviceMetadata
        );

     //封装要注册服务的URL
        List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);

        //对每一个支持的协议进行服务暴露
        for (ProtocolConfig protocolConfig : protocols) {
            String pathKey = URL.buildKey(getContextPath(protocolConfig)
                    .map(p -> p + "/" + path)
                    .orElse(path), group, version);
            // In case user specified path, register service one more time to map it to path.
            repository.registerService(pathKey, interfaceClass);
            // TODO, uncomment this line once service key is unified
            serviceMetadata.setServiceKey(pathKey);
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);//实际的服务暴露
        }
    }

来到ServiceConfig.doExportUrlsFor1Protocol():

1     private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
  2         String name = protocolConfig.getName();
  3         if (StringUtils.isEmpty(name)) {
  4             name = DUBBO;
  5         }
  6 
  7         Map<String, String> map = new HashMap<String, String>();
  8         map.put(SIDE_KEY, PROVIDER_SIDE);
  9 
 10         ServiceConfig.appendRuntimeParameters(map);
 11         AbstractConfig.appendParameters(map, getMetrics());
 12         AbstractConfig.appendParameters(map, getApplication());
 13         AbstractConfig.appendParameters(map, getModule());
 14         // remove 'default.' prefix for configs from ProviderConfig
 15         // appendParameters(map, provider, Constants.DEFAULT_KEY);
 16         AbstractConfig.appendParameters(map, provider);
 17         AbstractConfig.appendParameters(map, protocolConfig);
 18         AbstractConfig.appendParameters(map, this);
 19         MetadataReportConfig metadataReportConfig = getMetadataReportConfig();
 20         if (metadataReportConfig != null && metadataReportConfig.isValid()) {
 21             map.putIfAbsent(METADATA_KEY, REMOTE_METADATA_STORAGE_TYPE);
 22         }
 23         if (CollectionUtils.isNotEmpty(getMethods())) {
 24             for (MethodConfig method : getMethods()) {
 25                 AbstractConfig.appendParameters(map, method, method.getName());
 26                 String retryKey = method.getName() + ".retry";
 27                 if (map.containsKey(retryKey)) {
 28                     String retryValue = map.remove(retryKey);
 29                     if ("false".equals(retryValue)) {
 30                         map.put(method.getName() + ".retries", "0");
 31                     }
 32                 }
 33                 List<ArgumentConfig> arguments = method.getArguments();
 34                 if (CollectionUtils.isNotEmpty(arguments)) {
 35                     for (ArgumentConfig argument : arguments) {
 36                         // convert argument type
 37                         if (argument.getType() != null && argument.getType().length() > 0) {
 38                             Method[] methods = interfaceClass.getMethods();
 39                             // visit all methods
 40                             if (methods.length > 0) {
 41                                 for (int i = 0; i < methods.length; i++) {
 42                                     String methodName = methods[i].getName();
 43                                     // target the method, and get its signature
 44                                     if (methodName.equals(method.getName())) {
 45                                         Class<?>[] argtypes = methods[i].getParameterTypes();
 46                                         // one callback in the method
 47                                         if (argument.getIndex() != -1) {
 48                                             if (argtypes[argument.getIndex()].getName().equals(argument.getType())) {
 49                                                 AbstractConfig.appendParameters(map, argument, method.getName() + "." + argument.getIndex());
 50                                             } else {
 51                                                 throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
 52                                             }
 53                                         } else {
 54                                             // multiple callbacks in the method
 55                                             for (int j = 0; j < argtypes.length; j++) {
 56                                                 Class<?> argclazz = argtypes[j];
 57                                                 if (argclazz.getName().equals(argument.getType())) {
 58                                                     AbstractConfig.appendParameters(map, argument, method.getName() + "." + j);
 59                                                     if (argument.getIndex() != -1 && argument.getIndex() != j) {
 60                                                         throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
 61                                                     }
 62                                                 }
 63                                             }
 64                                         }
 65                                     }
 66                                 }
 67                             }
 68                         } else if (argument.getIndex() != -1) {
 69                             AbstractConfig.appendParameters(map, argument, method.getName() + "." + argument.getIndex());
 70                         } else {
 71                             throw new IllegalArgumentException("Argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>");
 72                         }
 73 
 74                     }
 75                 }
 76             } // end of methods for
 77         }
 78 
 79         if (ProtocolUtils.isGeneric(generic)) {
 80             map.put(GENERIC_KEY, generic);
 81             map.put(METHODS_KEY, ANY_VALUE);
 82         } else {
 83             String revision = Version.getVersion(interfaceClass, version);
 84             if (revision != null && revision.length() > 0) {
 85                 map.put(REVISION_KEY, revision);
 86             }
 87 
 88             String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
 89             if (methods.length == 0) {
 90                 logger.warn("No method found in service interface " + interfaceClass.getName());
 91                 map.put(METHODS_KEY, ANY_VALUE);
 92             } else {
 93                 map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
 94             }
 95         }
 96 
 97         /**
 98          * Here the token value configured by the provider is used to assign the value to ServiceConfig#token
 99          */
100         if(ConfigUtils.isEmpty(token) && provider != null) {
101             token = provider.getToken();
102         }
103 
104         if (!ConfigUtils.isEmpty(token)) {
105             if (ConfigUtils.isDefault(token)) {
106                 map.put(TOKEN_KEY, UUID.randomUUID().toString());
107             } else {
108                 map.put(TOKEN_KEY, token);
109             }
110         }
111         //init serviceMetadata attachments
112         serviceMetadata.getAttachments().putAll(map);
113 
114         // export service
115         String host = findConfigedHosts(protocolConfig, registryURLs, map);
116         Integer port = findConfigedPorts(protocolConfig, name, map);
117         URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
118 
119         // You can customize Configurator to append extra parameters
120         if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
121                 .hasExtension(url.getProtocol())) {
122             url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
123                     .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
124         }
125 
126         String scope = url.getParameter(SCOPE_KEY);//这里获取URL中的scope参数,来指定用何种暴露方式,如果没有配置,则本地和远程都暴露
127         // don't export when none is configured
128         if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
129 
130             // export to local if the config is not remote (export to remote only when config is remote)
131             if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
132                 exportLocal(url);//服务本地暴露
133             }
134             // export to remote if the config is not local (export to local only when config is local)
135             if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
136                 if (CollectionUtils.isNotEmpty(registryURLs)) {
137                     for (URL registryURL : registryURLs) {
138                         //if protocol is only injvm ,not register
139                         if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
140                             continue;
141                         }
142                         url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
143                         URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL);
144                         if (monitorUrl != null) {
145                             url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
146                         }
147                         if (logger.isInfoEnabled()) {
148                             if (url.getParameter(REGISTER_KEY, true)) {
149                                 logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
150                             } else {
151                                 logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
152                             }
153                         }
154 
155                         // For providers, this is used to enable custom proxy to generate invoker
156                         String proxy = url.getParameter(PROXY_KEY);
157                         if (StringUtils.isNotEmpty(proxy)) {
158                             registryURL = registryURL.addParameter(PROXY_KEY, proxy);
159                         }
160                //获取所支持协议对应的Invoker
161                         Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
162                         Exporter<?> exporter = PROTOCOL.export(invoker);//调用Protocol实现的export()
163                         exporters.add(exporter);
164                     }
165                 } else {
166                     if (logger.isInfoEnabled()) {
167                         logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
168                     }
169                     Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
170                     Exporter<?> exporter = PROTOCOL.export(invoker);
171                     exporters.add(exporter);
172                 }
173                 /**
174                  * @since 2.7.0
175                  * ServiceData Store
176                  */
177                 WritableMetadataService metadataService = WritableMetadataService.getExtension(url.getParameter(METADATA_KEY, DEFAULT_METADATA_STORAGE_TYPE));
178                 if (metadataService != null) {
179                     metadataService.publishServiceDefinition(url);
180                 }
181             }
182         }
183         this.urls.add(url);
184     }

注:上面第161行调用了ProxyFactory$Adaptive.java(运行过程中由dubbo自动生产的类)的getInvoker()方法来获取接口对应的代理Invoker(默认用的是javassist代理):

public class ProxyFactory$Adaptive implements org.apache.dubbo.rpc.ProxyFactory {
   ... 
  public org.apache.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, org.apache.dubbo.common.URL arg2) throws org.apache.dubbo.rpc.RpcException {
        if (arg2 == null) throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg2;
        String extName = url.getParameter("proxy", "javassist");//默认用的是javassist代理
        if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
        org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
        return extension.getInvoker(arg0, arg1, arg2);
    }
    ...
}

再回到ServiceConfig.doExportUrlsFor1Protocol(),第162行中调用了实现Proctol接口协议的export()方法:

 

 

 

 

dubbo应用怎么启动的 dubbo启动方式_bootstrap_02

 

 

上面方法中获取了URL中的protocol属性值,这里值为registry,对应的Protocol实现为RegistryProctol,接下来沿着调用链路会调到RegistryProctol.export():

1     @Override
 2     public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
 3         URL registryUrl = getRegistryUrl(originInvoker);
 4         // url to export locally
 5         URL providerUrl = getProviderUrl(originInvoker);
 6 
 7         // Subscribe the override data
 8         // FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call
 9         //  the same service. Because the subscribed is cached key with the name of the service, it causes the
10         //  subscription information to cover.
11         final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
12         final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
13         overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
14 
15         providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
16         providerUrl = UrlUtils.unmodifiableUrl(providerUrl);
17 
18         //export invoker
19         final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
20 
21         // url to registry
22         final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
23 
24         // decide if we need to delay publish
25         boolean register = providerUrl.getParameter(REGISTER_KEY, true);
26         if (register) {
27             //服务注册
28             register(registryUrl, registeredProviderUrl);
29         }
30 
31         // register stated url on provider model
32         registerStatedUrl(registryUrl, registeredProviderUrl, register);
33 
34         // Deprecated! Subscribe to override rules in 2.6.x or before.
35         final Registry registry = registryFactory.getRegistry(registryUrl);
36         registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
37 
38         exporter.setRegisterUrl(registeredProviderUrl);
39         exporter.setSubscribeUrl(overrideSubscribeUrl);
40 
41         notifyExport(exporter);
42         //Ensure that a new exporter instance is returned every time export
43         return new DestroyableExporter<>(exporter);
44     }

上面第19行调用了doLocalExport()方法,沿着调用链路,最终会通过Protocol$Adaptive获取Protocol的默认实现DubboProtocol。

dubbo应用怎么启动的 dubbo启动方式_bootstrap_03

 

Proctol默认实现是dubbo协议,Proctol接口默认实现是DubboProctol(通过Protocol$Adaptive.export()获取),接下来看看DubboProctol.export()方法:

@Override
    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(STUB_EVENT_KEY, DEFAULT_STUB_EVENT);
        Boolean isCallbackservice = url.getParameter(IS_CALLBACK_SERVICE, false);
        if (isStubSupportEvent && !isCallbackservice) {
            String stubServiceMethods = url.getParameter(STUB_EVENT_METHODS_KEY);
            if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
                if (logger.isWarnEnabled()) {
                    logger.warn(new IllegalStateException("consumer [" + url.getParameter(INTERFACE_KEY) +
                            "], has set stubproxy support event ,but no stub methods founded."));
                }

            }
        }

        openServer(url);//服务提供方Server开启
        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(IS_SERVER_KEY, true);
        if (isServer) {
            ProtocolServer server = serverMap.get(key);
            if (server == null) {
                synchronized (this) {
                    server = serverMap.get(key);
                    if (server == null) {
                        serverMap.put(key, createServer(url));//调用createServer()创建并启动Server
                    }
                }
            } else {
                // server supports reset, use together with override
                server.reset(url);
            }
        }
    }

    private ProtocolServer createServer(URL url) {
        url = URLBuilder.from(url)
                // send readonly event when server closes, it's enabled by default
                .addParameterIfAbsent(CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString())
                // enable heartbeat by default
                .addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT))
                .addParameter(CODEC_KEY, DubboCodec.NAME)
                .build();
        String str = url.getParameter(SERVER_KEY, DEFAULT_REMOTING_SERVER);

        if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
            throw new RpcException("Unsupported server type: " + str + ", url: " + url);
        }

        ExchangeServer server;
        try {
            server = Exchangers.bind(url, requestHandler);//创建并启动Server
        } catch (RemotingException e) {
            throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
        }

        str = url.getParameter(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 new DubboProtocolServer(server);
    }

看看Exchangers.bind()方法:

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);
    }

接下来调用HeaderExchanger.bind():

@Override
    public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
        return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
    }

再往下,来到Transporters.bind():

public static RemotingServer 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();
  }

上面方法中调用了getTransporter()获取Transporter接口的实现,从@SPI("netty")可见,默认实现是Netty。

 

dubbo应用怎么启动的 dubbo启动方式_dubbo应用怎么启动的_04

因此接下来调用Transporter接口的实现NettyTransporter的bind()方法:

/**
 * Default extension of {@link Transporter} using netty4.x.
 */
public class NettyTransporter implements Transporter {

    public static final String NAME = "netty";

    @Override
    public RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException {
        return new NettyServer(url, handler);
    }

    @Override
    public Client connect(URL url, ChannelHandler handler) throws RemotingException {
        return new NettyClient(url, handler);
    }

}

如上所示,在bind()方法中new了一个NettyServer(),级创建并开启了一个NettyServer用于接收服务消费方(NettyClient)对提供方(NettyServer)的服务调用。关于NettyServer启动过程请看我之前的文章。