前言:東家系統是用dubbo做的微服務架構,按不同業務模塊分出不同的dubbo服務,大大小小有幾十個項目,項目之間都是通過rpc接口通信,測試過程經常遇到當前測試項目依賴其他項目的處理結果(rpc回調)而受其他項目服務異常影響阻塞測試,這是問題之一;爲保證數據完整性和一致性,項目中用到了大量的定時任務去處理一些業務,定時任務的觸發器雖說是可配的,但想要靈活控制定時任務的執行,還是有點麻煩,這是問題之二。這兩個問題困擾筆者很久,一直沒有思路,也苦於加班加到懷疑人生無心思考,直到有位同事提出一個思路:寫一個rpc接口集成到公司項目中,再寫一個web服務,提供http接口供測試同事通過http協議調用,在該http接口中通過泛化調用我們集成到公司項目的rpc接口,在rpc接口中再通過反射調用公司項目的方法,這樣可以解決上面說的兩個問題。當時聽到這個idea簡直眼前一亮,這位同事平時點子也多,和他共事算是在東家爲數不多開心的事情之一。
廢話說完了,開幹。
簡單圖解:
涉及知識儲備:springmvc、dubbo、java反射,對這些不熟的同學可以去百度瞭解下,這裏不做展開。
1.編寫web服務
1.1 請求參數DTO
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。
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接口
package com.etyero.dubboprovider;
import com.alibaba.fastjson.JSONArray;
public interface InvokeMethodService {
Object doInvoke(String targetClass,String targetMethod,JSONArray paramobjArr);
}
2.2 接口實現類
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