关于测试过程中直接调用rpc接口及定时器方法的一点思路

news/2024/5/19 2:26:52 标签: java, dubbo, rpc, 定时任务, 反射

前言:东家系统是用dubbo做的微服务架构,按不同业务模块分出不同的dubbo服务,大大小小有几十个项目,项目之间都是通过rpc接口通信,测试过程经常遇到当前测试项目依赖其他项目的处理结果(rpc回调)而受其他项目服务异常影响阻塞测试,这是问题之一;为保证数据完整性和一致性,项目中用到了大量的定时任务去处理一些业务,定时任务的触发器虽说是可配的,但想要灵活控制定时任务的执行,还是有点麻烦,这是问题之二。这两个问题困扰笔者很久,一直没有思路,也苦于加班加到怀疑人生无心思考,直到有位同事提出一个思路:写一个rpc接口集成到公司项目中,再写一个web服务,提供http接口供测试同事通过http协议调用,在该http接口中通过泛化调用我们集成到公司项目的rpc接口,在rpc接口中再通过反射调用公司项目的方法,这样可以解决上面说的两个问题。当时听到这个idea简直眼前一亮,这位同事平时点子也多,和他共事算是在东家为数不多开心的事情之一。

废话说完了,开干。

简单图解:

涉及知识储备:springmvc、dubbojava反射,对这些不熟的同学可以去百度了解下,这里不做展开。

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

 


http://www.niftyadmin.cn/n/1277462.html

相关文章

WinPcap学习(四)打开适配器并捕获数据包

打开设备的函数是pcap_open()。下面参数snaplen,flags和to_ms的解释说明 snaplen制定要捕获数据包中的哪些部分。在一些操作系统中&#xff08;比如xBSD和Win32&#xff09;&#xff0c;驱动可以被配置成只捕获数据包的初始化部分&#xff1a;这样可以减少应用间程序间复制数据…

Shiro框架基本知识及应用

1. Shiro 基本知识 1. 目前市面主流的安全框架&#xff1a; shiro&#xff1a;轻量级的&#xff0c;使用很方便&#xff0c;灵活&#xff0c;是apache提供的&#xff0c;在任何框架的SpringSecurity&#xff1a;是Spring家族的一部分&#xff0c;很多项目中会使用spring全家桶…

git使用命令, 特别:git checkout -b a 与 git branch a区别(转)

创建分支&#xff1a; $ git branch mybranch切换分支&#xff1a; $ git checkout mybranch创建并切换分支&#xff1a; $ git checkout -b mybranch 更新master主线上的东西到该分支上&#xff1a;$git rebase master 切换到master分支&#xff1a;$git checkout master 更新…

WinPcap学习(五)不用回调方法捕获数据包

主要是学习pcap_next_ex()函数如何代替pcap_loop()函数。 pcap_loop()函数是基于回调的原理来进行数据捕获&#xff0c;这是一种精妙的方法&#xff0c;并且在某些场合中&#xff0c;这是一种很好的选择。然而&#xff0c;处理回调有时候并不实用--它会增加程序的复杂度&#…

springboot 获取客户端IP地址方法

在使用springboot时&#xff0c;需要获取访问客户端的IP地址&#xff0c; //获取客户端IP地址private String getIpAddress() {String ip request.getHeader("x-forwarded-for");if(ip null || ip.length() 0 || "unknow".equalsIgnoreCase(ip)) {ip …

springboot 获取当前项目的端口号 ip

增加配置类 Component public class IpConfiguration implements ApplicationListener<WebServerInitializedEvent> {private int serverPort;Overridepublic void onApplicationEvent(WebServerInitializedEvent event) {this.serverPort event.getWebServer().getPor…

WinPcap学习(六)过滤数据包

用来过滤数据包的函数是pcap_compile()和pcap_setfilter()。 pcap_complie()它将一个高层的布尔过滤表达式编译成一个能够被过滤引擎所解释的低层的字节码。 pcap_setfilter()将一个过滤器与内核捕获会话相关联。当pcap_setfilter&#xff08;&#xff09;被调用时&#xff0…

SQLite 数据类型总结

SQLite 数据类型总结 1。严格地说&#xff0c;SQLite 没有数据类型。SQLite 使用动态数据类型&#xff0c;即&#xff1a;数据的类型取决于数据本身&#xff0c;而不是它的容器&#xff08;字段&#xff09; 2。存储类型&#xff08;Storage Class&#xff09;&#xff1a;数据…