dubbo yml配置_基于dubbo动态调用实现的回调

业务背景

实际开发中,经常会碰到需要A服务调用B服务,B服务接收到请求返回调用成功,实际处理需要异步,当异步处理完成之后,再通知A,如下图:

5a063fdc224713e5a8cf3455ec181d98.png

我们希望的B提供的接口与回调的方式是统一的,这样B就能实现与业务解耦,业务有增加,B服务不需要做任何变更即可支撑。

怎么实现

12步骤是同步流程,我们可以有很多方式对接B。在java微服务中,我们可以通过http的请求,可以通过mq的方式,也可以通过dubbo(rpc)。

3步骤在java微服务中,也可以通过上述3种方式来操作,可以与1方式相同,也可以不同。

基于dubbo方案

对应标题,基于dubbo的方案,来实现样例

有接触dubbo的同学都知道,dubbo是通过接口实现的方式提供服务的,那么就有可能出现同一个接口有多个实现,provider就有很多同一个接口实现的服务,这个时候就需要有唯一标识来区分提供的服务. consumer通过唯一标识来指定调用具体的服务。

  • 通常我们可以通过以下几个方式来区分

    • address:注册中心地址

    • group: dubbo服务分组

    • version: dubbo服务版本

以下为基于dubbo具体的实现方案,默认为同一个注册中心,使用不同的group来区分

项目结构

基于spring boot 2.3.0 + dubbo 2.7.7 + zookeeper的实现

主要的maven依赖:

org.springframework.bootspring-boot-starter-parent2.3.0.RELEASE
org.apache.dubbodubbo-spring-boot-starter2.7.7

├─common
│ ├─src
│ │ └─main
│ │ ├─java // 请求接口和回调接口, 和回调接口实现,拦截器
│ │ └─resources
│ │ └─META-INF
│ │ └─dubbo // dubbo请求接口拦截器spi
├─service_A1 // 依赖common包
│ ├─src
│ │ └─main
│ │ ├─java
│ │ └─resources
├─service_A2 // 依赖common包
│ ├─src
│ │ └─main
│ │ ├─java
└─service_B // 依赖common包,实现dubbo动态回调
├─src
│ └─main
│ └─resources

1. common
// 基础对象, 封装了调用方应用名和回调dubbo接口的group
public class BaseDTO implements Serializable {
// 服务名
private String applicationName;

// dubbo group
private String group;
}
// dubbo接口的请求参数,继承基础对象
public class CallbackDTO extends BaseDTO {
// 具体的接口参数
private String message;
}
// 步骤`1`推送接口dubbo
public interface DubboFacade {

void test(CallbackDTO callbackDTO);
}
// 步骤`3`回调接口
public interface CallbackFacade {

void invoke(CallbackDTO CallbackDTO);
}
// 回调接口实现
// 通过定义一个spring.application.id 来当做group的唯一标识,每个依赖common的服务如果需要对外暴露dubbo服务就需要配置这个属性。
@DubboService(group = "${spring.application.id}")
public class CallbackService implements CallbackFacade {

@Override
public void invoke(CallbackDTO callbackDTO) {
System.out.println("========begin======");
System.out.println(callbackDTO.getMessage());
System.out.println(callbackDTO.getApplicationName());
System.out.println(callbackDTO.getGroup());
System.out.println("=========end=======");
}
}
// 步骤`1`dubbo请求拦截器,用户将参数注入到请求对象中
// 需要标识为spring bean才能注入变量Environment
@Component
public class DubboFacadeFilter implements Filter {

/**Environment是Spring当前应用的配置对象,可以理解为application.properties本身,大家可以看一下这个对象具体的属性,应该有你想要的值,也可以使用setter其他对象实现你要的逻辑,比如dubbo的接口什么的**/
private Environment environment;

public Environment getEnvironment() {
return environment;
}

public void setEnvironment(Environment environment) {
this.environment = environment;
}

@Override
public Result invoke(Invoker> invoker, Invocation invocation) throws RpcException {
System.out.println("拦截进来了");
Object[] arguments = invocation.getArguments();
if (arguments != null) {
if (arguments.length == 1) {
try {
Object argument = arguments[0];
// 拦截器对上面定义的请求接口进行参数设置
if (argument instanceof BaseDTO) {
BaseDTO dto = (BaseDTO) argument;
dto.setApplicationName(environment.getProperty("spring.application.name"));
dto.setGroup(environment.getProperty("spring.application.id"));
}
} catch (Exception e) {
System.out.println("类型转换不了" + e.getMessage());
}
}
}
return invoker.invoke(invocation);
}
}

