RPC概览(以Dubbo为例)

Dubbo得以成立的三个组件:
基于行和列的key-value数据库,支持单表内上百万列、数十亿行稀疏数据的分布式存储,自动分片,方便扩容,但不支持MySQL中的非聚集索引、事务、触发器、高级查询语句等特性。

准备


Provider初始化

第一步,启动Netty服务端,包括但不限于实例化各种类,实例化编码和解码器,创建IO线程池等。
第二步,启动Dubbo,实例化过滤器链等Dubbo核心类。
第三步,为将要提供服务的接口创建代理类:

package com.dubbo.test;

public class Hello {
    
    public void sayHello(String name)  {
         System.out.println("hello " + name);
    }
}

通过反射生成的代理类Wrapper:

public class Wrapper {

    public Object invokeMethod(Object obj, String methodName, Object[] args) throws java.lang.reflect.InvocationTargetException {
        com.dubbo.test.Hello w = (com.dubbo.test.Hello) obj;
        try {
            if ("sayHello".equals(methodName)) {
                w.sayHello((java.lang.String) args[0]);
                return null;
            }
        } catch (Throwable e) {
            throw new java.lang.reflect.InvocationTargetException(e);
        }
        throw new NoSuchMethodException("Not found method \"" + methodName + "\" in class com.test.Hello");
    }
}

当provider接收到的consumer的调用请求消息后,会从消息里面解析出类名、方法名、参数,然后找到类对应的Wrapper,调用invokeMethod方法。
第四步,生成包括这一个接口所有信息的URL,并通过URL在注册中心注册这一个provider:
dubbo://127.1.1.1:20880/com.dubbo.test.Hello?anyhost=true&application=sas-provider&dubbo=2.0.2&generic=false&interface=com.dubbo.test.Hello&methods=sayHello

Comsumer初始化

第一步,从注册中心拉取provider的信息,订阅Provider,同样通过URL的方式注册一个consumer。
第二步,启动Netty客户端,包括但不限于实例化各种类,实例化编码和解码器,创建IO线程池等。
第三步,启动Dubbo,包括实例化集群容错、负载均衡、过滤器链等Dubbo核心类。
第四步,为想要调用的接口创建代理类:

package com.dubbo.test;

public interface Hello {
    
    void sayHello(String name);
}

通过反射生成代理类Proxy,在Spring的视角中Proxy就是接口的实现类:

package com.dubbo.test;

public class Proxy implements com.dubbo.test.Hello {

    public static java.lang.reflect.Method method;

    // Dubbo的核心类,通过invoke进入Dubbo框架
    private Invoker invoker;

    public void sayHello(String name) {
        Object[] args = new Object[1];
        args[0] = name;
        invoker.invoke(this, method, args);
    }
}

 

请求


在这里插入图片描述

第一步,通过生成的代理类,发起对sayHello方法的调用。在使用者视角里,调用别人提供的接口跟调用自己实现的接口一样。
第二步,进入FailOver的控制管理策略中,目的是指定本次调用是对单个provider发起调用还是多个发起调用,以及在本次调用失败的时候,应该怎么做。支持以下策略:

策略说明
AvailableClusterInvoker直接发起调用,不处理结果
FailoverClusterInvoker(default)失败时重试其他provider,重试n次,默认重试2次。
FailbackClusterInvoker固定时间间隔后重试
FailfastClusterInvoker直接抛出异常
FailsafeClusterInvoker打印日志并忽略异常
ForkingClusterInvoker同时对多个provider发起调用
BroadcastClusterInvoker同时对所有provider发起调用

第三步,负载均衡策略,从所有的provider中选出一个。支持五种策略:

策略说明
RandomLoadBalance(default)加权随机,按照设置的权重随机选一个,权重越大选中的概率越大。默认所有provider权重相同。
LeastActiveLoadBalance选择当前活跃连接数最小的provider,依赖过滤器链。
ConsistentHashLoadBalance一致性哈希。相同参数的请求总是发到同一个provider。
RoundRobinLoadBalance加权轮询。在轮流分配的基础上加上权重,最终每个provider的请求数比值接近权重比。权重相等时,等于轮流分配。
ShortestResponseLoadBalance选择预计等待时间最短的provider,依赖过滤器链。

第四步,执行过滤器链,按顺序依次执行每一个过滤器,过滤器会传入这一次调用的信息,包括接口、方法、参数等等。过滤器支持用户自定义,框架内置过滤器包括不限于:

类名作用类别
ActiveLimitFilter1.限制consumer的并发调用数,可以设置并发数、被限制后的超时时间。
2.记录调用数据:并发数,调用总数,成功次数、失败次数、调用耗时等。consumer
ExecuteLimitFilter限制provider的并发调用数,达到限制直接抛出异常。provider

第四步,对参数编码和发送Request。

 

通信


在这里插入图片描述
应用层默认使用Dubbo协议通信,框架内置支持http,redis,grpc等。
如果是Dubbo协议,对Request/Response组装一个Dubbo协议包发出去:

在这里插入图片描述
重要字段:
MagicHigh+MagicLow表示每一条消息的起始位置,通过Magic分割解决粘包拆包的问题。
Req/Res标识是请求还是响应。
SerializationID标识序列化方式,例如fastjson。
Status表示状态,包括OK、TimeOut、Error等。
RequestId唯一标识。

 

执行


在这里插入图片描述
第一步,把收到的Request消息从Netty的IO线程转移到执行任务的外部线程池中。
第二步,对收到的消息解码,提取请求的接口、调用的方法名、传入的参数值等,找到准备阶段已经生成好的Wrapper(里面保存了具体的实现类)。
第三步,执行包装在Wrapper外层的过滤器链。
第四步,执行provider的sayHello方法。
第五步,把方法的调用结果组装成Response消息,发送给consumer。

 

响应


在这里插入图片描述
同步调用:发送Request消息后,Dubbo内部线程马上执行Future.get()阻塞住,直到有结果后返回给用户。
异步调用:方法的返回值必须是CompletableFuture,发送完Request后直接把Future返回给用户。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值