一:RPC
RPC【Remote Procedure Call】是指远程过程调用,是一种进程间通信方式,他是一种技术的思想,而不是规范。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。
RPC两个核心模块:通讯,序列化,这两个模块决定RPC的性能好坏。
二:Dubbo
Apache Dubbo (incubating) |ˈdʌbəʊ| 是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。官网:http://dubbo.apache.org/
- 服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者(Consumer): 调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
- 监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
调用关系说明:
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
三:初体验
1.搭建注册中心
- 安装JDK
- 将zookeeper压缩包上传到linux中,解压文件,下载地址:https://archive.apache.org/dist/zookeeper/zookeeper-3.4.6/
- 进入 zookeeper-3.4.6 目录,创建 data 文件夹:mkdir data
- 进入conf目录 ,把 zoo_sample.cfg拷贝一份并改名为 zoo.cfg
- 打开zoo.cfg , 修改 data 属性:dataDir=自己目录/zookeeper-3.4.6/data
- 启动zookeeper服务:./zkServer.sh start|stop|status,输出一下表示启动成功
2.安装dubbo-admin管理控制台
dubbo本身并不是一个服务软件。它其实就是一个jar包能够帮你的java程序连接到zookeeper,并利用zookeeper消费、提供服务。所以你不用在Linux上启动什么dubbo服务。但是为了让用户更好的管理监控众多的dubbo服务,官方提供了一个可视化的监控程序,不过这个监控即使不装也不影响使用。
- 下载dubbo-admin:https://github.com/apache/incubator-dubbo-ops
- 进入目录,修改dubbo-admin配置:修改 src\main\resources\application.properties 指定zookeeper地址
- 打包dubbo-admin:mvn clean package -Dmaven.test.skip=true
- 运行dubbo-admin:java -jar dubbo-admin-0.0.1-SNAPSHOT.jar,使用root/root登录
3.创建服务
需求:某个电商系统,订单服务需要调用用户服务获取某个用户的所有地址;我们现在 需要创建两个服务模块进行测试
测试预期结果:订单服务web模块在A服务器,用户服务模块在B服务器,A可以远程调用B的功能。
分包:建议将服务接口,服务模型,服务异常等均放在 API 包中,因为服务模型及异常也是 API 的一部分,同时,这样做也符合分包原则:重用发布等价原则(REP),共同重用原则(CRP)。如果需要,也可以考虑在 API 包中放置一份 spring 的引用配置,这样使用方,只需在 spring 加载过程中引用此配置即可,配置建议放在模块的包目录下,以免冲突,如:com/alibaba/china/xxx/dubbo-reference.xml。
粒度:服务接口尽可能大粒度,每个服务方法应代表一个功能,而不是某功能的一个步骤,否则将面临分布式事务问题,Dubbo 暂未提供分布式事务支持。服务接口建议以业务场景为单位划分,并对相近业务做抽象,防止接口数量爆炸。不建议使用过于抽象的通用接口,如:Map query(Map),这样的接口没有明确语义,会给后期维护带来不便。
创建gmail-interface模块
创建UserAddress类:
public class UserAddress implements Serializable{
private Integer id;//id
private String userAddress;//收货地址
private String userId;//用户id
private String consignee;//收货人
private String phoneNum;//收货号码
private String isDefault;//是否为默认地址 Y是 N否
//get set
}
添加UserService服务:
public interface UserService {
public List<UserAddress> getUserAddressList(String userId);
}
添加OrderService服务:
public interface OrderService {
public void initOrder(String userId);
}
创建user-service-prodiver模块:在pom文件中依赖gmail-interface
<dependencies>
<dependency>
<groupId>com.peng.test</groupId>
<artifactId>gmail-interface</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
添加UserServiceImpl实现类:
public class UserServiceImpl implements UserService{
@Override
public List<UserAddress> getUserAddressList(String userId) {
System.out.println("UserServiceImpl.....old...");
UserAddress address1 = new UserAddress(1, "北京市昌平区宏福科技园综合楼3层", "1", "李老师", "010-56253825", "Y");
UserAddress address2 = new UserAddress(2, "深圳市宝安区西部硅谷大厦B座3层(深圳分校)", "1", "王老师", "010-56253825", "N");
return Arrays.asList(address1,address2);
}
}
创建订单服务order-service-consumer,依赖gmail-interface
创建OrderServiceImpl实现:
public class OrderServiceImpl implements OrderService {
UserService userService;
@Override
public void initOrder(String userId) {
//查询用户的收货地址
List<UserAddress> userAddressList = userService.getUserAddressList(userId);
System.out.println(userAddressList);
}
}
4.集成dubbo
在user-service-prodiver中添加dubbo及zookeeper依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
配置dubbo,创建prodiver.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 设置包扫描 -->
<context:component-scan base-package="com.peng.test.service.impl" ></context:component-scan>
<!--当前应用的名字 -->
<dubbo:application name="user-service-prodiver"></dubbo:application>
<!--指定注册中心的地址 -->
<dubbo:registry address="zookeeper://192.168.25.128:2181" />
<!--使用dubbo协议,将服务暴露在20880端口 -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 指定需要暴露的服务 -->
<dubbo:service interface="com.peng.test.service.UserService" ref="userServiceImpl" />
</beans>
在UserServiceImpl上加@Service注解(spring的)
添加测试:
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:prodiver.xml");
classPathXmlApplicationContext.start();
try {
System.in.read();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
在order-service-consumer中添加dubbo及zookeeper依赖
配置dubbo,创建consumer.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 设置包扫描 -->
<context:component-scan base-package="com.peng.test.serice.impl" ></context:component-scan>
<!-- 应用名 -->
<dubbo:application name="order-service-consumer"></dubbo:application>
<!-- 指定注册中心地址 -->
<dubbo:registry address="zookeeper://192.168.25.128:2181" />
<!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
<dubbo:reference id="userService" interface="com.peng.test.service.UserService"></dubbo:reference>
</beans>
在OrderServiceImpl上加@Service注解(spring)及userService上加@Autowired注解(spring)
添加测试:
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("consumer.xml");
OrderService bean = classPathXmlApplicationContext.getBean(OrderService.class);
bean.initOrder("12");
classPathXmlApplicationContext.start();
try {
System.in.read();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
先启动user-service-prodiver的测试类,在启动的测试类,会发现order-service-consumer调用了user-service-prodiver提供的服务
四:监控中心安装
- 使用mvn package将dubbo-monitor-simple打包
- 在target目录下会生成dubbo-monitor-simple-2.0.0.jar及dubbo-monitor-simple-2.0.0-assembly.tar.gz文件,将dubbo-monitor-simple-2.0.0-assembly.tar.gz解压,修改conf/dubbo.properties文件,将dubbo.registry.address=zookeeper://192.168.25.128:2181
- 启动监控中心:在assembly.bin文件下,点击start.bat,浏览器访问http://localhost:8080/
- 在prodiver.xml及consumer.xml中添加如下配置,重新启动两个测试类
<!-- 监控中心协议,如果为protocol="registry",表示从注册中心发现监控中心地址,否则直连监控中心 -->
<dubbo:monitor protocol="registry"></dubbo:monitor>
五:整合SpringBoot
1.搭建项目
修改gmail-interface中OrderService接口
使用Eclipse的STS插件创建boot-user-service-provider,jar包
加入依赖:
<dependency>
<groupId>com.peng.test</groupId>
<artifactId>gmail-interface</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.5</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
创建UserServiceImpl实现:
@Service//dubbo注解
public class UserServiceImpl implements UserService {
@Override
public List<UserAddress> getUserAddressList(String userId) {
System.out.println("UserServiceImpl.....old...");
UserAddress address1 = new UserAddress(1, "北京市昌平区宏福科技园综合楼3层", "1", "李老师", "010-56253825", "Y");
UserAddress address2 = new UserAddress(2, "深圳市宝安区西部硅谷大厦B座3层(深圳分校)", "1", "王老师", "010-56253825", "N");
return Arrays.asList(address1,address2);
}
}
配置文件:
dubbo.application.name=boot-user-service-provider
dubbo.registry.address=192.168.25.128:2181
dubbo.registry.protocol=zookeeper
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
dubbo.monitor.protocol=registry
启动类上添加@EnableDubbo,开启对Dubbo的支持
创建boot-order-service-consumer项目,war包
添加依赖:
<dependency>
<groupId>com.peng.test</groupId>
<artifactId>gmail-interface</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.5</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
创建OrderServiceImpl实现:
@Service
public class OrderServiceImpl implements OrderService {
@Reference//dubbo注解
private UserService userService;
@Override
public List<UserAddress> initOrder(String userId) {
//查询用户的收货地址
List<UserAddress> userAddressList = userService.getUserAddressList(userId);
return userAddressList;
}
}
创建controller:
@Controller
public class OrderController {
@Autowired
private OrderService orderService;
@RequestMapping("/initOrder")
@ResponseBody
public List<UserAddress> initOrder(String userId){
List<UserAddress> initOrder = orderService.initOrder(userId);
return initOrder;
}
}
配置文件:
server.port=8090
dubbo.application.name=boot-order-service-consumer
dubbo.registry.address=zookeeper://192.168.25.128:2181
dubbo.monitor.protocol=registry
启动类上添加@EnableDubbo,开启对Dubbo的支持
2.测试
启动boot-user-service-provider及boot-order-service-consumer,访问http://localhost:8090/initOrder?userId=444,可以正常返回
3.SpringBoot使用原生xml配置dubbo
表写xml配置文件与之前一样,在启动类上配置@ImportResource("classpath:dubbo.xml"),取消@EnableDubbo注解
4.使用java配置dubbo
@Configuration
public class MyDubboConfig {
@Bean
private ApplicationConfig applicationConfig() {
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("boot-user-service-provider");
return applicationConfig;
}
@Bean
public RegistryConfig registryConfig() {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("127.0.0.1:2181");
registryConfig.setProtocol("zookeeper");
return registryConfig;
}
@Bean
public ProtocolConfig protocolConfig() {
ProtocolConfig protocolConfig = new ProtocolConfig();
protocolConfig.setName("dubbo");
protocolConfig.setPort(20882);
return protocolConfig();
}
@Bean
public MonitorConfig monitorConfig() {
MonitorConfig monitorConfig = new MonitorConfig();
monitorConfig.setProtocol("registry");
return monitorConfig;
}
@Bean
public ServiceConfig<UserService> userServiceConfig(UserServiceImpl userServiceImpl){
ServiceConfig<UserService> serviceConfig = new ServiceConfig<UserService>();
serviceConfig.setInterface(UserService.class);
serviceConfig.setRef(userServiceImpl);
serviceConfig.setTimeout(3000);
serviceConfig.setVersion("1.0.0");
MethodConfig methodConfig = new MethodConfig();
methodConfig.setName("getUserAddressList");
methodConfig.setTimeout(1000);
serviceConfig.setMethods(Arrays.asList(methodConfig));
return serviceConfig;
}
}
启动类上配置@EnableDubbo(scanBasePackages= {"com.peng.test.service.configuration"}),其他注解一样使用
六:配置简介
1.配置启动时检查:检查消费的服务是否在注册中心中,不在则报错
2.超时&配置覆盖关系
优先级:
- 方法级优先,接口级次之,全局配置再次之。
- 如果级别一样,则消费方优先,提供方次之。
3.重试次数:第一次请求失败后,会重试,第一次请求次数不算
幂等(多次请求结果一样比如:查询,删除,修改)方法适合设置重试次数,非幂等(多次请求结果不一样如:新增)方法不适合设置重试次数
4.多版本配置:程序升级后,只对部分服务器升级测试,其他依旧使用原有程序
服务提供方配置多个服务提供,通过版本号区别
服务消费方通过版本号指定消费哪个服务
5.本地存根:可以验证提交参数
编写服务实现:
public class UserServiceStub implements UserService {
private final UserService userService;
//必须提供有参构造器
public UserServiceStub(UserService userService) {
this.userService = userService;
}
@Override
public List<UserAddress> getUserAddressList(String userId) {
if (!StringUtils.isEmpty(userId)) {//验证参数
return userService.getUserAddressList(userId);
}
return null;
}
}
dubbo会通过有参构造将真是的服务传过来
6.更多配置参考dubbo开发手册:http://dubbo.apache.org/zh-cn/docs/user/quick-start.html
七:服务的高可用
1.Zookeeper宕机与Dubbo直连
将Zookepper宕机后,发现服务依旧可以调用,那是因为dubbo存在服务提供者的本地缓存,还可以通@Reference(url="127.0.0.1:20880")配置dubbo直连。
2.dubbo的负载均衡
负载均衡策略:
- Random LoadBalance:随机,按权重设置随机概率。在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
- RoundRobin LoadBalance:轮循,按公约后的权重设置轮循比率。存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在第二台上。
- LeastActive LoadBalance:最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
- ConsistentHash LoadBalance:一致性 Hash,相同参数的请求总是发到同一提供者。当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。算法参见:http://en.wikipedia.org/wiki/Consistent_hashing缺省只对第一个参数 Hash,如果要修改,请配置 <dubbo:parameter key="hash.arguments" value="0,1" />缺省用 160 份虚拟节点,如果要修改,请配置 <dubbo:parameter key="hash.nodes" value="320" /
默认使用随机,使用@Reference(loadbalance="roundrobin")配置轮训的方式,可以使用dubbo的监控平台调节服务的权重
3.服务降级
当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作。可以通过服务降级功能临时屏蔽某个出错的非关键服务,并定义降级后的返回策略。
服务还未调用时直接返回为空:
在消费者层面通过屏蔽达到在服务消费方调用服务时直接返回为空
服务调用失败后在返回为空:
依旧是在服务消费方,当服务消费方调用服务后返回失败,在返回为空
4.服务容错&Hystrix
集群容错模式:
- Failover Cluster:失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次),默认。
- Failfast Cluster:快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
- Failsafe Cluster:失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
- Failback Cluster:失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
- Forking Cluster:并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。
- Broadcast Cluster:广播调用所有提供者,逐个调用,任意一台报错则报错 。通常用于通知所有提供者更新缓存或日志等本地资源信息。
集群模式配置:按照以下示例在服务提供方和消费方配置集群模式<dubbo:service cluster="failsafe" />或<dubbo:reference cluster="failsafe" />
整合hystrix:
服务提供方及服务消费方加入hystrix依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
服务提供方及服务消费方在启动类上加@EnableHystrix注解
服务提供方的服务方法上加@HystrixCommand注解
服务消费方的消费方法上加@HystrixCommand(fallbackMethod="hello")注解,当服务提供方出现异常,会调用hello方法
八:RPC原理
1.RPC原理
一次完整的RPC调用流程(同步调用,异步另说)如下:
- 服务消费方(client)调用以本地调用方式调用服务;
- client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;
- client stub找到服务地址,并将消息发送到服务端;
- server stub收到消息后进行解码;
- server stub根据解码结果调用本地的服务;
- 本地服务执行并将结果返回给server stub;
- server stub将返回结果打包成消息并发送至消费方;
- client stub接收到消息,并进行解码;
- 服务消费方得到最终结果。
RPC框架的目标就是要2~8这些步骤都封装起来,这些细节对用户来说是透明的,不可见的。
九:dubbo原理
1.dubbo框架设计结构
- config 配置层:对外配置接口,以 ServiceConfig, ReferenceConfig 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类
- proxy 服务代理层:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以 ServiceProxy 为中心,扩展接口为 ProxyFactory
- registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactory, Registry, RegistryService
- cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 Cluster, Directory, Router, LoadBalance
- monitor 监控层:RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory, Monitor, MonitorService
- protocol 远程调用层:封装 RPC 调用,以 Invocation, Result 为中心,扩展接口为 Protocol, Invoker, Exporter
- exchange 信息交换层:封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer
- transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel, Transporter, Client, Server, Codec
- serialize 数据序列化层:可复用的一些工具,扩展接口为 Serialization, ObjectInput, ObjectOutput, ThreadPool
2.配置文件解析
dubbo的配置文件中,所有的配置都是以dubbo开头的,在spring中解析配置文件的标签都是使用BeanDefinitionParser接口的实现类来解析的,所以解析dubbo的配置标签也不例外。
dubbo实现了一个自己的标签解析器,实现了其中的parse()方法,parse(0方法是用来一条一条解析dubbo标签的,在方法的参数中传入一个beanClass,然后在方法中依次判断是否是对应类型的对象
那么这些对象有时在何时生成的呢?其实是在调用DubboBeanDefinitionParser的构造方法前调用了DubboNamespaceHandler的初始化方法,创建很多DubboBeanDefinitionParser来解析对应的标签
经过DubboBeanDefinitionParser的解析后,会将每一条标签数据放到对应的XXXConfig中,不过值得注意的是service及reference是保存在对应的Bean中的。
3.服务暴露流程
上面说过在解析标签时会创建一个ServiceBean,那么现在就来看看ServiceBean,会发现ServiceBean实现InitializingBean及ApplicationListener<ContextRefreshedEvent>两个接口,InitializingBean会在创建对象时调用其afterPropertiesSet方法,ApplicationListener监听ContextRefreshedEvent,说明当IOC容器刷新完毕后会调用onApplicationEvent方法,下面就看看这两个方法。
对于afterPropertiesSet就是将之前读取到的配置信息以XXXConfig添加到serviceBean对象中。
onApplicationEvent中通过export暴露服务,经过一系列的数据验证后,来到com.alibaba.dubbo.config.ServiceConfig.doExportUrls()方法,暴露服务
循环配置的所有的dubbo协议,暴露服务,配置文件中可以配置多个<dubbo:protocol name="dubbo" port="20880" />
通过代理工厂将向外提供服务的接口及暴露的地址包装为Invoker,最后调用protocol的export方法,将Invoker传入进去,暴露服务
对于Protocol有两个重要的实现类DubboProtocol用于dubbo协议暴露服务,RegistryProtocol用于注册中心,最先来到RegistryProtocol的export()方法
来到doLocalExport()方法中
invokerDelegete中保存了暴露的接口,接口实现类,暴露的地址的信息,再次放行,又来到DubboProtocol的export方法中
首先获取暴露到zookeeper中服务的地址,之后调用openServer()
第一次需要调用createServer创建服务器
openServer()执行完毕后,netty服务器就创建完毕,之后又在RegistryProtocol的export方法中注册提供者
又在底层缓存一个暴露的url对应的服务执行器,当消费者通过url调用服务时,提供者就可以通过这个映射关系获取到服务执行器,执行服务。到这里服务就暴露出去了,dubbo底层启动netty服务,监听20880端口,当有请求进来时,netty就会根据请求的信息通过providerInvokers中缓存的信息调用对应的服务。
4.服务引用流程
对于服务的引用于服务的暴露前面步骤类似,标签<dubbo:reference/>也对应一个ReferenceBean
发现ReferenceBean是FactoryBean的实现,那么当通过在项目中需要服务的实现时就会调用ReferenceBean的getObject()方法获取服务的实现
在init()方法中,创建服务的代理对象
传入一个map,包装了注册中心地址,调用的方法,调用的接口
从注册中心中获取远程接口,refprotocol又是Protocol,对应的又是DubboProtocol及RegistryProtocol两个实现,放行后,首先来到RegistryProtocol的refer方法
在doRefer方法中
放行后又来到DubboProtocol的refer方法中
通过服务地址,在getClients()中获取客户端
通过initClient初始化客户端
最后还是调用netty传输器连接,创建Netty客户端,服务点阅完毕后就会获取一个invoker
invoker中封装了服务地址等信息
最后又将服务的地址及服务执行器对应起来,最后将invoker返回
至此,代理对象就创建完毕
5.服务调用流程
上面服务的代理对象已经创建完毕,下面就看看,代理对象是如何执行服务的,通过代理对象执行方法时,首先来到
invoker里面层层封装着不同的invoker
最后调用doInvoke()方法
通过负载均衡策略从Invoker的List中选择一个Invoker
一直不断地f5进入invoker()方法,最终来到DubboInvoker的doInvoke()方法
通过在服务引用中获取到的客户端发起请求,返回响应结果,invoker调用链主要用来进行数据统计等其他一些操作。