还有一个很重要的需要配置dubboFilter

1a728dac6115ebb80e7084e5c21458cd.png

文件路径: resources/META-INF/dubbo/org.apache.dubbo.rpc.Filter
文件内容: dubboFacadeFilter=com.github.dubbo.DubboFacadeFilter

2. service-A

A1 A2 是一模一样的,只是spring.application.id 配置不同,标识为不同的服务
为了方便测试,创建为一个spring boot web项目


@SpringBootApplication // 标识为spring boot项目
@EnableDubbo // 启动dubbo支持
@DubboComponentScan(value = {"com.github.dubbo.service"}) // 回调接口的实现类所在的包
@ComponentScan(basePackages = {"com.github.dubbo", "com.github.dubbo.provider"}) // 回调接口的实现类所在的包 和 本项目需要注入bean的包路径
public class ServiceAApplication {

public static void main(String[] args) {
SpringApplication.run(Service1Application.class, args);
}
}
@RestController
public class TestController {

// 注入dubbo请求接口,配置拦截器
@DubboReference(filter = "dubboFacadeFilter ")
private DubboFacade dubboFacade;

@RequestMapping("/test")
public String test1(String message) {
System.out.println("开始调用重试: " + message);
CallbackDTO callbackDTO = new CallbackDTO();
callbackDTO.setMessage(message);
dubboFacade.test(callbackDTO);
return "success: " + message;
}
}
## application.yml配置
spring:
application:
name: service_A1
id: service_a1
## 以下省略dubbo配置
3.service_B

也创建为一个spring boot web项目,提供异步延迟动态回调的功能

//  DubboFacade 实现 DubboService
@DubboService
public class DubboService implements DubboFacade {

@Autowired
private CallbackInvoker callbackInvoker;

@Override
public void test(CallbackDTO callbackDTO) {
System.out.println("调用进来了: " + callbackDTO.getGroup());
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
// 延迟操作 方便看出效果
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("开始回调: " + callbackDTO.getGroup());
callbackInvoker.invoke(callbackDTO);
}
};
// 异步调用
Thread thread = new Thread(runnable);
thread.start();
}
}
// 动态调用CallbackInvoker
~~~
@Component
public class CallbackInvoker implements ApplicationContextAware {

private static Map> referenceConfigMap = new ConcurrentHashMap<>();
private static Map registryConfigMap = new ConcurrentHashMap<>();
private ApplicationContext applicationContext;
/**
* 获取注册中心地址
*
* @param group
* @return
*/
private RegistryConfig getRegistry(String group) {
RegistryConfig registryConfig = registryConfigMap.get(group);
if (null == registryConfig) {
registryConfig = new RegistryConfig();
RegistryConfig bean = applicationContext.getBean(RegistryConfig.class);
registryConfig.setAddress(bean.getAddress());
registryConfig.setProtocol(bean.getProtocol());
registryConfigMap.put(group, registryConfig);
}
return registryConfig;
}
/**
* 获取服务的代理对象
* @param applicationName
* @param group
* @return
*/
private ReferenceBean getReferenceConfig(String applicationName, String group) {
ReferenceBean referenceBean = referenceConfigMap.get(applicationName);
if (null == referenceBean) {
referenceBean = new ReferenceBean<>();
referenceBean.setRegistry(getRegistry(group));
referenceBean.setInterface(CallbackFacade.class);
referenceBean.setGroup(group);
referenceConfigMap.put(applicationName, referenceBean);
}
return referenceBean;
}
public void invoke(CallbackDTO callbackDTO) {
ReferenceBean referenceConfig = getReferenceConfig(callbackDTO.getApplicationName(), callbackDTO.getGroup());
if (null != referenceConfig) {
CallbackFacade callback = referenceConfig.get();
if (callback != null) {
callback.invoke(callbackDTO);
}
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
测试结果
1.  A1服务
调用service_A1的http接口:
/test?message=serviceA1
立刻返回了:
success: serviceA1
然后查看service_A1日志,可以看到触发了拦截器

2. B服务
看到调用进来了:调用进来了: service_a1
等一会之后,看到: 开始回调: service_a1

3. A1服务
看到打印了:=====start=== ====end====的日志

同样的方式请求A2服务的,都能准确的响应到对应的group服务

总结

上面回调实现方案是基于dubbo的方式:

  1. 在spring cloud中也可以基于http的方式,通过服务名的方式区分。

  2. 也可以mq的方式通过发送不同的队列,对应的服务进行消费。

方式有很多种,主要目的是为了让接入方,不需要编写多余的代码,完全解耦的方式实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值