基于Netty和Spring实现RPC

基于Netty和Spring实现RPC

1. 需求分析

基本需求分析:

  1. 服务端通过注册服务开放服务代理;
  2. 客户端通过申请服务,经过rpc系统代理远程调用服务端开放的服务;

程序需求分析:

  1. 代码侵入程度低,全部通过注解和配置实现;
  2. 客户端实现代理之后,使用与本地服务尽量接近;

2. 相关技术

  1. Netty:基于netty实现客户端与服务端之间的通讯,相比http请求更高效;
  2. spring boot

3. 通讯协议设计

  1. 服务端和客户端之间通讯基于netty,通过发送和接收协议包来完成通讯;
  2. 协议包的传输需要先进行序列化,本程序选择的是json的序列化方式,使用fastjson;

协议包:

public class dubboProtocol<T> {
   
    Long REQUEST_ID;
    int type;
    T content;
}

REQUEST_ID是必要的,用于对应响应和请求;

type用于标识是请求还是响应;

content是一个泛型变量,用于接收响应或请求的内容;

请求:

public class Request {
   
    private String serviceName;
    private String methodName;
    private Object[] parameters;
    private Class<?>[] parametersType;
}

serviceName:请求服务的className;

methodName:请求的方法;

parameters:请求的参数,通过一个Object数组接收;

parametersType:用于接收参数的类型,因为经过序列化和反序列化后参数类型会变成JsonObject类型,因此需要知道原来的类型进行还原;

编码:

public class JSONEncoder extends MessageToByteEncoder<dubboProtocol> {
   
    @Override
    protected void encode(ChannelHandlerContext ctx, dubboProtocol msg, ByteBuf out) throws Exception {
   
        String json = JSONObject.toJSONString(msg);
        byte[] bytes = json.getBytes(CharsetUtil.UTF_8);
        out.writeInt(bytes.length);
        out.writeBytes(bytes);
    }
}

解码:

public class JSONDecoder extends ByteToMessageDecoder {
   
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
   
        int len = in.readInt();
        byte[] bytes = new byte[len];
        in.readBytes(bytes);
        String json = new String(bytes, CharsetUtil.UTF_8);
        dubboProtocol msg = JSONObject.parseObject(json, dubboProtocol.class);
        out.add(msg);
    }
}

4. 异步模型设计

Netty的执行是异步的,需要通过一个Future对象来获取响应结果;

Future:

public class RpcFuture<T> {
   

    private Promise<T> promise;

    public RpcFuture(Promise<T> promise) {
   
        this.promise = promise;
    }

    public Promise<T> getPromise() {
   
        return promise;
    }

    public void setPromise(Promise<T> promise) {
   
        this.promise = promise;
    }
}

由于有可能有多个请求在等待返回结果,因此需要有一个FutureFactory对多个Future进行管理;

FutureFactory:

public class FutureFactory {
   

    public static final AtomicLong REQUEST_ID=new AtomicLong();

    public static final Map<Long, RpcFuture> REQUEST_MAP=new ConcurrentHashMap<>();

    public static Long put(RpcFuture future){
   
        long Rid = REQUEST_ID.incrementAndGet();
        REQUEST_MAP.put(Rid,future);
        return Rid;
    }

    public static RpcFuture get(Long id){
   
        return REQUEST_MAP.get(id);
    }

    public static void remove(Long id){
   
        REQUEST_MAP.remove(id);
    }
}

5. 功能注解

客户端注解:

申请代理注解:

/**
 * 被该注解注释的类会被rpc代理
 */
@Target({
   ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(ClientCondition.class)
public @interface Deposit {
   
    String value() default "";
}

客户端容器注解:

@Target({
   ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
@Conditional(ClientCondition.class)
public @interface ClientComponent {
   
}

包扫描注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(AutoMapperScanImportBeanDefinitionRegistrar.class)
public @interface ProxyScan {
    @AliasFor("value")
    String[] basePackage() default {};

    @AliasFor("basePackage")
    String[] value() default {};
}

用于扫描并注入需要被代理的服务的bean,由于客户端需要被代理的服务没有本地的实现类,且interface不能被实例化,需要通过代理的方式注入bean,因此需要通过扫描包并自定义注入方式来进行初始化;

服务器注解:

注册服务注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
@Conditional(ServerCondition.class)
public @interface Register {
   
}

服务器容器:

@Target({
   ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
@Conditional(ServerCondition.class)
public @interface ServerComponent {
   
}

服务器容器和客户端容器都是通过@Conditional来扩展@Component,主要是为了实现通过配置来避免不必要的依赖注入;这两个注解相关的Condition如下:

public class ClientCondition implements Condition {
   

    private static String property;

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
   
        if(property==null){
   
            Environment environment = context.getEnvironment();
            property = environment.getProperty("rpc.job");
        }
        return property
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值