目标
- 运行 examples下面的 apache-dubbo 服务
- 学习文档,结合 dubbo 插件,发起 http 请求 soul 网关,体验 dubbo 代理
soul 网关接入 Dubbo 应用
参考官方文档:https://dromara.org/zh-cn/doc...
引入相关依赖(官方示例默认开启的alibaba-dubbo,具体参考:soul-examples-dubbo)
soul-bootstrap
新增如下依赖:org.dromara soul-spring-boot-starter-plugin-apache-dubbo ${project.version} org.apache.dubbo dubbo 2.7.5 org.apache.curator curator-client 4.0.1 org.apache.curator curator-framework 4.0.1 org.apache.curator curator-recipes 4.0.1 soul-examples-apache-dubbo-service
新增如下依赖:org.dromara soul-spring-boot-starter-plugin-apache-dubbo 2.2.1 org.apache.dubbo dubbo 2.7.5 org.apache.zookeeper zookeeper 3.5.6 org.slf4j slf4j-log4j12 log4j log4j application.yml
添加相关配置soul: dubbo: adminUrl: http://localhost:9095 contextPath: /dubbo appName: dubbo # adminUrl: 为你启动的soul-admin 项目的ip + 端口,注意要加 http:// # contextPath: 为你的这个项目在soul网关的路由前缀,比如/order ,/product 等等,网关会根据你的这个前 缀来进行路由. # appName:你的应用名称,不配置的话,会默认取 dubbo配置中application 中的名称
dubbo 插件设置
接口注册到网关
- dubbo 服务实现类的方法上加上
@SoulDubboClient
注解,表示该接口方法注册到网关
- dubbo 服务实现类的方法上加上
- 先启动
zk
,然后再启动TestApacheDubboApplication
,输出日志dubbo client register success
,表示dubbo
接口已经发布到 soul 网关
@SoulDubboClient 注解解析
服务在启动时会将类方法上的 @SoulDubboClient
注解注册到网关,接下来分析一下@SoulDubboClient
注册逻辑。
soul-examples-apache-dubbo-service
添加了soul-spring-boot-starter-plugin-apache-dubbo
依赖,注册逻辑应该就在这个自定义的spring-boot-starter
中。
首先注释掉DubboTestServicel
类中findById
方法上的@SoulDubboClient
注解
@Override
//@SoulDubboClient(path = "/findById", desc = "Query by Id")
public DubboTest findById(final String id) {
DubboTest dubboTest = new DubboTest();
dubboTest.setId(id);
dubboTest.setName("hello world Soul Apache, findById");
return dubboTest;
}
soul-spring-boot-starter-client-apache-dubbo 自定义spring-boot-starter
通过 spring.factories 加载了SoulApacheDubboClientConfiguration
类:
@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();
}
}
SoulApacheDubboClientConfiguration
加载了ApacheDubboServiceBeanPostProcessor
实例,该实例实现了ApplicationListener
接口,该类中的主要方法:
public ApacheDubboServiceBeanPostProcessor(final DubboConfig dubboConfig) {
//获取 application.yml 文件中的配置信息
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 = dubboConfig.getAdminUrl() + "/soul-client/dubbo-register";
executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
}
@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
//根据 beanType 获取所有的 bean,这里获取的是 spring-dubbo.xml 中 中的DubboTestService和 //DubboMultiParamService
//ServiceBean.class:org.apache.dubbo.config.spring.ServiceBean.class
Map serviceBean = contextRefreshedEvent.getApplicationContext().getBeansOfType(ServiceBean.class);
for (Map.Entry entry : serviceBean.entrySet()) {
executorService.execute(() -> handler(entry.getValue()));
}
}
处理类中的每个方法
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 soulDubboClient = method.getAnnotation(SoulDubboClient.class);
if (Objects.nonNull(soulDubboClient)) {
//远程调用,注册方法信息到 soul-admin
RegisterUtils.doRegister(buildJsonParams(serviceBean, soulDubboClient, method), url, RpcTypeEnum.DUBBO);
}
}
}
当获取到DubboTestServicel
类中findById
的方法时,由于我们上面注释了 @SoulDubboClient
注解,所有以这里获取 SoulDubboClient 的值为 null。
至此,@SoulDubboClient
注解分析完毕
问题
先记录一个问题,明天再看:soul 的数据同步是将数据库中的信息同步到本地缓存中,如果是已经通过 @SoulDubboClient
注解注册到soul-admin
的方法被删除了,数据库中的信息什么时候删除?例如上面findById
方法已经注释掉了 @SoulDubboClient
注解,服务重启时输出的注册信息已经没有了findById
方法,但是soul-admin
中dubbo 插件列表和元数据列表中依然存在findById
方法信息。