简介摘要
SOFARPC是蚂蚁金服开源的高可扩展性、高性能、生产级的轻量级Java RPC服务框架,基于Netty网络通信框架提供应用之间的点对点服务调用功能,为应用之间提供点对点远程服务调用功能。SOFARPC服务发布按照编程界面分为两种使用SOFARPC的方式:
1.通过SOFARPC使用:
服务发布过程涉及到RegistryConfig注册中心配置类,ServerConfig 服务运行容器配置类以及ProviderConfig服务发布配置类。
(1)RegistryConfig注册中心配置类
RegistryConfig registryConfig = new RegistryConfig()
.setProtocol("zookeeper")
.setAddress("127.0.0.1:2181")
RegistryConfig表示注册中心,如上声明服务注册中心的地址和端口是127.0.0.1:2181,协议是Zookeeper。
(2)ServerConfig服务运行容器配置类
ServerConfig serverConfig = new ServerConfig()
.setPort(8803)
.setProtocol("bolt");
ServerConfig表示服务运行容器,如上声明一个使用8803端口和bolt 协议的server。
(3)ProviderConfig服务发布配置类
ProviderConfig<HelloWorldService> providerConfig = new ProviderConfig<HelloWorldService>()
.setInterfaceId(HelloWorldService.class.getName())
.setRef(new HelloWorldServiceImpl())
.setServer(serverConfig)
.setRegistry(registryConfig);
providerConfig.export();
ProviderConfig表示服务发布,如上声明服务的接口,实现和该服务运行的server,最终通过export方法将此服务发布出去。
SOFARPC服务发布支持如下特性:
(1)同一服务发布bolt,rest,dubbo多种协议,构建多个ServerConfig设置给ProviderConfig:
List<ServerConfig> serverConfigs = new ArrayList<ServerConfig>();
serverConfigs.add(serverConfigA);
serverConfigs.add(serverConfigB);
providerConfig.setServer(serverConfigs);
(2)同一服务注册多个注册中心,构建多个RegistryConfig设置给 ProviderConfig:
List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
registryConfigs.add(registryA);
registryConfigs.add(registryB);
providerConfig.setRegistry(registryConfigs);
(3)提供MethodConfig进行方法级别参数设置,API方式使用相应的对象set法即可为其设置参数:
MethodConfig methodConfigA = new MethodConfig();
MethodConfig methodConfigB = new MethodConfig();
List<MethodConfig> methodConfigs = new ArrayList<MethodConfig>();
methodConfigs.add(methodConfigA);
methodConfigs.add(methodConfigB);
providerConfig.setMethods(methodConfigs); //服务端设置
2.通过SOFABoot使用:
(1)服务发布使用XML配置通过sofa:service元素表示发布服务,XML配置如下所示就能够发布SOFARPC服务:
<bean id="helloSyncServiceImpl" class="com.alipay.sofa.rpc.samples.invoke.HelloSyncServiceImpl"/>
<sofa:service ref="helloSyncServiceImpl" interface="com.alipay.sofa.rpc.samples.invoke.HelloSyncService">
<sofa:binding.bolt/>
</sofa:service>
如上声明服务实现helloSyncServiceImpl,通过sofa:service元素将该服务发布,其中ref属性表示发布的服务实例,interface属性表示该服务的接口。而sofa:binding.bolt元素表示该服务会提供 bolt 协议调用通道。
当SOFARPC应用启动的时候发现当前应用需要发布RPC服务的话,SOFARPC将这些服务注册到服务注册中心上面。服务发布将该服务实例注册到对应协议的server上,并且发布该服务的元数据信息到注册中心。
一个服务也可以通过多种协议进行发布,并且通过sofa:method元素表示方法级别配置,XML配置如下所示:
<sofa:service ref="helloSyncServiceImpl" interface="com.alipay.sofa.rpc.samples.invoke.HelloSyncService">
<sofa:binding.bolt/>
<sofa:binding.rest/>
<sofa:binding.dubbo/>
</sofa:service>
<sofa:service ref="sampleFacadeImpl" interface="com.alipay.sofa.rpc.bean.SampleFacade">
<sofa:binding.bolt>
<sofa:global-attrs timeout="3000"/>
<sofa:method name="sayName" timeout="2000"/>
</sofa:binding.bolt>
</sofa:service>
(2)服务发布使用注解方式通过@SofaService注解表示发布服务,interfaceType元素指定服务接口,bindings元素指定协议类型(多协议场景使用@SofaServiceBinding注解bindingType元素指定协议类型):
@SofaService(interfaceType = AnnotationService.class, bindings = { @SofaServiceBinding(bindingType = "bolt") })
@Component
public class AnnotationServiceImpl implements AnnotationService {
@Override
public String sayAnnotation(String stirng) {
return stirng;
}
}
源码解析
搭建环境服务发布示例:
package org.alipay.sofa.rpc;
import com.alipay.sofa.rpc.config.ProviderConfig;
import com.alipay.sofa.rpc.config.ServerConfig;
public class RpcServer {
public static void main(String[] args) {
ServerConfig serverConfig = new ServerConfig()
.setProtocol("bolt") // 设置一个协议,默认bolt
.setPort(12800) // 设置一个端口,默认12200
.setDaemon(false); // 非守护线程
ProviderConfig<HelloService> providerConfig = new ProviderConfig<HelloService>()
.setInterfaceId(HelloService.class.getName()) // 指定接口
.setRef(new HelloServiceImpl()) // 指定实现
.setServer(serverConfig); // 指定服务端
providerConfig.export(); // 发布服务
}
}
参考SOFARPC Example示例模块(com.alipay.sofa.rpc.quickstart.QuickStartServer):
package com.alipay.sofa.rpc.quickstart;
import com.alipay.sofa.rpc.config.ProviderConfig;
import com.alipay.sofa.rpc.config.ServerConfig;
/**
* Quick Start Server
*/
public class QuickStartServer {
public static void main(String[] args) {
ServerConfig serverConfig = new ServerConfig()
.setProtocol("bolt") // 设置一个协议,默认bolt
.setPort(12000) // 设置一个端口,默认12200
.setDaemon(false); // 非守护线程
ProviderConfig<HelloService> providerConfig = new ProviderConfig<HelloService>()
.setInterfaceId(HelloService.class.getName()) // 指定接口
.setRef(new HelloServiceImpl()) // 指定实现
.setServer(serverConfig); // 指定服务端
providerConfig.export(); // 发布服务
}
}
运行服务发布端示例类QuickStartServer查看提供端运行效果,服务提供者输出日志如下:
Connected to the target VM, address: '127.0.0.1:59510', transport: 'socket'
2018-05-14 15:34:11,927 main INFO [com.alipay.sofa.rpc.context.RpcRuntimeContext:info:102] - Welcome! Loading SOFA RPC Framework : 5.4.0_20180427231325, PID is:1952
2018-05-14 15:34:14,752 main INFO [com.alipay.sofa.rpc.module.ModuleFactory:info:102] - Install Module: fault-tolerance
2018-05-14 15:34:54,347 main INFO [com.alipay.sofa.rpc.bootstrap.DefaultProviderBootstrap:infoWithApp:122] - Export provider config : com.alipay.sofa.rpc.quickstart.HelloService: with bean id rpc-cfg-0
Sofa-Middleware-Log SLF4J : Actual binding is of type [ com.alipay.remoting Log4j ]
2018-05-14 15:35:59,083 main INFO [com.alipay.sofa.common.log:report:30] - Sofa-Middleware-Log SLF4J : Actual binding is of type [ com.alipay.remoting Log4j ]
参考sofa-rpc-boot-projects范例模块(com.alipay.sofa.rpc.samples.annotation):
package com.alipay.sofa.rpc.samples.annotation;
public interface AnnotationService {
String sayAnnotation(String stirng);
}
package com.alipay.sofa.rpc.samples.annotation;
import com.alipay.sofa.runtime.api.annotation.SofaService;
import com.alipay.sofa.runtime.api.annotation.SofaServiceBinding;
import org.springframework.stereotype.Component;
@SofaService(interfaceType = AnnotationService.class, bindings = { @SofaServiceBinding(bindingType = "bolt") })
@Component
public class AnnotationServiceImpl implements AnnotationService {
@Override
public String sayAnnotation(String stirng) {
return stirng;
}
}
package com.alipay.sofa.rpc.samples.annotation;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ImportResource;
@SpringBootApplication
public class AnnotationServerApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(AnnotationServerApplication.class);
ApplicationContext applicationContext = springApplication.run(args);
}
}
运行服务发布端范例类AnnotationServerApplication查看提供端运行效果,服务提供者输出日志如下:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.4.2.RELEASE)
Sofa-Middleware-Log SLF4J : Actual logging.path is [ ./logs ]
2018-06-06 00:25:01.856 INFO 14648 --- [ main] com.alipay.sofa.common.log : Sofa-Middleware-Log SLF4J : Actual logging.path is [ ./logs ]
Sofa-Middleware-Log SLF4J : Actual binding is of type [ com.alipay.sofa.infra Logback ]
2018-06-06 00:25:02.000 INFO 14648 --- [ main] com.alipay.sofa.common.log : Sofa-Middleware-Log SLF4J : Actual binding is of type [ com.alipay.sofa.infra Logback ]
Sofa-Middleware-Log SLF4J : Actual binding is of type [ com.alipay.sofa.healthcheck Logback ]
2018-06-06 00:25:02.265 INFO 14648 --- [ main] com.alipay.sofa.common.log : Sofa-Middleware-Log SLF4J : Actual binding is of type [ com.alipay.sofa.healthcheck Logback ]
2018-06-06 00:25:02.512 INFO 14648 --- [ main] c.a.s.r.s.a.AnnotationServerApplication : Starting AnnotationServerApplication on Program with PID 14648 (D:\Program\Github\sofa-rpc-boot-projects\sofa-boot-samples\target\classes started by Administrator in D:\Program\Github\sofa-rpc-boot-projects)
2018-06-06 00:25:02.514 INFO 14648 --- [ main] c.a.s.r.s.a.AnnotationServerApplication : No active profile set, falling back to default profiles: default
2018-06-06 00:25:02.836 INFO 14648 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@4c12331b: startup date [Wed Jun 06 00:25:02 CST 2018]; root of context hierarchy
2018-06-06 00:25:07.133 INFO 14648 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'com.alipay.sofa.runtime.spring.configuration.SofaRuntimeAutoConfiguration' of type [class com.alipay.sofa.runtime.spring.configuration.SofaRuntimeAutoConfiguration$$EnhancerBySpringCGLIB$$75b40bdf] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2018-06-06 00:25:07.254 INFO 14648 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'bindingConverterFactory' of type [class com.alipay.sofa.runtime.service.impl.BindingConverterFactoryImpl] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2018-06-06 00:25:07.292 INFO 14648 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'bindingAdapterFactory' of type [class com.alipay.sofa.runtime.service.impl.BindingAdapterFactoryImpl] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2018-06-06 00:25:07.311 INFO 14648 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'sofaRuntimeProperties' of type [class com.alipay.sofa.runtime.spring.config.SofaRuntimeProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2018-06-06 00:25:07.336 INFO 14648 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'sofaRuntimeContext' of type [class com.alipay.sofa.runtime.spi.component.SofaRuntimeContext] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2018-06-06 00:25:09.229 INFO 14648 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2018-06-06 00:25:09.253 INFO 14648 --- [ main] o.apache.catalina.core.StandardService : Starting service Tomcat
2018-06-06 00:25:09.256 INFO 14648 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.6
2018-06-06 00:25:10.000 INFO 14648 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2018-06-06 00:25:10.001 INFO 14648 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 7165 ms
2018-06-06 00:25:10.773 INFO 14648 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2018-06-06 00:25:10.785 INFO 14648 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'metricsFilter' to: [/*]
2018-06-06 00:25:10.786 INFO 14648 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-06-06 00:25:10.786 INFO 14648 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-06-06 00:25:10.787 INFO 14648 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-06-06 00:25:10.787 INFO 14648 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
2018-06-06 00:25:10.787 INFO 14648 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'webRequestLoggingFilter' to: [/*]
2018-06-06 00:25:10.788 INFO 14648 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'applicationContextIdFilter' to: [/*]
Sofa-Middleware-Log SLF4J : Actual binding is of type [ com.alipay.sofa.runtime Logback ]
2018-06-06 00:25:11.020 INFO 14648 --- [ main] com.alipay.sofa.common.log : Sofa-Middleware-Log SLF4J : Actual binding is of type [ com.alipay.sofa.runtime Logback ]
Sofa-Middleware-Log SLF4J : Actual binding is of type [ com.alipay.sofa.rpc Logback ]
2018-06-06 00:25:11.560 INFO 14648 --- [ main] com.alipay.sofa.common.log : Sofa-Middleware-Log SLF4J : Actual binding is of type [ com.alipay.sofa.rpc Logback ]
2018-06-06 00:25:12.044 INFO 14648 --- [ main] o.a.c.f.imps.CuratorFrameworkImpl : Starting
2018-06-06 00:25:12.060 INFO 14648 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:zookeeper.version=3.4.6-1569965, built on 02/20/2014 09:09 GMT
2018-06-06 00:25:12.061 INFO 14648 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:host.name=Program
2018-06-06 00:25:12.061 INFO 14648 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:java.version=1.8.0_141
2018-06-06 00:25:12.061 INFO 14648 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:java.vendor=Oracle Corporation
2018-06-06 00:25:12.061 INFO 14648 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:java.home=C:\Program Files\Java\jdk1.8.0_141\jre
2018-06-06 00:25:12.062 INFO 14648 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:java.class.path=C:\Program Files\Java\jdk1.8.0_141\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\rt.jar;D:\Program\Github\sofa-rpc-boot-projects\sofa-boot-samples\target\classes;D:\Program\Github\sofa-rpc-boot-projects\sofa-boot-starter\target\classes;C:\Users\Administrator\.m2\repository\com\alipay\sofa\sofa-rpc-all\5.4.0\sofa-rpc-all-5.4.0.jar;C:\Users\Administrator\.m2\repository\com\alipay\sofa\bolt\1.4.1\bolt-1.4.1.jar;C:\Users\Administrator\.m2\repository\org\slf4j\slf4j-api\1.7.21\slf4j-api-1.7.21.jar;C:\Users\Administrator\.m2\repository\io\netty\netty-all\4.1.25.Final\netty-all-4.1.25.Final.jar;C:\Users\Administrator\.m2\repository\com\alipay\sofa\hessian\3.3.0\hessian-3.3.0.jar;C:\Users\Administrator\.m2\repository\com\alipay\sofa\tracer-core\2.1.1\tracer-core-2.1.1.jar;C:\Users\Administrator\.m2\repository\io\opentracing\opentracing-api\0.22.0\opentracing-api-0.22.0.jar;C:\Users\Administrator\.m2\repository\io\opentracing\opentracing-noop\0.22.0\opentracing-noop-0.22.0.jar;C:\Users\Administrator\.m2\repository\io\opentracing\opentracing-mock\0.22.0\opentracing-mock-0.22.0.jar;C:\Users\Administrator\.m2\repository\io\opentracing\opentracing-util\0.22.0\opentracing-util-0.22.0.jar;C:\Users\Administrator\.m2\repository\com\alipay\sofa\lookout\lookout-api\1.4.0\lookout-api-1.4.0.jar;C:\Users\Administrator\.m2\repository\com\alipay\sofa\runtime-sofa-boot-starter\2.4.0\runtime-sofa-boot-starter-2.4.0.jar;C:\Users\Administrator\.m2\repository\org\apache\curator\curator-client\2.9.1\curator-client-2.9.1.jar;C:\Users\Administrator\.m2\repository\org\apache\curator\curator-framework\2.9.1\curator-framework-2.9.1.jar;C:\Users\Administrator\.m2\repository\org\apache\curator\curator-recipes\2.9.1\curator-recipes-2.9.1.jar;C:\Users\Administrator\.m2\repository\org\jboss\resteasy\resteasy-jaxrs\3.0.12.Final\resteasy-jaxrs-3.0.12.Final.jar;C:\Users\Administrator\.m2\repository\org\jboss\spec\javax\annotation\jboss-annotations-api_1.1_spec\1.0.1.Final\jboss-annotations-api_1.1_spec-1.0.1.Final.jar;C:\Users\Administrator\.m2\repository\javax\activation\activation\1.1.1\activation-1.1.1.jar;C:\Users\Administrator\.m2\repository\org\apache\httpcomponents\httpclient\4.3.6\httpclient-4.3.6.jar;C:\Users\Administrator\.m2\repository\org\apache\httpcomponents\httpcore\4.3.3\httpcore-4.3.3.jar;C:\Users\Administrator\.m2\repository\commons-logging\commons-logging\1.1.3\commons-logging-1.1.3.jar;C:\Users\Administrator\.m2\repository\commons-codec\commons-codec\1.6\commons-codec-1.6.jar;C:\Users\Administrator\.m2\repository\commons-io\commons-io\2.1\commons-io-2.1.jar;C:\Users\Administrator\.m2\repository\net\jcip\jcip-annotations\1.0\jcip-annotations-1.0.jar;C:\Users\Administrator\.m2\repository\org\jboss\resteasy\resteasy-client\3.0.12.Final\resteasy-client-3.0.12.Final.jar;C:\Users\Administrator\.m2\repository\org\jboss\resteasy\resteasy-jackson-provider\3.0.12.Final\resteasy-jackson-provider-3.0.12.Final.jar;C:\Users\Administrator\.m2\repository\org\codehaus\jackson\jackson-core-asl\1.9.12\jackson-core-asl-1.9.12.jar;C:\Users\Administrator\.m2\repository\org\codehaus\jackson\jackson-mapper-asl\1.9.12\jackson-mapper-asl-1.9.12.jar;C:\Users\Administrator\.m2\repository\org\codehaus\jackson\jackson-jaxrs\1.9.12\jackson-jaxrs-1.9.12.jar;C:\Users\Administrator\.m2\repository\org\codehaus\jackson\jackson-xc\1.9.12\jackson-xc-1.9.12.jar;C:\Users\Administrator\.m2\repository\org\jboss\resteasy\resteasy-netty4\3.0.12.Final\resteasy-netty4-3.0.12.Final.jar;C:\Users\Administrator\.m2\repository\org\jboss\resteasy\resteasy-validator-provider-11\3.0.12.Final\resteasy-validator-provider-11-3.0.12.Final.jar;C:\Users\Administrator\.m2\repository\org\hibernate\hibernate-validator\5.0.1.Final\hibernate-validator-5.0.1.Final.jar;C:\Users\Administrator\.m2\repository\javax\validation\validation-api\1.1.0.Final\validation-api-1.1.0.Final.jar;C:\Users\Administrator\.m2\repository\org\jboss\logging\jboss-logging\3.1.1.GA\jboss-logging-3.1.1.GA.jar;C:\Users\Administrator\.m2\repository\com\fasterxml\classmate\0.8.0\classmate-0.8.0.jar;C:\Users\Administrator\.m2\repository\org\jboss\resteasy\jaxrs-api\3.0.12.Final\jaxrs-api-3.0.12.Final.jar;C:\Users\Administrator\.m2\repository\org\jboss\resteasy\resteasy-multipart-provider\3.0.12.Final\resteasy-multipart-provider-3.0.12.Final.jar;C:\Users\Administrator\.m2\repository\org\jboss\resteasy\resteasy-jaxb-provider\3.0.12.Final\resteasy-jaxb-provider-3.0.12.Final.jar;C:\Users\Administrator\.m2\repository\com\sun\xml\bind\jaxb-impl\2.2.7\jaxb-impl-2.2.7.jar;C:\Users\Administrator\.m2\repository\com\sun\xml\bind\jaxb-core\2.2.7\jaxb-core-2.2.7.jar;C:\Users\Administrator\.m2\repository\javax\xml\bind\jaxb-api\2.2.7\jaxb-api-2.2.7.jar;C:\Users\Administrator\.m2\repository\com\sun\istack\istack-commons-runtime\2.16\istack-commons-runtime-2.16.jar;C:\Users\Administrator\.m2\repository\com\sun\xml\fastinfoset\FastInfoset\1.2.12\FastInfoset-1.2.12.jar;C:\Users\Administrator\.m2\repository\javax\xml\bind\jsr173_api\1.0\jsr173_api-1.0.jar;C:\Users\Administrator\.m2\repository\javax\mail\mail\1.5.0-b01\mail-1.5.0-b01.jar;C:\Users\Administrator\.m2\repository\org\apache\james\apache-mime4j\0.6\apache-mime4j-0.6.jar;C:\Users\Administrator\.m2\repository\javax\el\javax.el-api\2.2.5\javax.el-api-2.2.5.jar;C:\Users\Administrator\.m2\repository\org\glassfish\web\javax.el\2.2.6\javax.el-2.2.6.jar;C:\Users\Administrator\.m2\repository\com\alibaba\dubbo\2.4.10\dubbo-2.4.10.jar;C:\Users\Administrator\.m2\repository\org\jboss\netty\netty\3.2.5.Final\netty-3.2.5.Final.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-beans\4.3.4.RELEASE\spring-beans-4.3.4.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-core\4.3.4.RELEASE\spring-core-4.3.4.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\1.4.2.RELEASE\spring-boot-autoconfigure-1.4.2.RELEASE.jar;C:\Users\Administrator\.m2\repository\com\alipay\sofa\test-sofa-boot-starter\2.4.0\test-sofa-boot-starter-2.4.0.jar;C:\Users\Administrator\.m2\repository\com\alipay\sofa\healthcheck-sofa-boot-starter\2.4.0\healthcheck-sofa-boot-starter-2.4.0.jar;C:\Users\Administrator\.m2\repository\com\alipay\sofa\infra-sofa-boot-starter\2.4.0\infra-sofa-boot-starter-2.4.0.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-context\4.3.4.RELEASE\spring-context-4.3.4.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-aop\4.3.4.RELEASE\spring-aop-4.3.4.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-expression\4.3.4.RELEASE\spring-expression-4.3.4.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot\1.4.2.RELEASE\spring-boot-1.4.2.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter-actuator\1.4.2.RELEASE\spring-boot-starter-actuator-1.4.2.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter\1.4.2.RELEASE\spring-boot-starter-1.4.2.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter-logging\1.4.2.RELEASE\spring-boot-starter-logging-1.4.2.RELEASE.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-classic\1.1.7\logback-classic-1.1.7.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-core\1.1.7\logback-core-1.1.7.jar;C:\Users\Administrator\.m2\repository\org\slf4j\jcl-over-slf4j\1.7.21\jcl-over-slf4j-1.7.21.jar;C:\Users\Administrator\.m2\repository\org\slf4j\jul-to-slf4j\1.7.21\jul-to-slf4j-1.7.21.jar;C:\Users\Administrator\.m2\repository\org\slf4j\log4j-over-slf4j\1.7.21\log4j-over-slf4j-1.7.21.jar;C:\Users\Administrator\.m2\repository\org\yaml\snakeyaml\1.17\snakeyaml-1.17.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-actuator\1.4.2.RELEASE\spring-boot-actuator-1.4.2.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter-web\1.4.2.RELEASE\spring-boot-starter-web-1.4.2.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter-tomcat\1.4.2.RELEASE\spring-boot-starter-tomcat-1.4.2.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\apache\tomcat\embed\tomcat-embed-core\8.5.6\tomcat-embed-core-8.5.6.jar;C:\Users\Administrator\.m2\repository\org\apache\tomcat\embed\tomcat-embed-el\8.5.6\tomcat-embed-el-8.5.6.jar;C:\Users\Administrator\.m2\repository\org\apache\tomcat\embed\tomcat-embed-websocket\8.5.6\tomcat-embed-websocket-8.5.6.jar;C:\Users\Administrator\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.8.4\jackson-databind-2.8.4.jar;C:\Users\Administrator\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.8.0\jackson-annotations-2.8.0.jar;C:\Users\Administrator\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.8.4\jackson-core-2.8.4.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-web\4.3.4.RELEASE\spring-web-4.3.4.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-webmvc\4.3.4.RELEASE\spring-webmvc-4.3.4.RELEASE.jar;C:\Users\Administrator\.m2\repository\com\alibaba\fastjson\1.2.47\fastjson-1.2.47.jar;C:\Users\Administrator\.m2\repository\com\alipay\sofa\common\sofa-common-tools\1.0.12\sofa-common-tools-1.0.12.jar;C:\Users\Administrator\.m2\repository\org\apache\curator\curator-test\2.9.1\curator-test-2.9.1.jar;C:\Users\Administrator\.m2\repository\org\apache\zookeeper\zookeeper\3.4.6\zookeeper-3.4.6.jar;C:\Users\Administrator\.m2\repository\log4j\log4j\1.2.16\log4j-1.2.16.jar;C:\Users\Administrator\.m2\repository\jline\jline\0.9.94\jline-0.9.94.jar;C:\Users\Administrator\.m2\repository\io\netty\netty\3.7.0.Final\netty-3.7.0.Final.jar;C:\Users\Administrator\.m2\repository\org\javassist\javassist\3.18.1-GA\javassist-3.18.1-GA.jar;C:\Users\Administrator\.m2\repository\org\apache\commons\commons-math\2.2\commons-math-2.2.jar;C:\Users\Administrator\.m2\repository\com\google\guava\guava\16.0.1\guava-16.0.1.jar;C:\Users\Administrator\.m2\repository\com\101tec\zkclient\0.10\zkclient-0.10.jar;D:\Program\JetBrains\IntelliJ\lib\idea_rt.jar
2018-06-06 00:25:12.066 INFO 14648 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:java.library.path=C:\Program Files\Java\jdk1.8.0_141\bin;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\ProgramData\Oracle\Java\javapath;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files\Java\jdk1.8.0_141\bin;C:\Program Files\Java\jdk1.8.0_141\jre\bin;C:\Program Files\Maven\bin;C:\Program Files (x86)\Git\cmd;C:\Program Files\MySQL\MySQL Utilities 1.6\;C:\Users\Administrator\AppData\Local\Microsoft\WindowsApps;;C:\Program Files\Mercurial;.
2018-06-06 00:25:12.066 INFO 14648 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:java.io.tmpdir=C:\Users\ADMINI~1\AppData\Local\Temp\
2018-06-06 00:25:12.066 INFO 14648 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:java.compiler=<NA>
2018-06-06 00:25:12.067 INFO 14648 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:os.name=Windows 10
2018-06-06 00:25:12.067 INFO 14648 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:os.arch=amd64
2018-06-06 00:25:12.067 INFO 14648 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:os.version=10.0
2018-06-06 00:25:12.067 INFO 14648 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:user.name=Administrator
2018-06-06 00:25:12.067 INFO 14648 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:user.home=C:\Users\Administrator
2018-06-06 00:25:12.067 INFO 14648 --- [ main] org.apache.zookeeper.ZooKeeper : Client environment:user.dir=D:\Program\Github\sofa-rpc-boot-projects
2018-06-06 00:25:12.070 INFO 14648 --- [ main] org.apache.zookeeper.ZooKeeper : Initiating client connection, connectString=127.0.0.1:2181 sessionTimeout=60000 watcher=org.apache.curator.ConnectionState@4d0753c9
2018-06-06 00:25:12.118 INFO 14648 --- [127.0.0.1:2181)] org.apache.zookeeper.ClientCnxn : Opening socket connection to server 127.0.0.1/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error)
2018-06-06 00:25:12.121 INFO 14648 --- [127.0.0.1:2181)] org.apache.zookeeper.ClientCnxn : Socket connection established to 127.0.0.1/127.0.0.1:2181, initiating session
2018-06-06 00:25:12.265 INFO 14648 --- [127.0.0.1:2181)] org.apache.zookeeper.ClientCnxn : Session establishment complete on server 127.0.0.1/127.0.0.1:2181, sessionid = 0x10005c434e80000, negotiated timeout = 40000
2018-06-06 00:25:12.277 INFO 14648 --- [ain-EventThread] o.a.c.f.state.ConnectionStateManager : State change: CONNECTED
2018-06-06 00:25:12.352 WARN 14648 --- [ main] org.apache.curator.utils.ZKPaths : The version of ZooKeeper being used doesn't support Container nodes. CreateMode.PERSISTENT will be used instead.
Sofa-Middleware-Log SLF4J : Actual binding is of type [ com.alipay.sofa.rpc.boot Logback ]
2018-06-06 00:25:13.094 INFO 14648 --- [ main] com.alipay.sofa.common.log : Sofa-Middleware-Log SLF4J : Actual binding is of type [ com.alipay.sofa.rpc.boot Logback ]
2018-06-06 00:25:14.686 INFO 14648 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@4c12331b: startup date [Wed Jun 06 00:25:02 CST 2018]; root of context hierarchy
2018-06-06 00:25:14.901 INFO 14648 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-06-06 00:25:14.904 INFO 14648 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2018-06-06 00:25:15.002 INFO 14648 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-06-06 00:25:15.003 INFO 14648 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-06-06 00:25:15.108 INFO 14648 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-06-06 00:25:16.177 INFO 14648 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/sofaboot/versions || /sofaboot/versions.json],produces=[application/json]}" onto public java.lang.Object com.alipay.sofa.infra.endpoint.SofaBootVersionEndpointMvcAdapter.invoke()
2018-06-06 00:25:16.183 INFO 14648 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/env/{name:.*}],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint.value(java.lang.String)
2018-06-06 00:25:16.184 INFO 14648 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/env || /env.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2018-06-06 00:25:16.186 INFO 14648 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/mappings || /mappings.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2018-06-06 00:25:16.188 INFO 14648 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/dump || /dump.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2018-06-06 00:25:16.190 INFO 14648 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/beans || /beans.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2018-06-06 00:25:16.192 INFO 14648 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/info || /info.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2018-06-06 00:25:16.194 INFO 14648 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/health || /health.json],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint.invoke(java.security.Principal)
2018-06-06 00:25:16.196 INFO 14648 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/heapdump || /heapdump.json],methods=[GET],produces=[application/octet-stream]}" onto public void org.springframework.boot.actuate.endpoint.mvc.HeapdumpMvcEndpoint.invoke(boolean,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws java.io.IOException,javax.servlet.ServletException
2018-06-06 00:25:16.199 INFO 14648 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/configprops || /configprops.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2018-06-06 00:25:16.201 INFO 14648 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/logfile || /logfile.json],methods=[GET || HEAD]}" onto public void org.springframework.boot.actuate.endpoint.mvc.LogFileMvcEndpoint.invoke(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws javax.servlet.ServletException,java.io.IOException
2018-06-06 00:25:16.211 INFO 14648 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/metrics/{name:.*}],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint.value(java.lang.String)
2018-06-06 00:25:16.211 INFO 14648 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/metrics || /metrics.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2018-06-06 00:25:16.213 INFO 14648 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/trace || /trace.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2018-06-06 00:25:16.215 INFO 14648 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/autoconfig || /autoconfig.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2018-06-06 00:25:16.216 INFO 14648 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/health/readiness || /health/readiness.json],produces=[application/json]}" onto public java.lang.Object com.alipay.sofa.healthcheck.service.SofaBootReadinessCheckMvcEndpoint.invoke(java.security.Principal)
2018-06-06 00:25:16.603 INFO 14648 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-06-06 00:25:16.684 INFO 14648 --- [ main] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 0
Sofa-Middleware-Log SLF4J : Actual binding is of type [ com.alipay.remoting Logback ]
2018-06-06 00:25:17.592 INFO 14648 --- [ main] com.alipay.sofa.common.log : Sofa-Middleware-Log SLF4J : Actual binding is of type [ com.alipay.remoting Logback ]
2018-06-06 00:25:19.261 INFO 14648 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2018-06-06 00:25:19.313 INFO 14648 --- [ main] c.a.s.r.s.a.AnnotationServerApplication : Started AnnotationServerApplication in 19.767 seconds (JVM running for 21.37)
SOFARPC服务发布流程:
(1)创建服务运行容器配置类ServerConfig,设置ServerConfig实例监听端口、通信协议以及是否为守护线程(是否hold住端口,true的话随主线程退出而退出,false的话则要主动退出)等基础配置信息,当然ServerConfig除自定义设置基础配置以外,类加载自动加载配置文件获取服务运行容器默认配置:
/**
* 服务端配置
*
*/
public class ServerConfig extends AbstractIdConfig implements Serializable
服务运行容器配置类ServerConfig继承默认配置带ID配置类 AbstractIdConfig,服务端配置ServerConfig空构造器没有自动加载默认配置,如何实现自动通过配置文件加载服务端默认配置信息?默认配置带ID类AbstractIdConfig静态代码块调用全局的运行时上下文类RpcRuntimeContext的now()方法,通过运行时上下文类RpcRuntimeContext静态代码模块在类加载的时候读取配置文件设置服务端类ServerConfig默认配置属性:
static {
RpcRuntimeContext.now();
}
static {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Welcome! Loading SOFA RPC Framework : {}, PID is:{}", Version.BUILD_VERSION, PID);
}
put(RpcConstants.CONFIG_KEY_RPC_VERSION, Version.RPC_VERSION);
// 初始化一些上下文
initContext();
// 初始化其它模块
ModuleFactory.installModules();
// 增加jvm关闭事件
if (RpcConfigs.getOrDefaultValue(RpcOptions.JVM_SHUTDOWN_HOOK, true)) {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("SOFA RPC Framework catch JVM shutdown event, Run shutdown hook now.");
}
destroy(false);
}
}, "SOFA-RPC-ShutdownHook"));
}
}
全局运行时上下文类RpcRuntimeContext首先初始化上下文自动部署的appId、appName、appInsId以及当前所在文件夹地址appPath信息:
/**
* 初始化一些上下文
*/
private static void initContext() {
putIfAbsent(KEY_APPID, RpcConfigs.getOrDefaultValue(APP_ID, null));
putIfAbsent(KEY_APPNAME, RpcConfigs.getOrDefaultValue(APP_NAME, null));
putIfAbsent(KEY_APPINSID, RpcConfigs.getOrDefaultValue(INSTANCE_ID, null));
putIfAbsent(KEY_APPAPTH, System.getProperty("user.dir"));
}
其中通过配置加载器和操作入口RpcConfigs静态代码块在类加载的时候加载rpc-config-default.json、sofa-rpc/rpc-config.json、META-INF/sofa-rpc/rpc-config.json配置文件获取默认配置、自定义配置信息自定义配置信息存储在全部配置CFG:
static {
init(); // 加载配置文件
}
private static void init() {
try {
// loadDefault
String json = FileUtils.file2String(RpcConfigs.class, "rpc-config-default.json", "UTF-8");
Map map = JSON.parseObject(json, Map.class);
CFG.putAll(map);
// loadCustom
loadCustom("sofa-rpc/rpc-config.json");
loadCustom("META-INF/sofa-rpc/rpc-config.json");
// load system properties
CFG.putAll(new HashMap(System.getProperties())); // 注意部分属性可能被覆盖为字符串
} catch (Exception e) {
throw new SofaRpcRuntimeException("Catch Exception when load RpcConfigs", e);
}
}
接着扩展加载器ExtensionLoader根据需要被加载的模块列表遍历判断是否需要加载来安装模板初始化其他模块:
/**
* 加载全部模块
*/
public static void installModules() {
ExtensionLoader<Module> loader = ExtensionLoaderFactory.getExtensionLoader(Module.class);
String moduleLoadList = RpcConfigs.getStringValue(RpcOptions.MODULE_LOAD_LIST);
for (Map.Entry<String, ExtensionClass<Module>> o : loader.getAllExtensions().entrySet()) {
String moduleName = o.getKey();
Module module = o.getValue().getExtInstance();
// judge need load from rpc option
if (needLoad(moduleLoadList, moduleName)) {
// judge need load from implement
if (module.needLoad()) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Install Module: {}", moduleName);
}
module.install();
INSTALLED_MODULES.put(moduleName, module);
} else {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("The module " + moduleName + " does not need to be loaded.");
}
}
} else {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("The module " + moduleName + " is not in the module load list.");
}
}
}
}
最后根据是否主动监听JVM关闭事件(默认为true)增加JVM关闭事件监听执行反注册服务端,关闭启动端口、发布服务、调用服务、注册中心、客户端公共资源,卸载模块、钩子以及清理缓存销毁操作:
private static void destroy(boolean active) {
RpcRunningState.setShuttingDown(true);
for (Destroyable.DestroyHook destroyHook : DESTROY_HOOKS) {
destroyHook.preDestroy();
}
List<ProviderConfig> providerConfigs = new ArrayList<ProviderConfig>();
for (ProviderBootstrap bootstrap : EXPORTED_PROVIDER_CONFIGS) {
providerConfigs.add(bootstrap.getProviderConfig());
}
// 先反注册服务端
List<Registry> registries = RegistryFactory.getRegistries();
if (CommonUtils.isNotEmpty(registries) && CommonUtils.isNotEmpty(providerConfigs)) {
for (Registry registry : registries) {
registry.batchUnRegister(providerConfigs);
}
}
// 关闭启动的端口
ServerFactory.destroyAll();
// 关闭发布的服务
for (ProviderBootstrap bootstrap : EXPORTED_PROVIDER_CONFIGS) {
bootstrap.unExport();
}
// 关闭调用的服务
for (ConsumerBootstrap bootstrap : REFERRED_CONSUMER_CONFIGS) {
ConsumerConfig config = bootstrap.getConsumerConfig();
if (!CommonUtils.isFalse(config.getParameter(RpcConstants.HIDDEN_KEY_DESTROY))) { // 除非不让主动unrefer
bootstrap.unRefer();
}
}
// 关闭注册中心
RegistryFactory.destroyAll();
// 关闭客户端的一些公共资源
ClientTransportFactory.closeAll();
// 卸载模块
if (!RpcRunningState.isUnitTestMode()) {
ModuleFactory.uninstallModules();
}
// 卸载钩子
for (Destroyable.DestroyHook destroyHook : DESTROY_HOOKS) {
destroyHook.postDestroy();
}
// 清理缓存
RpcCacheManager.clearAll();
RpcRunningState.setShuttingDown(false);
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("SOFA RPC Framework has been release all resources {}...",
active ? "actively " : "");
}
}
(2)创建服务发布配置类ProviderConfig,设置ProviderConfig实例接口名称(服务接口:做为服务唯一标识的组成部分)、接口实现类引用以及配置的协议列表即指定服务运行容器配置:
/**
* 服务提供者配置
*
* @param <T> the type parameter
*/
public class ProviderConfig<T> extends AbstractInterfaceConfig<T, ProviderConfig<T>> implements Serializable
服务发布配置类ProviderConfig继承接口级公共配置类AbstractInterfaceConfig,能够通过集成的注册中心更新服务发布接口级别配置例如超时时间、权重等。
(3)服务发布配置类ProviderConfig负责加载更新服务发布接口级配置,根据面向对象单一职责原则,需要绑定服务提供者启动类ProviderBootstrap进行发布服务:
/**
* 发布服务
*/
public synchronized void export() {
if (providerBootstrap == null) {
providerBootstrap = Bootstraps.from(this);
}
providerBootstrap.export();
}
首先判断服务提供者启动类ProviderBootstrap是否为空,通过发布服务辅助工具类Bootstraps根据绑定服务发布配置扩展加载工厂ExtensionLoaderFactory加载初始化ProviderBootstrap实例:
/**
* 发布一个服务
*
* @param providerConfig 服务发布者配置
* @param <T> 接口类型
* @return 发布启动类
*/
public static <T> ProviderBootstrap<T> from(ProviderConfig<T> providerConfig) {
String bootstrap = providerConfig.getBootstrap();
if (StringUtils.isEmpty(bootstrap)) {
// Use default provider bootstrap
bootstrap = RpcConfigs.getStringValue(RpcOptions.DEFAULT_PROVIDER_BOOTSTRAP);
providerConfig.setBootstrap(bootstrap);
}
ProviderBootstrap providerBootstrap = ExtensionLoaderFactory.getExtensionLoader(ProviderBootstrap.class)
.getExtension(bootstrap, new Class[] { ProviderConfig.class }, new Object[] { providerConfig });
return (ProviderBootstrap<T>) providerBootstrap;
}
接着发布服务包装类ProviderBootstrap通过export()方法发布服务,ProviderBootstrap基于Bolt、Rest、Dubbo网络通信协议提供三种协议服务发布实现类:BoltProviderBootstrap、RestProviderBootstrap以及DubboProviderBootstrap。默认服务提供者启动器DefaultProviderBootstrap发布服务export()方法首先判断服务发布延迟时间配置(默认0,配置为-1代表Spring加载完毕)是否大于0,启动线程睡眠指定延迟时间延迟加载,接着调用doExport()方法执行SOFARPC发布服务逻辑:
public void export() {
if (providerConfig.getDelay() > 0) { // 延迟加载,单位毫秒
Thread thread = factory.newThread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(providerConfig.getDelay());
} catch (Throwable ignore) { // NOPMD
}
doExport();
}
});
thread.start();
} else {
doExport();
}
}
private void doExport() {
if (exported) {
return;
}
String key = providerConfig.buildKey();
String appName = providerConfig.getAppName();
// 检查参数
checkParameters();
if (LOGGER.isInfoEnabled(appName)) {
LOGGER.infoWithApp(appName, "Export provider config : {} with bean id {}", key, providerConfig.getId());
}
// 注意同一interface,同一uniqleId,不同server情况
AtomicInteger cnt = EXPORTED_KEYS.get(key); // 计数器
if (cnt == null) { // 没有发布过
cnt = CommonUtils.putToConcurrentMap(EXPORTED_KEYS, key, new AtomicInteger(0));
}
int c = cnt.incrementAndGet();
int maxProxyCount = providerConfig.getRepeatedExportLimit();
if (maxProxyCount > 0) {
if (c > maxProxyCount) {
cnt.decrementAndGet();
// 超过最大数量,直接抛出异常
throw new SofaRpcRuntimeException("Duplicate provider config with key " + key
+ " has been exported more than " + maxProxyCount + " times!"
+ " Maybe it's wrong config, please check it."
+ " Ignore this if you did that on purpose!");
} else if (c > 1) {
if (LOGGER.isInfoEnabled(appName)) {
LOGGER.infoWithApp(appName, "Duplicate provider config with key {} has been exported!"
+ " Maybe it's wrong config, please check it."
+ " Ignore this if you did that on purpose!", key);
}
}
}
try {
// 构造请求调用器
providerProxyInvoker = new ProviderProxyInvoker(providerConfig);
// 初始化注册中心
if (providerConfig.isRegister()) {
List<RegistryConfig> registryConfigs = providerConfig.getRegistry();
if (CommonUtils.isNotEmpty(registryConfigs)) {
for (RegistryConfig registryConfig : registryConfigs) {
RegistryFactory.getRegistry(registryConfig); // 提前初始化Registry
}
}
}
// 将处理器注册到server
List<ServerConfig> serverConfigs = providerConfig.getServer();
for (ServerConfig serverConfig : serverConfigs) {
try {
Server server = serverConfig.buildIfAbsent();
// 注册序列化接口
server.registerProcessor(providerConfig, providerProxyInvoker);
if (serverConfig.isAutoStart()) {
server.start();
}
} catch (SofaRpcRuntimeException e) {
throw e;
} catch (Exception e) {
LOGGER.errorWithApp(appName, "Catch exception when register processor to server: "
+ serverConfig.getId(), e);
}
}
// 注册到注册中心
providerConfig.setConfigListener(new ProviderAttributeListener());
register();
} catch (Exception e) {
cnt.decrementAndGet();
if (e instanceof SofaRpcRuntimeException) {
throw (SofaRpcRuntimeException) e;
} else {
throw new SofaRpcRuntimeException("Build provider proxy error!", e);
}
}
// 记录一些缓存数据
RpcRuntimeContext.cacheProviderConfig(this);
exported = true;
}
默认服务提供者启动器DefaultProviderBootstrap发布服务doExport()方法执行逻辑:
(1)根据服务发布配置ProviderConfig获取服务接口、服务标签构建key以及获取应用名称appName;
(2)调用checkParameters()方法检查参数,包括检查注入的ref是否接口实现类、检查服务运行容器配置是否为空、检查方法及其黑白名单:
/**
* for check fields and parameters of consumer config
*/
protected void checkParameters() {
// 检查注入的ref是否接口实现类
Class proxyClass = providerConfig.getProxyClass();
String key = providerConfig.buildKey();
T ref = providerConfig.getRef();
if (!proxyClass.isInstance(ref)) {
throw ExceptionUtils.buildRuntime("provider.ref",
ref == null ? "null" : ref.getClass().getName(),
"This is not an instance of " + providerConfig.getInterfaceId()
+ " in provider config with key " + key + " !");
}
// server 不能为空
if (CommonUtils.isEmpty(providerConfig.getServer())) {
throw ExceptionUtils.buildRuntime("server", "NULL", "Value of \"server\" is not specified in provider" +
" config with key " + key + " !");
}
checkMethods(proxyClass);
}
/**
* 检查方法,例如方法名、多态(重载)方法
*
* @param itfClass 接口类
*/
protected void checkMethods(Class<?> itfClass) {
ConcurrentHashMap<String, Boolean> methodsLimit = new ConcurrentHashMap<String, Boolean>();
for (Method method : itfClass.getMethods()) {
String methodName = method.getName();
if (methodsLimit.containsKey(methodName)) {
// 重名的方法
if (LOGGER.isWarnEnabled(providerConfig.getAppName())) {
LOGGER.warnWithApp(providerConfig.getAppName(), "Method with same name \"" + itfClass.getName()
+ "." + methodName + "\" exists ! The usage of overloading method in rpc is deprecated.");
}
}
// 判断服务下方法的黑白名单
Boolean include = methodsLimit.get(methodName);
if (include == null) {
include = inList(providerConfig.getInclude(), providerConfig.getExclude(), methodName); // 检查是否在黑白名单中
methodsLimit.putIfAbsent(methodName, include);
}
providerConfig.setMethodsLimit(methodsLimit);
}
}
(3)检验同一个服务(服务接口&服务标签相同)的发布次数是否超过服务引用配置的最大次数,超过最大数量直接抛出异常;
(4)根据服务发布配置构造请求调用器ProviderProxyInvoker,构造服务端调用链最底层是调用过滤器执行链filterChain:
/**
* 构造执行链
*
* @param providerConfig 服务端配置
*/
public ProviderProxyInvoker(ProviderConfig providerConfig) {
this.providerConfig = providerConfig;
// 最底层是调用过滤器
this.filterChain = FilterChain.buildProviderChain(providerConfig,
new ProviderInvoker(providerConfig));
}
服务端调用链入口ProviderProxyInvoker根据服务发布配置通过服务端调用业务实现类ProviderInvoker初始化过滤器包装Invoker对象(隔离Filter和Service的关系,filter是单例)指定无需下一层过滤器,按照自动装载扩展实现逻辑,获取用户new实例方式注入的过滤器(优先级高),判断是否需要排除系统过滤器,解析用户通过别名方式注入的过滤器准备数据,解析自动加载的过滤器加入自定义的过滤器构造执行链:
/**
* 构造服务端的执行链
*
* @param providerConfig provider配置
* @param lastFilter 最后一个filter
* @return filter执行链
*/
public static FilterChain buildProviderChain(ProviderConfig<?> providerConfig, FilterInvoker lastFilter) {
/*
* 例如自动装载扩展 A(a),B(b),C(c) filter=[-a,d] filterRef=[new E, new Exclude(b)]
* 逻辑如下:
* 1.解析config.getFilterRef(),记录E和-b
* 2.解析config.getFilter()字符串,记录 d 和 -a,-b
* 3.再解析自动装载扩展,a,b被排除了,所以拿到c,d
* 4.对c d进行排序
* 5.拿到C、D实现类
* 6.加上自定义,返回C、D、E
*/
// 用户通过自己new实例的方式注入的filter,优先级高
List<Filter> customFilters = providerConfig.getFilterRef() == null ?
new ArrayList<Filter>() : new CopyOnWriteArrayList<Filter>(providerConfig.getFilterRef());
// 先解析是否有特殊处理
HashSet<String> excludes = parseExcludeFilter(customFilters);
// 准备数据:用户通过别名的方式注入的filter,需要解析
List<ExtensionClass<Filter>> extensionFilters = new ArrayList<ExtensionClass<Filter>>();
List<String> filterAliases = providerConfig.getFilter(); //
if (CommonUtils.isNotEmpty(filterAliases)) {
for (String filterAlias : filterAliases) {
if (startsWithExcludePrefix(filterAlias)) { // 排除用的特殊字符
excludes.add(filterAlias.substring(1));
} else {
ExtensionClass<Filter> filter = EXTENSION_LOADER.getExtensionClass(filterAlias);
if (filter != null) {
extensionFilters.add(filter);
}
}
}
}
// 解析自动加载的过滤器
if (!excludes.contains(StringUtils.ALL) && !excludes.contains(StringUtils.DEFAULT)) { // 配了-*和-default表示不加载内置
for (Map.Entry<String, ExtensionClass<Filter>> entry : PROVIDER_AUTO_ACTIVES.entrySet()) {
if (!excludes.contains(entry.getKey())) {
extensionFilters.add(entry.getValue());
}
}
}
excludes = null; // 不需要了
// 按order从小到大排序
if (extensionFilters.size() > 1) {
Collections.sort(extensionFilters, new OrderedComparator<ExtensionClass<Filter>>());
}
List<Filter> actualFilters = new ArrayList<Filter>();
for (ExtensionClass<Filter> extensionFilter : extensionFilters) {
actualFilters.add(extensionFilter.getExtInstance());
}
// 加入自定义的过滤器
actualFilters.addAll(customFilters);
return new FilterChain(actualFilters, lastFilter, providerConfig);
}
/**
* 构造执行链
*
* @param filters 包装过滤器列表
* @param lastInvoker 最终过滤器
* @param config 接口配置
*/
protected FilterChain(List<Filter> filters, FilterInvoker lastInvoker, AbstractInterfaceConfig config) {
// 调用过程外面包装多层自定义filter
// 前面的过滤器在最外层
invokerChain = lastInvoker;
if (CommonUtils.isNotEmpty(filters)) {
loadedFilters = new ArrayList<Filter>();
for (int i = filters.size() - 1; i >= 0; i--) {
try {
Filter filter = filters.get(i);
if (filter.needToLoad(invokerChain)) {
invokerChain = new FilterInvoker(filter, invokerChain, config);
// cache this for filter when async respond
loadedFilters.add(filter);
}
} catch (Exception e) {
LOGGER.error("Error when build filter chain", e);
throw new SofaRpcRuntimeException("Error when build filter chain", e);
}
}
}
}
(5)判断服务配置是否注册提前初始化注册中心,注册中心配置类RegistryConfig根据注册中心配置获取注册中心实现类Registry实例,注册中心实现类Registry包括基于本地文件注册中心LocalRegistry和基于Zookeeper分布式协调系统注册中心ZookeeperRegistry:
/**
* 得到注册中心对象
*
* @param registryConfig RegistryConfig类
* @return Registry实现
*/
public static synchronized Registry getRegistry(RegistryConfig registryConfig) {
if (ALL_REGISTRIES.size() > 3) { // 超过3次 是不是配错了?
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("Size of registry is greater than 3, Please check it!");
}
}
try {
// 注意:RegistryConfig重写了equals方法,如果多个RegistryConfig属性一样,则认为是一个对象
Registry registry = ALL_REGISTRIES.get(registryConfig);
if (registry == null) {
ExtensionClass<Registry> ext = ExtensionLoaderFactory.getExtensionLoader(Registry.class)
.getExtensionClass(registryConfig.getProtocol());
if (ext == null) {
throw ExceptionUtils.buildRuntime("registry.protocol", registryConfig.getProtocol(),
"Unsupported protocol of registry config !");
}
registry = ext.getExtInstance(new Class[] { RegistryConfig.class }, new Object[] { registryConfig });
ALL_REGISTRIES.put(registryConfig, registry);
}
return registry;
} catch (SofaRpcRuntimeException e) {
throw e;
} catch (Throwable e) {
throw new SofaRpcRuntimeException(e.getMessage(), e);
}
}
(6)根据服务发布配置ProviderConfig获取服务运行容器配置列表,遍历服务运行容器配置构建启动服务运行容器Server实例,服务运行容器Server包括基于Bolt协议的BoltServer和基于Rest协议的RestServer:
/**
* 启动服务
*
* @return the server
*/
public synchronized Server buildIfAbsent() {
if (server != null) {
return server;
}
// 提前检查协议+序列化方式
// ConfigValueHelper.check(ProtocolType.valueOf(getProtocol()),
// SerializationType.valueOf(getSerialization()));
server = ServerFactory.getServer(this);
return server;
}
通过服务运行容器工厂ServerFactory的getServer()方法获取启动基于当前服务运行容器配置ServerConfig的服务端Server实例,首先按照服务运行容器配置确定服务端Server的Host和Port,扩展加载器工厂ExtensionLoaderFactory生成服务运行容器Server实例,初始化启动服务端Server,缓存服务端Server到SERVER_MAP:
/**
* 初始化Server实例
*
* @param serverConfig 服务端配置
* @return Server
*/
public synchronized static Server getServer(ServerConfig serverConfig) {
try {
Server server = SERVER_MAP.get(Integer.toString(serverConfig.getPort()));
if (server == null) {
// 算下网卡和端口
resolveServerConfig(serverConfig);
ExtensionClass<Server> ext = ExtensionLoaderFactory.getExtensionLoader(Server.class)
.getExtensionClass(serverConfig.getProtocol());
if (ext == null) {
throw ExceptionUtils.buildRuntime("server.protocol", serverConfig.getProtocol(),
"Unsupported protocol of server!");
}
server = ext.getExtInstance();
server.init(serverConfig);
SERVER_MAP.put(serverConfig.getPort() + "", server);
}
return server;
} catch (SofaRpcRuntimeException e) {
throw e;
} catch (Throwable e) {
throw new SofaRpcRuntimeException(e.getMessage(), e);
}
}
默认服务运行容器BoltServer启动Server端设置服务运行容器配置引用,按照服务端业务线程池核心线程数、最大线程数、线程队列以及回收时间配置初始化业务线程池提供给RpcHandler 处理复杂业务使用,不会影响Netty IO线程运行。根据服务运行容器BoltServer创建BoltServerProcessor处理器并且支持自定义业务线程池,BoltServerProcessor处理器继承AbstractUserProcessor类实现UserProcessor接口,当调用RpcHandler处理器的channelRead(ChannelHandlerContext ctx, Object msg)方法时候,根据请求类名称获取对应的UserProcessor执行handleRequest(BizContext bizCtx, AsyncContext asyncCtx, SofaRequest request)方法查找服务调用器、请求方法实施调用方法处理请求:
public void init(ServerConfig serverConfig) {
this.serverConfig = serverConfig;
// 启动线程池
bizThreadPool = initThreadPool(serverConfig);
boltServerProcessor = new BoltServerProcessor(this);
}
protected ThreadPoolExecutor initThreadPool(ServerConfig serverConfig) {
ThreadPoolExecutor threadPool = BusinessPool.initPool(serverConfig);
threadPool.setThreadFactory(new NamedThreadFactory(
"BOLT-BIZ-" + serverConfig.getPort(), serverConfig.isDaemon()));
threadPool.setRejectedExecutionHandler(new SofaRejectedExecutionHandler());
if (serverConfig.isPreStartCore()) { // 初始化核心线程池
threadPool.prestartAllCoreThreads();
}
return threadPool;
}
/**
* Construct
*
* @param boltServer 所在的Server
*/
public BoltServerProcessor(BoltServer boltServer) {
this.boltServer = boltServer;
this.executorSelector = new UserThreadPoolSelector(); // 支持自定义业务线程池
}
/**
* Bolt server processor of bolt server.
*
*/
public class BoltServerProcessor extends AsyncUserProcessor<SofaRequest>{...}
public abstract class AsyncUserProcessor<T> extends AbstractUserProcessor<T>{...}
public abstract class AbstractUserProcessor<T> implements UserProcessor<T>{...}
public void process(RemotingContext ctx, RpcRequestCommand cmd, ExecutorService defaultExecutor) throws Exception {
if (this.deserializeRequestCommand(ctx, cmd, 0)) {
UserProcessor userProcessor = ctx.getUserProcessor(cmd.getRequestClass());
if (userProcessor == null) {
String errMsg = "No user processor found for request: " + cmd.getRequestClass();
logger.error(errMsg);
this.sendResponseIfNecessary(ctx, cmd.getType(), this.getCommandFactory().createExceptionResponse(cmd.getId(), errMsg));
} else {
ctx.setTimeoutDiscard(userProcessor.timeoutDiscard());
if (userProcessor.processInIOThread()) {
if (this.deserializeRequestCommand(ctx, cmd, 2)) {
(new RpcRequestProcessor.ProcessTask(ctx, cmd)).run();
}
} else {
Executor executor = null;
if (null == userProcessor.getExecutorSelector()) {
executor = userProcessor.getExecutor();
} else {
if (!this.deserializeRequestCommand(ctx, cmd, 1)) {
return;
}
executor = userProcessor.getExecutorSelector().select(cmd.getRequestClass(), cmd.getRequestHeader());
}
if (executor == null) {
executor = this.getExecutor() == null ? defaultExecutor : this.getExecutor();
}
((Executor)executor).execute(new RpcRequestProcessor.ProcessTask(ctx, cmd));
}
}
}
}
什么时候调用BoltServerProcessor处理器handleRequest方法执行方法调用处理请求?查看BoltServerProcessor处理器handleRequest方法调用链:(7)服务端Server实例注册服务提供者配置、服务端调用实现,默认服务运行容器BoltServer缓存服务端调用Invoker对象到接口--> Invoker映射invokerMap以及服务发布接口方法:
public void registerProcessor(ProviderConfig providerConfig, Invoker instance) {
// 缓存Invoker对象
String key = ConfigUniqueNameGenerator.getUniqueName(providerConfig);
invokerMap.put(key, instance);
// 缓存接口的方法
for (Method m : providerConfig.getProxyClass().getMethods()) {
ReflectCache.putOverloadMethodCache(key, m);
}
}
(8)判断服务端配置ServerConfig是否自动启动,启动服务运行容器Server实例:
public void start() {
if (started) {
return;
}
synchronized (this) {
if (started) {
return;
}
// 生成Server对象
remotingServer = initRemotingServer();
try {
if (!remotingServer.start(serverConfig.getBoundHost())) {
throw new SofaRpcRuntimeException("Failed to start bolt server, see more detail from bolt log.");
}
started = true;
} catch (SofaRpcRuntimeException e) {
throw e;
} catch (Exception e) {
throw new SofaRpcRuntimeException("Failed to start bolt server!", e);
}
}
}
默认服务端BoltServer启动服务首先以服务端配置端口通过RpcServer初始化生成远程服务端Server实例,远程服务端Server将BoltServerProcessor处理器缓存到userProcessors:
protected RemotingServer initRemotingServer() {
// 绑定到端口
RemotingServer remotingServer = new RpcServer(serverConfig.getPort());
remotingServer.registerUserProcessor(boltServerProcessor);
return remotingServer;
}
public RpcServer(int port) {
super(port);
this.globalSwitch = new GlobalSwitch();
this.connectionEventListener = new ConnectionEventListener();
this.userProcessors = new ConcurrentHashMap(4);
this.bossGroup = new NioEventLoopGroup(1, new NamedThreadFactory("Rpc-netty-server-boss"));
}
public void registerUserProcessor(UserProcessor<?> processor) {
if (processor != null && !StringUtils.isBlank(processor.interest())) {
UserProcessor<?> preProcessor = (UserProcessor)this.userProcessors.putIfAbsent(processor.interest(), processor);
if (preProcessor != null) {
String errMsg = "Processor with interest key [" + processor.interest() + "] has already been registered to rpc server, can not register again!";
throw new RuntimeException(errMsg);
}
} else {
throw new RuntimeException("User processor or processor interest should not be blank!");
}
}
接着远程服务端Server按照服务运行容器绑定Host配置启动服务,执行init()方法初始化RpcRemoting远程连接事件处理器以及连接管理器,初始化服务启动类ServerBootstrap实例,设置服务启动类ServerBootstrap属性,根据缓存BoltServerProcessor的userProcessors创建RpcHandler处理器并且添加到ChannelPipeline,创建Rpc服务连接,执行doStart()方法服务启动类ServerBootstrap实例绑定指定IP地址同步:
public boolean start(String ip) {
this.init();
if (this.started.compareAndSet(false, true)) {
try {
logger.warn("Server started on " + ip + ":" + this.port);
return this.doStart(ip);
} catch (Throwable var3) {
this.started.set(false);
logger.error("ERROR: Failed to start the Server!", var3);
return false;
}
} else {
logger.error("ERROR: The server has already started!");
return false;
}
}
protected void doInit() {
if (this.addressParser == null) {
this.addressParser = new RpcAddressParser();
}
this.initRpcRemoting((RpcRemoting)null);
this.bootstrap = new ServerBootstrap();
((ServerBootstrap)((ServerBootstrap)((ServerBootstrap)this.bootstrap.group(this.bossGroup, workerGroup).channel(NioServerSocketChannel.class)).option(ChannelOption.SO_BACKLOG, SystemProperties.tcp_so_backlog())).option(ChannelOption.SO_REUSEADDR, SystemProperties.tcp_so_reuseaddr())).childOption(ChannelOption.TCP_NODELAY, SystemProperties.tcp_nodelay()).childOption(ChannelOption.SO_KEEPALIVE, SystemProperties.tcp_so_keepalive());
this.initWriteBufferWaterMark();
boolean pooledBuffer = SystemProperties.netty_buffer_pooled();
if (pooledBuffer) {
((ServerBootstrap)this.bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)).childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
}
final boolean idleSwitch = SystemProperties.tcp_idle_switch();
final int idleTime = SystemProperties.tcp_server_idle();
final ChannelHandler serverIdleHandler = new ServerIdleHandler();
final RpcHandler rpcHandler = new RpcHandler(true, this.userProcessors);
this.bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast("decoder", new RpcProtocolDecoder(1));
pipeline.addLast("encoder", new ProtocolCodeBasedEncoder(ProtocolCode.fromBytes(new byte[]{2})));
if (idleSwitch) {
pipeline.addLast("idleStateHandler", new IdleStateHandler(0L, 0L, (long)idleTime, TimeUnit.MILLISECONDS));
pipeline.addLast("serverIdleHandler", serverIdleHandler);
}
pipeline.addLast("connectionEventHandler", RpcServer.this.connectionEventHandler);
pipeline.addLast("handler", rpcHandler);
this.createConnection(channel);
}
private void createConnection(SocketChannel channel) {
Url url = RpcServer.this.addressParser.parse(RemotingUtil.parseRemoteAddress(channel));
if (RpcServer.this.globalSwitch.isOn(2)) {
RpcServer.this.connectionManager.add(new Connection(channel, url), url.getUniqueKey());
} else {
new Connection(channel, url);
}
channel.pipeline().fireUserEventTriggered(ConnectionEventType.CONNECT);
}
});
}
protected boolean doStart() throws InterruptedException {
this.channelFuture = this.bootstrap.bind(new InetSocketAddress(this.port)).sync();
return this.channelFuture.isSuccess();
}
(9)服务发布配置ProviderConfig实例绑定Provider配置发生变化监听器ProviderAttributeListener,监听Provider属性变化更新服务发布配置属性:
/**
* Provider配置发生变化监听器
*/
private class ProviderAttributeListener implements ConfigListener {
@Override
public void configChanged(Map newValue) {
}
@Override
public synchronized void attrUpdated(Map newValueMap) {
String appName = providerConfig.getAppName();
// 可以改变的配置 例如tag concurrents等
Map<String, String> newValues = (Map<String, String>) newValueMap;
Map<String, String> oldValues = new HashMap<String, String>();
boolean reexport = false;
try { // 检查是否有变化
// 是否过滤map?
for (Map.Entry<String, String> entry : newValues.entrySet()) {
String newValue = entry.getValue();
String oldValue = providerConfig.queryAttribute(entry.getKey());
boolean changed = oldValue == null ? newValue != null : !oldValue.equals(newValue);
if (changed) {
oldValues.put(entry.getKey(), oldValue);
}
reexport = reexport || changed;
}
} catch (Exception e) {
LOGGER.errorWithApp(appName, "Catch exception when provider attribute compare", e);
return;
}
// 需要重新发布
if (reexport) {
try {
if (LOGGER.isInfoEnabled(appName)) {
LOGGER.infoWithApp(appName, "Reexport service {}", providerConfig.buildKey());
}
unExport();
// change attrs
for (Map.Entry<String, String> entry : newValues.entrySet()) {
providerConfig.updateAttribute(entry.getKey(), entry.getValue(), true);
}
export();
} catch (Exception e) {
LOGGER.errorWithApp(appName, "Catch exception when provider attribute changed", e);
//rollback old attrs
for (Map.Entry<String, String> entry : oldValues.entrySet()) {
providerConfig.updateAttribute(entry.getKey(), entry.getValue(), true);
}
export();
}
}
}
}
(10)注册订阅服务列表,判断服务发布配置是否注册,根据服务发布注册中心配置列表遍历注册中心配置通过注册中心工厂获取注册中心实例并且初始化启动注册中心,注册服务发布配置到注册中心:
/**
* 订阅服务列表
*/
protected void register() {
if (providerConfig.isRegister()) {
List<RegistryConfig> registryConfigs = providerConfig.getRegistry();
if (registryConfigs != null) {
for (RegistryConfig registryConfig : registryConfigs) {
Registry registry = RegistryFactory.getRegistry(registryConfig);
registry.init();
registry.start();
try {
registry.register(providerConfig);
} catch (SofaRpcRuntimeException e) {
throw e;
} catch (Throwable e) {
String appName = providerConfig.getAppName();
if (LOGGER.isWarnEnabled(appName)) {
LOGGER.warnWithApp(appName, "Catch exception when register to registry: "
+ registryConfig.getId(), e);
}
}
}
}
}
}
(11)全局运行时上下文RpcRuntimeContext增加缓存ProviderConfig,缓存服务发布配置到EXPORTED_PROVIDER_CONFIGS集合。
解析总结
SOFARPC服务发布流程概括为SOFARPC服务需要创建服务运行容器配置ServerConfig,自定义设置基础配置并且通过配置文件加载服务端默认配置;创建服务发布配置ProviderConfig,自定义设置接口名称、接口实现类引用以及指定服务端配置;服务发布启动类ProviderBootstrap发布服务:构造请求调用器ProviderProxyInvoker(最底层调用过滤器执行链FilterChain),提前初始化注册中心,创建服务端Server包括启动业务线程池bizThreadPool 、创建BoltServerProcessor处理器,服务端Server注册服务提供者配置ProviderConfig、服务端调用实现ProviderProxyInvoker,服务端Server启动服务创建RpcServer,将BoltServerProcessor处理器缓存到userProcessors,初始化服务启动ServerBootstrap,根据userProcessors创建RpcHandler处理器添加到ChannelPipeline,创建RPC服务连接完成启动服务链路。RPC服务请求调用RpcHandler的channelRead()方法获取UserProcessor执行handlerRequest()方法查找服务调用器Invoker实施调用过滤器执行链FilterChain方法调用。