目标
-
基于apache-dubbo消息转发
zookeeper 本地注册中心搭建
soul admin 本地配置dubbo插件
-
启动soul-example-apache-dubbo-service
- dubbo注册服务到admin
分别演示允许转发与过滤转发
-
分析example-dubbo启动完成接口的注册
- soul-spring-boot-starter-client-apache-dubbo注入
- ApacheDubboServiceBeanPostProcessor
- onApplicationEvent
总结
基于apache-dubbo消息转发
由于soul的dubbo采用zookeeper作为注册中心,所以下面我们就先搭建zookeeper.
zookeeper本地注册中心搭建
- 下载zookeeper(zk)安装包
- 解压zookeeper安装包
tar -xvf apache-zookeeper-3.6.2-bin.tar.gz -C /opt
vim conf/zoo.cfg # 修改配置文件
# The number of milliseconds of each tick
2 tickTime=2000
3 # The number of ticks that the initial
4 # synchronization phase can take
5 initLimit=10
6 # The number of ticks that can pass between
7 # sending a request and getting an acknowledgement
8 syncLimit=5
9 # the directory where the snapshot is stored.
10 # do not use /tmp for storage, /tmp here is just
11 # example sakes.
12 dataDir=/Volumes/Nuo/tools/data/zookeeper
13 dataLogDir=/Volumes/Nuo/tools/data/zookeeper/logs
14 # the port at which the clients will connect
15 clientPort=2181</pre>
- 启动zk服务
zkServer.sh start
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /Volumes/Nuo/tools/apache-zookeeper-3.6.2-bin/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
# 表示 zk 启动成功
soul admin启动配置dubbo
register: 填写我们刚启动的zk服务地址: zookeeper://localhost:2181
状态: 打开
启动soul-example-apache-dubbo-service
- 注册服务到admin
@Configuration
public class SoulApacheDubboClientConfiguration {
/**
* Apache dubbo service bean post processor alibaba dubbo service bean post processor.
*
* @param dubboConfig the dubbo config
* @return the alibaba dubbo service bean post processor
*/
@Bean
public ApacheDubboServiceBeanPostProcessor apacheDubboServiceBeanPostProcessor(final DubboConfig dubboConfig) {
return new ApacheDubboServiceBeanPostProcessor(dubboConfig);
}
/**
* Dubbo config dubbo config.
*
* @return the dubbo config
*/
@Bean
@ConfigurationProperties(prefix = "soul.dubbo")
public DubboConfig dubboConfig() {
return new DubboConfig();
}
}
dubbo服务注册admin运行方式与sprinigmvc类似。根据配置的admin地址和自动化注入Bean方式进行接口注入
public void onApplicationEvent(final ContextRefreshedEvent contextRefreshedEvent) {
if (Objects.nonNull(contextRefreshedEvent.getApplicationContext().getParent())) {
return;
}
// Fix bug(https://github.com/dromara/soul/issues/415), upload dubbo metadata on ContextRefreshedEvent
Map<String, ServiceBean> serviceBean = contextRefreshedEvent.getApplicationContext().getBeansOfType(ServiceBean.class);
for (Map.Entry<String, ServiceBean> entry : serviceBean.entrySet()) {
executorService.execute(() -> handler(entry.getValue()));
}
使用反射获取需要代理的接口,首先根据@Servic获取类,然后根据类获取对应接口的名称
然后将获取到的名称同步到admin,下图就是自动获取配置并且沾水
演示dubbo转发
分析example-dubbo启动完成接口的注册
soul-spring-boot-starter-client-apache-dubbo注入ApacheDubboServiceBeanPostProcessor初始化
自定义boot starter会自动注入
public ApacheDubboServiceBeanPostProcessor(final DubboConfig dubboConfig) {
String contextPath = dubboConfig.getContextPath();
String adminUrl = dubboConfig.getAdminUrl();
if (StringUtils.isEmpty(contextPath)
|| StringUtils.isEmpty(adminUrl)) {
throw new RuntimeException("apache dubbo client must config the contextPath, adminUrl");
}
this.dubboConfig = dubboConfig;
// 拼接发送注册地址的url (admin 接口地址)
url = dubboConfig.getAdminUrl() + "/soul-client/dubbo-register";
executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
}
ApplicationListene<ContextRefreshedEvent>
ContextRefreshedEvent:容器刷新完成(所有bean都完全创建)会发布这个事件
public class ApacheDubboServiceBeanPostProcessor implements ApplicationListener<ContextRefreshedEvent>
dubbo 客户端初始化与divide 初始化缺少一个Bean后置处理,原因是Dubbo 依赖注册中心,要将所有的接口类都注册到注册中心之后,我们在发起注册。而ApplicationListener触发是发生在IOC容器初始化完成Bean后置处理之后。
onApplicationEvent
@Override
public void onApplicationEvent(final ContextRefreshedEvent contextRefreshedEvent) {
if (Objects.nonNull(contextRefreshedEvent.getApplicationContext().getParent())) {
return;
}
// Fix bug(https://github.com/dromara/soul/issues/415), upload dubbo metadata on ContextRefreshedEvent
Map<String, ServiceBean> serviceBean = contextRefreshedEvent.getApplicationContext().getBeansOfType(ServiceBean.class);
for (Map.Entry<String, ServiceBean> entry : serviceBean.entrySet()) {
executorService.execute(() -> handler(entry.getValue()));
}
}
通过@ImportResource装备dubbo的两个service
@ImportResource({"classpath:spring-dubbo.xml"})
public class TestApacheDubboApplication {
/**
* Main Entrance.
*
* @param args startup arguments
*/
public static void main(final String[] args) {
SpringApplication.run(TestApacheDubboApplication.class, args);
}
}
<dubbo:service timeout="10000" interface="org.dromara.soul.examples.dubbo.api.service.DubboTestService" ref="dubboTestService"/>
<dubbo:service timeout="10000" interface="org.dromara.soul.examples.dubbo.api.service.DubboMultiParamService" ref="dubboMultiParamService"/>
接口注册到admin
private void handler(final ServiceBean serviceBean) {
Class<?> clazz = serviceBean.getRef().getClass();
if (ClassUtils.isCglibProxyClass(clazz)) {
String superClassName = clazz.getGenericSuperclass().getTypeName();
try {
clazz = Class.forName(superClassName);
} catch (ClassNotFoundException e) {
log.error(String.format("class not found: %s", superClassName));
return;
}
}
final Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(clazz);
for (Method method : methods) {
SoulDubboClient soulDubboClient = method.getAnnotation(SoulDubboClient.class);
if (Objects.nonNull(soulDubboClient)) {
RegisterUtils.doRegister(buildJsonParams(serviceBean, soulDubboClient, method), url, RpcTypeEnum.DUBBO);
}
}
}
使用反射拿到接口参数,注册到admin