手写简易版rpc

3 篇文章 0 订阅
3 篇文章 0 订阅

本文为手写版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);
}

      

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
介绍RCP的实现原理 目录 1. 前言 2 2. 基本概念 3 2.1. IDL 3 2.2. 代理(Proxy) 3 2.3. 存根(Stub) 4 3. 三要素 4 3.1. 网络通讯 4 3.2. 消息编解码 5 3.3. IDL编译器 5 4. flex和bison 5 4.1. 准备概念 5 4.1.1. 正则表达式(regex/regexp) 6 4.1.2. 符号∈ 6 4.1.3. 终结符/非终结符/产生式 6 4.1.4. 记号(Token) 6 4.1.5. 形式文法 7 4.1.6. 上下文无关文法(CFG) 7 4.1.7. BNF 8 4.1.8. 推导 8 4.1.9. 语法树 8 4.1.10. LL(k) 9 4.1.11. LR(k) 9 4.1.12. LALR(k) 9 4.1.13. GLR 9 4.1.14. 移进/归约 9 4.2. flex和bison文件格式 9 4.2.1. 定义部分 10 4.2.2. 规则部分 10 4.2.3. 用户子例程部分 10 4.3. flex基础 10 4.3.1. flex文件格式 11 4.3.2. 选项 11 4.3.3. 名字定义 11 4.3.4. 词法规则 12 4.3.5. 匹配规则 12 4.3.6. %option 13 4.3.7. 全局变量yytext 13 4.3.8. 全局变量yyval 13 4.3.9. 全局变量yyleng 13 4.3.10. 全局函数yylex 13 4.3.11. 全局函数yywrap 13 4.4. bison基础 14 4.4.1. bison文件格式 14 4.4.2. %union 14 4.4.3. %token 15 4.4.4. 全局函数yyerror() 15 4.4.5. 全局函数yyparse() 15 4.5. 例1:单词计数 15 4.5.1. 目的 15 4.5.2. flex词法文件wc.l 16 4.5.3. Makefile 16 4.6. 例2:表达式 17 4.6.1. 目的 17 4.6.2. flex词法exp.l 17 4.6.3. bison语法exp.y 17 4.6.4. Makefile 19 4.6.5. 代码集成 19 4.7. 例3:函数 20 4.7.1. 目的 20 4.7.2. func.h 20 4.7.3. func.c 21 4.7.4. IDL代码func.idl 22 4.7.5. flex词法func.l 22 4.7.6. bison语法func.y 24 4.7.7. Makefile 27 5. 进阶 27 5.1. 客户端函数实现 27 5.2. 服务端函数实现 28 5.2.1. Stub部分实现 28 5.2.2. 用户部分实现 29 6. 参考资料 29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值