Dubbo源码解析-Dubbo服务提供者_Injvm协议(⼀)前⾔: 根据前⾯两篇⾃定义RPC框架的⽂章,我们带着问题来学习Dubbo。
学习Dubbo是如何解决这些服务调⽤、注册中⼼、序列化、集容错等问题。 本⽂主要集中在Dubbo服务端,使⽤最原始的API⽅式来构建⼀个可⽤服务。
原子核的能级1.使⽤API构建服务端
1public class ProviderApplication {
2 public static void main(String[] args) {
3 // 服务实现(⾃定义DemoService接⼝)
4 DemoService demoService = new DemoServiceImpl();
5
7 ApplicationConfig application = new ApplicationConfig();
8 application.setName("provider");
9
10 // 连接注册中⼼配置
11 RegistryConfig registry = new RegistryConfig();
12 // 本地zookeeper作为配置中⼼
13 registry.setAddress("zookeeper://localhost:2181");
14
15 // 服务提供者协议配置
16 ProtocolConfig protocol = new ProtocolConfig();
17 // dubbo协议,并以20881端⼝暴露
18 protocol.setName("dubbo");
19 protocol.setPort(20881);
20
21 // 服务提供者暴露服务配置
22 ServiceConfig<DemoService> service = new ServiceConfig<DemoService>();
23 service.setApplication(application);
24 service.setRegistry(registry);
25 service.setProtocol(protocol);
26 service.setInterface(DemoService.class);
27 service.setRef(demoService);
28 service.setVersion("1.0.0");
29
30 // 暴露及注册服务
31 port();
32 }
33}
34
35// 接⼝
36public interface DemoService {
37 String sayHello(String name);
38}
我们使⽤最原始的这种⽅式来构造⼀个Dubbo provider。不建议在分析源码时使⽤spring-dubbo等⽅式,因为最终还是解析相关dubbo标签然后⽣成这样原始的bean的⽅式来发起dubbo服务的。
水半夏有些⽐较关键的配置类,我们⼀起来简单分析下。
1.1 ApplicationConfig解析
2public class ApplicationConfig extends AbstractConfig {
3 // 名称
4 private String name;
5 // 版本
6 private String version;
7
8 // Java字节码编译器,⽤于动态类的⽣成,可选:jdk或javassist
9 private String compiler;
10
11 // ⽇志输出⽅式,可选:slf4j,jcl,log4j,log4j2,jdk,默认为slf4j
12 private String logger;
13
14 // 配置参数
15 private Map<String, String> parameters;
16
17 // dubbo 2.5.8 新版本增加了 QOS 模块,提供了新的 telnet 命令⽀持
18 // 具体可参考/zh/docsv2.7/user/references/qos/
19 private Boolean qosEnable;
20 private String qosHost;
21 private Integer qosPort;
22 private Boolean qosAcceptForeignIp;
23
24 // 对应的注册中⼼信息
25 private List<RegistryConfig> registries;
26
27 // 对应的监控配置信息
28 private MonitorConfig monitor;
29
30 // 更多可参考:/zh/docsv2.7/user/references/xml/dubbo-application/
31 ...
王学左派
32}
ApplicationConfig主要描述了该服务端应⽤的配置信息。
1.2 RegistryConfig
1// 注册中⼼信息
2public class RegistryConfig extends AbstractConfig {
3
4 // 注册中⼼地址,如本地zookeeper,则address为zookeeper://localhost:2181
5 private String address;
6 private Integer port;
7
8 // 协议名称,⽀持dubbo, multicast, zookeeper, redis等
9 private String protocol;
10 // ⽹络传输⽅式,可选mina,netty
11 private String transporter;
12
13 // 更多配置可参考:/zh/docsv2.7/user/references/xml/dubbo-registry/
14 ...
15}
为什么需要注册中⼼,因为我们需要⼀个能够保存服务提供者信息的地⽅,需要能够动态的提醒消费者服务的上下线。
1.3 ProtocolConfig
Dubbo提供了很多种的协议实现⽅式,org.apache.dubbo.rpc.Protocol接⼝,所有的实现类即dubbo提供的协议。具体如下:
后续我们会仔细分析,本⽂我们先知道有这么多协议类型即可
1.4 ServiceConfig
1
// 协议配置2
public class ProtocolConfig extends AbstractConfig {3
4
// 协议名称,默认为dubbo 5
private String name;6
7
// 协议端⼝号,默认dubbo 协议为20880端⼝8
private Integer port;9
10
// 请求及响应数据包⼤⼩限制,默认为8M 11
private Integer payload;12
// 协议编码⽅式,默认为dubbo 13
private String codec;14
// 序列化⽅式,默认dubbo 为hessian215
private String serialization;16
17
// 服务提供者上下⽂路径,为服务path 的前缀18
private String contextpath;19
20
// 协议的消息派发⽅式,⽤于指定线程模型,⽐如:dubbo 协议的all, direct, message, execution, connection 等21
private String dispatcher;22
23
// ⽹络读写缓冲区⼤⼩,默认为8Kb 24
private Integer buffer;25
26
// 该协议的服务是否注册到注册中⼼27
private Boolean register;28
29
// ⽤户可⾃定义线程池信息,⽤于在接收请求时分配线程30
// ⽐较简单的参数定义,具体可直接看源码注释31
private String threadpool;32
private String threadname;33
private Integer corethreads;34
private Integer threads;35
private Integer iothreads;36
private Integer queues;37
38
// 更多协议配置信息可参考:/zh/docsv2.7/user/references/xml/dubbo-protocol/39
...40 41}
1public class ServiceConfig<T> extends ServiceConfigBase<T> {
2
3 // 主要属性如下
4 // 服务接⼝名
5 protected String interfaceName;
6 // 服务对象实现引⽤
7 protected T ref;
8 // 服务路径
9 protected String path;
10
11 // 远程服务调⽤超时时间(毫秒),默认为1秒
12 protected Integer timeout;
13 // 远程服务调⽤重试次数,不包括第⼀次调⽤,默认为2,则说明总共会调⽤3次
14 protected Integer retries;
15
16 // 服务是否动态注册,如果设为false,注册后将显⽰后disable状态,需⼈⼯启⽤,并且服务提供者停⽌时,也不会⾃动取消册,需⼈⼯禁⽤。
17 protected Boolean dynamic = true;麦肯锡
18
19 // 更多参数请参考/zh/docsv2.7/user/references/xml/dubbo-service/
20 ...
辽河油田华宇网21
22}
ServiceConfig本⾝并没有什么属性,主要就是⼀些⽅法。
我们使⽤ServiceConfig主要就是为了指定接⼝和实现类。
它的属性都在⽗类中提供,具体类结构图如下:
port()
上⾯所有的配置都是为了集成到ServiceConfig中,最重要的还是port()⽅法。
1public class ServiceConfig<T> extends ServiceConfigBase<T> {
2
3 public synchronized void export() {
4 ...
5 // ⽀持延时
6 if (shouldDelay()) {
7 DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
8 } else {
阴阳互易9 // 交由doExport处理
10 doExport();
11 }
12
13 exported();
14 }
15
16 // doExport
17 protected synchronized void doExport() {
18 if (unexported) {
19 throw new IllegalStateException("The service " + Name() + " has already unexported!");
20 }
21 // 只能export⼀次
22 if (exported) {
23 return;
24 }
25 exported = true;
26
27 if (StringUtils.isEmpty(path)) {
28 path = interfaceName;
29 }
30 // 交由doExportUrls处理
31 doExportUrls();
32 }
33
34 // doExportUrls
35 private void doExportUrls() {
36 ...
37 List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);
38
39 // 这个protocols就是我们上⾯ServiceConfig添加的的ProtocolConfig对象,如果添加多个,则说明当前服务⽀持多协议暴露
40 for (ProtocolConfig protocolConfig : protocols) {
41 String pathKey = URL.buildKey(getContextPath(protocolConfig)
42 .map(p -> p + "/" + path)
43 .orElse(path), group, version);
44 isterService(pathKey, interfaceClass);
45 serviceMetadata.setServiceKey(pathKey);
46 // 重点在这⾥
47 doExportUrlsFor1Protocol(protocolConfig, registryURLs);
48 }
49 }
50
51 // 真正的暴露服务的⽅法
52 private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
53 String name = Name();
54 if (StringUtils.isEmpty(name)) {
55 name = DUBBO;
56 }
57
58 // 配置参数添加到map中
59 Map<String, String> map = new HashMap<String, String>();
60 map.put(SIDE_KEY, PROVIDER_SIDE);
61
62 ServiceConfig.appendRuntimeParameters(map);
63 ...
64 MetadataReportConfig metadataReportConfig = getMetadataReportConfig();
65 if (metadataReportConfig != null && metadataReportConfig.isValid()) {