背景
目前在学习一些中间件,里面看到了一个词是叫泛化调用, 其实这个场景在JAVA中比较常见。我们常用的有反射,反射就是我知道类名称、类方法和参数,调用一个Object的类,但是在HTTP或者RPC远程调用过程中,我们一般会引入对方的SDK,从而引入接口规范和协议。
 但是从一个中间件的角度触发,少依赖实现解耦,接入的低成本,少发布都是一个必须考虑的点。
 因此本文从HTTP和DUBBO角度分析系统设计中泛化调用。
基于Cloud的泛化调用
以RocketMQ的事务消息场景为例,假设我是一个独立消息微服务,如下图所示。正常的ClientA完成了步骤1和步骤2后为一次正常结束,但是如果出现了ClientB的情况,那么就需要独立消息微服务(我)取回调ClientB的某个方法。当然你可以要求各个Client都用你定义的接口,但是我想玩花活,因此我的目标:各个Client可定义自己的方法,当然返回协议要统一,独立消息微服务可以在消息有问题的时候去调用各方且不需要引入各方的SDK。
 
 当然我们也需要约定一种协议,只不过是不需要引入SDK的协议。
appName://methodPath/msgKey
以上面的场景的为例,步骤1的发送内容有
| 字段 | 含义 | 
|---|---|
| bizCode | 多租户场景,每一个Client都有唯一的标识 | 
| msgBody | 消息内容,需要独立消费微服务发送的消息 | 
| appName | 服务提供者名称,即注册到Eureka中心的名称 | 
| msgKey | 1)用于回调消息用 2)用于消息上的key | 
| methodPath | HTTP类型为路径 | 
	/**
	 * ribbon 负载均衡客户端
	 */
	@Autowired
	private LoadBalancerClient loadBalancerClient;
	/**
	 * 消息微服务(我)向支付微服务(clientA)发起状态查询,并且支付微服务返回业务执行状态。
	 * @param msg
	 * @return
	 */
	public boolean checkMsgByHTTP(Msg msg){
		//解析订单号
		String msgKey=msg.getMsgKey();
		String methodPath = msg.getMethodPath();
		//拿到IP和端口
		ServiceInstance si=loadBalancerClient.choose(msg.getAppName());
		StringBuffer sb=new StringBuffer("");
		sb.append("http://");
		sb.append(si.getHost());
		sb.append(":");
		sb.append(si.getPort());
		sb.append("/");
		sb.append(methodPath);
		sb.append("/");
		sb.append(msgKey);
		LOGGER.info("回调服务地址:{}",sb.toString());
		调接口
		RestTemplate restTemplate=new RestTemplate();
		Boolean bo = restTemplate.getForObject(sb.toString(), Boolean.class, "java");
		return bo;
	}
基于DUBBO的泛化调用
目前公司中有一个消费MQ的中间件是AMB,解决的痛点是 各个MQ的消费者自己做限流、转换、不好调试。正常下游提供的就是RPC的接口,所以AMB要解决的问题之一就是调用下游的RPC接口。
 
代码DEMO如下图所示,
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.rpc.service.GenericService;
public class GenericInvokeDemo {
    public static void main(String[] args) {
        // 初始化 Dubbo 应用配置
        ApplicationConfig application = new ApplicationConfig();
        application.setName("generic-consumer");
        // 创建 Dubbo 泛化服务引用配置
        ReferenceConfig<GenericService> reference = new ReferenceConfig<>();
        reference.setApplication(application);
        reference.setInterface("com.example.service.SomeService"); // 设置服务接口名
        reference.setUrl("dubbo://127.0.0.1:20880"); // 设置服务提供者地址
        reference.setGeneric(true); // 开启泛化调用
        // 引用远程服务
        GenericService genericService = reference.get();
        // 构造泛化调用参数
        Object[] parameters = new Object[] { "parameter1", "parameter2" };
        // 进行泛化调用
        Object result = genericService.$invoke("someMethod", new String[] { "java.lang.String", "java.lang.String" }, parameters);
        // 处理调用结果
        System.out.println("Result: " + result);
    }
}
总结
稳定性在大厂是重要的,所以少发布是重点。
 泛化调用等价解耦,引入过多的SDK解决包冲突就是个坑。
作者阿里淘天Java开发工程师,CSDN博客专家,阿里云博客专家,专注于后端技术的分享。如果你迷茫,不妨来瞅瞅码农的轨迹。模拟面试、简历辅导、项目亮点、校招内推加VX:CHAI956056312



















