目標
-
基于apache-dubbo消息轉(zhuǎn)發(fā)
zookeeper 本地注冊中心搭建
soul admin 本地配置dubbo插件
-
啟動soul-example-apache-dubbo-service
- dubbo注冊服務(wù)到admin
分別演示允許轉(zhuǎn)發(fā)與過濾轉(zhuǎn)發(fā)
-
分析example-dubbo啟動完成接口的注冊
- soul-spring-boot-starter-client-apache-dubbo注入
- ApacheDubboServiceBeanPostProcessor
- onApplicationEvent
總結(jié)
基于apache-dubbo消息轉(zhuǎn)發(fā)
由于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服務(wù)
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服務(wù)地址: zookeeper://localhost:2181
狀態(tài): 打開
啟動soul-example-apache-dubbo-service
- 注冊服務(wù)到admin
image
@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服務(wù)注冊admin運行方式與sprinigmvc類似。根據(jù)配置的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()));
}
使用反射獲取需要代理的接口,首先根據(jù)@Servic獲取類,然后根據(jù)類獲取對應(yīng)接口的名稱
image
然后將獲取到的名稱同步到admin,下圖就是自動獲取配置并且沾水
image
演示dubbo轉(zhuǎn)發(fā)
image
image
分析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;
// 拼接發(fā)送注冊地址的url (admin 接口地址)
url = dubboConfig.getAdminUrl() + "/soul-client/dubbo-register";
executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
}
ApplicationListene<ContextRefreshedEvent>
ContextRefreshedEvent:容器刷新完成(所有bean都完全創(chuàng)建)會發(fā)布這個事件
public class ApacheDubboServiceBeanPostProcessor implements ApplicationListener<ContextRefreshedEvent>
dubbo 客戶端初始化與divide 初始化缺少一個Bean后置處理,原因是Dubbo 依賴注冊中心,要將所有的接口類都注冊到注冊中心之后,我們在發(fā)起注冊。而ApplicationListener觸發(fā)是發(fā)生在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);
}
}
}
使用反射拿到接口參數(shù),注冊到admin