本文为手写版rpc通信框架,主要为了解决服务间的通信复杂度,仅实现通信过程,不添加注册中心路由等组件,非常适合小型项目的解耦合。
1.自定义需要的注解 EmokeRpcClient,EmokeMapping
@Documented @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) public @interface EmokeMapping { String requestMapping(); Method method(); }
@Documented @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) public @interface EmokeRpcClient { /** * 请求的域名头 */ String zone(); Class<?> fallback(); }
2.为了调用端代码简洁,此处对接口端进行动态代理,使用过程中仅仅需要定义接口,无需关注业务逻辑
public class EmokeProxy { public static Object createProxy(Class<?> cls) { return Proxy.newProxyInstance( cls.getClassLoader(), new Class[]{cls}, new EmokeInvocationHandler()); } }
3.核心类EmokeInvocationHandler,用来代理实现rpc的通信过程
package com.emoke.core.rpc.proxy; import com.emoke.core.rpc.annotation.EmokeMapping; import com.emoke.core.rpc.annotation.EmokeRpcClient; import com.emoke.core.rpc.exception.RpcException; import com.emoke.core.rpc.http.HttpUtil; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.net.ConnectException; import java.net.SocketTimeoutException; import java.util.HashMap; import java.util.Map; public class EmokeInvocationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //已实现的具体类 if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { t.printStackTrace(); } //接口 } else { return run(method, args); } return null; } /** * 实现接口的核心方法 * * @param method:方法 * @param args:参数 * @return Object */ public Object run(Method method, Object[] args) { Class<?> declaringClass = method.getDeclaringClass(); EmokeRpcClient emokeRpcClient = declaringClass.getAnnotation(EmokeRpcClient.class); EmokeMapping emokeGetMapping = method.getAnnotation(EmokeMapping.class); Class<?> fallback= emokeRpcClient.fallback(); String zone = emokeRpcClient.zone(); String api = emokeGetMapping.requestMapping(); Class<?> returnType = method.getReturnType(); Parameter[] parameters = method.getParameters(); // System.out.println("parameters=====" + parameters.length); // System.out.println("主域名:" + zone); // System.out.println("api:" + api); // System.out.println("requestMethod:" + requestMethod); // System.out.println("主类是:" + declaringClass); // System.out.println("方法是:" + method.getName()); // System.out.println("返回对象是:" + returnType.getName()); Map<String, Object> getMap = new HashMap<>(); for (int i = 0; i < parameters.length; i++) { Parameter parameter = parameters[i]; getMap.put(parameter.getName(), args[i].toString()); } long s1=System.currentTimeMillis(); try { switch (emokeGetMapping.method()) { case GET: return HttpUtil.getInstance().get(zone + api, getMap, Class.forName(returnType.getName())); case PUT: case POST: return HttpUtil.getInstance().post(zone + api, getMap, Class.forName(returnType.getName())); case DELETE: break; } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IOException e) { System.out.println("ssss===="+(System.currentTimeMillis()-s1)); Object fallbackObj=null; try { fallbackObj= fallback.newInstance(); } catch (InstantiationException | IllegalAccessException ex) { ex.printStackTrace(); } if(e instanceof ConnectException){ //获取fallback try { return fallback.getMethod("onException", RpcException.class).invoke(fallbackObj,new RpcException(RpcException.Type.ConnectionException)); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) { ex.printStackTrace(); } }else if(e instanceof SocketTimeoutException){ //获取fallback try { return fallback.getMethod("onException", RpcException.class).invoke(fallbackObj,new RpcException(RpcException.Type.ConnectTimeoutException)); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) { ex.printStackTrace(); } } } return null; } }
4.测试调用,先定义好rpcclient数据,设置好参数,以及熔断器服务降级回调TestServiceFallback
import com.emoke.core.common.pojo.Result; import com.emoke.core.rpc.annotation.EmokeMapping; import com.emoke.core.rpc.annotation.EmokeRpcClient; import com.emoke.core.rpc.annotation.Method; @EmokeRpcClient(zone = "http://localhost:8800",fallback =TestServiceFallback.class ) public interface TestService { @EmokeMapping(method = Method.GET,requestMapping = "/addressBook/get") Result say(String name); }
import com.emoke.core.common.pojo.Result; import com.emoke.core.common.pojo.ResultCode; import com.emoke.core.rpc.FallbackFactory; import com.emoke.core.rpc.exception.RpcException; public class TestServiceFallback implements FallbackFactory<Result> { @Override public Result onException(RpcException e) { switch (e.getType()){ case ConnectionException: return new Result(ResultCode.CODE_ERROR,null); case ConnectTimeoutException: return new Result(ResultCode.ACCOUNT_HAS_EXPIRED,null); } return null; } }
目前熔断器定义了两个异常类型,一个是连接异常,即服务不可用,另一个是连接超时,使用者可以在此返回具体信息,以达到服务调用错误而正常通信。
5.调用测试方法
@GetMapping("/test") public Result test(String name) { TestService testService= (TestService) EmokeProxy.createProxy(TestService.class); return testService.say(name); }