關於測試過程中直接調用rpc接口及定時器方法的一點思路

前言:東家系統是用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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章