前言:东家系统是用dubbo做的微服务架构,按不同业务模块分出不同的dubbo服务,大大小小有几十个项目,项目之间都是通过rpc接口通信,测试过程经常遇到当前测试项目依赖其他项目的处理结果(rpc回调)而受其他项目服务异常影响阻塞测试,这是问题之一;为保证数据完整性和一致性,项目中用到了大量的定时任务去处理一些业务,定时任务的触发器虽说是可配的,但想要灵活控制定时任务的执行,还是有点麻烦,这是问题之二。这两个问题困扰笔者很久,一直没有思路,也苦于加班加到怀疑人生无心思考,直到有位同事提出一个思路:写一个rpc接口集成到公司项目中,再写一个web服务,提供http接口供测试同事通过http协议调用,在该http接口中通过泛化调用我们集成到公司项目的rpc接口,在rpc接口中再通过反射调用公司项目的方法,这样可以解决上面说的两个问题。当时听到这个idea简直眼前一亮,这位同事平时点子也多,和他共事算是在东家为数不多开心的事情之一。
废话说完了,开干。
简单图解:
涉及知识储备:springmvc、dubbo、java反射,对这些不熟的同学可以去百度了解下,这里不做展开。
1.编写web服务
1.1 请求参数DTO
java">package com.etyero.entity;
import com.alibaba.fastjson.JSONArray;
/**
* RequestDTO
*
* @author lijialong
*/
public class RequestDTO {
private String targetService; // 目标类
private String targetMethod; // 目标方法
private JSONArray params; // 目标方法入参
public String getTargetService() {
return targetService;
}
public void setTargetService(String targetService) {
this.targetService = targetService;
}
public String getTargetMethod() {
return targetMethod;
}
public void setTargetMethod(String targetMethod) {
this.targetMethod = targetMethod;
}
public JSONArray getParams() {
return params;
}
public void setParams(JSONArray params) {
this.params = params;
}
}
1.2 编写controller,处理入参,泛化调用rpc。
java">package com.etyero.controller;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import com.alibaba.dubbo.rpc.service.GenericService;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.etyero.entity.RequestDTO;
/**
* http转rpc
*
* @author lijialong
*/
@Controller
@RequestMapping("/customerTest")
public class ConsumerController {
private Logger logger = LoggerFactory.getLogger(ConsumerController.class);
private final static String className = "invokeMethodService";
private final static String methodName = "doInvoke";
@RequestMapping("/doTest")
public void sayHello(@RequestBody RequestDTO requestDTO, HttpServletResponse response) {
response.setContentType("application/json; charset=utf-8");
PrintWriter out;
JSONObject resJson = new JSONObject();
Object result;
int status = 1;
String msg = "success";
try {
// 上下文获取spring注入的bean
WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
String targetService = requestDTO.getTargetService();
String targetMethod = requestDTO.getTargetMethod();
JSONArray paramsArr = requestDTO.getParams();
String[] paramType = { "java.lang.String", "java.lang.String", "com.alibaba.fastjson.JSONArray" };
Object[] paramValue = { targetService, targetMethod, paramsArr };
// 泛化调用rpc
GenericService easyCommonService = (GenericService) wac.getBean(className);
result = easyCommonService.$invoke(methodName, paramType, paramValue);
} catch (Exception e) {
logger.info("异常----{}", e);
result = e;
status = 0;
msg = "failed";
}
resJson.put("status", status);
resJson.put("result", result);
resJson.put("msg", msg);
try {
out = response.getWriter();
out.write(resJson.toString());
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
1.3 在dubbo消费者配置文件向注册中心订阅所需的服务,笔者东家用的是zookeeper,这里也以zk为示例:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
">
<dubbo:application name="sayhello_consumer" owner="programmer"
organization="dubbox" />
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<!-- 向zk订阅自己所需的服务 -->
<dubbo:reference id="invokeMethodService" interface="com.etyero.dubboprovider.InvokeMethodService" generic="true"/>
</beans>
2.编写集成到公司项目中的rpc接口。
2.1.定义service接口
java">package com.etyero.dubboprovider;
import com.alibaba.fastjson.JSONArray;
public interface InvokeMethodService {
Object doInvoke(String targetClass,String targetMethod,JSONArray paramobjArr);
}
2.2 接口实现类
java">package com.etyero.dubboprovider.impl;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.etyero.dubboprovider.InvokeMethodService;
/**
* 反射调用目标类的函数
*
* @author lijialong
*/
@Service("invokeMethodService")
public class InvokeMethodServiceImpl implements InvokeMethodService {
private static final Logger logger = LoggerFactory.getLogger(InvokeMethodServiceImpl.class);
/**
* 反射调用目标类的函数
*
* @param targetClass
* 目标调用类
* @param targetMethod
* 目标调用函数
* @param paramobjArr
* 目标函数的入参
*/
@Override
public Object doInvoke(String targetClass, String targetMethod, JSONArray paramobjArr) {
Object obj = null;
try {
WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
Class<?> objClass = wac.getBean(targetClass).getClass();
int length = paramobjArr.size();
Object[] paramobj = new Object[length];
@SuppressWarnings("rawtypes")
Class[] argTypes = new Class[length];
for (int i = 0; i < length; i++) {
JSONObject jsonTemp = paramobjArr.getJSONObject(i);
String argType = jsonTemp.getString("paramType");
// 设置目标函数的入参类型
argTypes[i] = Class.forName(argType);
try {
// 入参为实体类
paramobj[i] = convertJsonToEntity(argType, jsonTemp.getJSONObject("paramValue"));
} catch (Exception e) {
// 入参为基础数据类型
paramobj[i] = jsonTemp.get("paramValue");
}
}
Method method = objClass.getMethod(targetMethod, argTypes);
method.setAccessible(true);
obj = method.invoke(wac.getBean(targetClass), paramobj);
} catch (Exception e) {
logger.info("反射执行异常----{}", e);
}
return obj;
}
/**
* json转换为实体类
*
* @param className
* 实体类名
* @param jsonObject
* json String
*/
private static Object convertJsonToEntity(String className, JSONObject jsonObject) throws Exception {
Class<?> class1 = Class.forName(className);
BeanInfo beanInfo = Introspector.getBeanInfo(class1);
Object object = class1.newInstance();
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor descriptor : propertyDescriptors) {
String proName = descriptor.getName();
if (jsonObject.containsKey(proName)) {
Object value = jsonObject.get(proName);
Method method = descriptor.getWriteMethod();
method.invoke(object, value);
}
}
return object;
}
}
2.3 在公司项目的dubbo服务配置文件加上该服务。
<dubbo:service interface="com.etyero.dubboprovider.InvokeMethodService" ref="invokeMethodService" />
<bean id="invokeMethodService" class="com.etyero.dubboprovider.impl.InvokeMethodServiceImpl" />
3.调用示例。
3.1 调用定时任务方法
3.2 调用其他rpc方法
以上,贵在思路,demo源码已上传度盘,按需自取。
链接: https://pan.baidu.com/s/1ukrFgLZjzXALtd_9v3GrdQ 密码: syju