微服务组件之RPC(下)

124 阅读6分钟

RPC高级功能剖析

本质上是服务治理,以技术方案解决管理成本

服务熔断

当某服务出现不可用或响应超时情况时,为了防止整个系统出现雪崩,cosumer暂时停止对该服务的调用。服务不在继续向下调用。

image.png

服务降级

对业务进行降级,跳过异常调用,返回关键数据,确保服务可用。一种有损补偿行为。服务继续向下调用。

image.png

熔断和降级是两个不同的概念,由于在微服务中的场景下经常一起出现,导致部分人会以为熔断和降级是同一个事情。 熔断会触发服务降级,超时、异常等。

动态权重

对新的节点分配低权重,逐步提高权重

限流

通过限制调用方流量,达到对provider的保护的目的。

限流算法

  1. 计数器算法 按固定时间设置上限值 例如 1000次/10s,每10秒为单位间隔固定限流。
  2. 滑动窗口算法
    按最近时间设置上限值,同样10s 为间隔 计数是固定的10秒(1-10,11-20),滑动是最近的10秒(2-12,5-15)
  3. 漏斗算法 经过漏斗器将流量均匀达到服务提供方。将请求缓存在队列中,出队时取出固定请求交给线程池进行处理。每次出队后睡眠一个短暂时间。
  4. 令牌桶算法 生产线程每秒生成n个token放入(固定长度的)桶中,consumer每次调用都从桶中取出一个token以调用provider,当桶满时不在生成以限制调用速度。

主流RPC对比分析

如何对RPC框架进行选型,需要考虑什么注意点?

RPC选型考虑

  • 计数团队语言
  • RPC框架特性
  • 框架成熟度
  • 技术支持
  • 社区活跃度

主流产品及其设计目标

  • gRPC
    • 由google开发,语言、平台中立,高性能开源框架
    • 传输协议 HTTP/2
    • 接口定义和序列化(ProtoBuf)
    • 客户端寻址。本地配置,需要开发注册中心
    • 支持 java、C++、Python、Go、Ruby、Nodejs、C#

image.png

  • Dubbo
    • 阿里巴巴开源高性能、透明化RPC调用方案,与spring无缝衔接。
    • 传输协议&序列化,多种通讯协议面向不同的应用场景
    • 支持java语言
    • 扩展性 Dubbo SPI 机制
    • 配套设施 自带注册中心、监控

image.png

  • brpc
    • 百度工业级RPC框架,上百万个实例(不包含client),目前开源C++版。
    • 传输&序列化,多种通讯协议面向不同的应用场景
    • 支持语言 java、C++
    • 性能优于其他RPC产品
  • Thrift
    • 2007年由facebook开发的序列化方法和rpc框架,包含独特序列化格式和IDL,支持多语言.
    • 传输&序列化,多协议支持
    • 支持多语言,跨语言

image.png

面试题

Http1.0 Http1.1 Http2.0的区别? http 1.0 短连接,一次请求一次响应。css、js等静态资源都需要多次连接获取。 http 1.1 为了解决多次连接获取静态资源问题,增加了keep-alive支持,对连接进行复用继续请求。 http 2.0 将http 1.1的通信方式 半双工通信优化为了全双工通信。

Dubbo原理剖析

dubbo 是市面上广泛使用的开源产品,具备面向接口代理的高性能RPC调用、服务注册与发现、运行期流量管理、智能负载均衡和高度可扩展性。

Dubbo整体架构

Dubbo总体分为业务层、RPC层、Remote层。其中Service 和 Config 可以划分为 API层,其他的可以划分为SPI层

image.png dubbo采用的分层设计,每一层的核心实现为上一层提供服务,分层设计实际上是一种插件化思维,这样每一层的实现都可以被替换。

Dubbo扩展点机制

Dubbo的扩展点加载机制使框架的接口和具体实现完全解耦,是框架可扩展行的基础。

  • Dubbo扩展点
    • Dubbo的SPI层所有接口都可以基于Dubbo框架做定制化的二次开发,对功能进行拓展。如负载均衡、序列化协议、注册中心等。
  • Dubbo SPI扩展点加载机制
    • Java SPI:Service Provider Interface 一个接口多种实现,通过配置确定使用哪个实现
      • 制定接口规范,供外部扩展人员实现
    • Dubbo SPI:Java SPI增强版,增加了对扩展点 IOC 和 AOP的支持。
      • SPI注解可扩展
      • 通过键值对配置
      • 根据key指定扩展类

负载均衡实现案例

@SPI 注解声明可扩展, RandomLoadBalance.NAME 作为key指定配置文件中的属性值扩,获取配置扩展类

@SPI(RandomLoadBalance.NAME)
public interface LoadBalance {
    @Adaptive(loadbalance)
    <T> Invoker<T> select(List<Invoke<T>> invokers, URL url, Invocation invocation) throw RpcException;
}

public class RandomLoadBalance extends AbstractLoadBalance {
    public static final String NAME = "random";
}

SPI配置文件 META-INF/dubbo/org.apache.dubbo.rpc.cluster.LoadBalance

### 随机
random=com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance
### 轮询
roundrobin=com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance
### 最少活跃度
leastactive=com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance
### 一致性hash
consistenthash=com.alibaba.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance

Dubbo扩展点特性

  1. 扩展点自动包装
    Wrapper(包装)类,不是扩展点真正的实现,但是它持有真正的扩展点实现类。在扩展点上添加逻辑

        //实现Protocol
       public class ProtocolFilterWrapper implements Protocol {
           //声明实现的目标类作为成员变量
           private final Protocol protocol;
           //通过构造方法注入目标类
           public ProtocolFilterWrapper (Protocol protocol) {...}
           
           @Override
           public <T> Expoter<T> export(Invoker<T> invoker) throws RpcException {
               //AOP切面增强
               if(REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
                   return protocol.export(invoker);
               }
               return protocol.export(buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonContants.PROVIDER));
           }
           
           @Override
           public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
               if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                   return protocol.refer(type, url);
               }
               return buildInvokerChain(protocol.refer(type,url), REFERENCE_FILTER_KEY,CommonConstants.CONSUMER);
           }
       }
    
    @SPI("dubbo")
        public interface Protocol {
            int getDefaultPort();
            
            @Adaptive
            <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
            
            @Adaptive
            <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
            
            void destory();
        }
    

    META-INF

       fliter=org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper
       listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
       mock=org.apache.dubbo.rpc.support.MockProtocol
       dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
    

    多个包装类并不只生效一个,包装类内部使用最内层实现,不加载包装类AOP也不会失效。

  2. 扩展点自动装配
    扩展点实现类的成员如果为其他扩展点类型,自动注入依赖.

这里有两个扩展点接口 分别为 CarMaker和WheelMaker

public interface CarMaker {
    Car makeCar();
}
public interface WheelMaker {
    Wheel makeWheel();
}

扩展点实现类

public class RaceCarMaker implements CarMaker {
    //WheelMaker扩展点作为 CarMaker扩展点的成员变量,自动注入依赖
    WheelMaker wheelMaker;
    
    public setWheelMaker(WheelMaker wheelMaker) {
        this.wheelMaker = wheelMaker;
    }
    
    public Car makeCar() {
        Wheel wheel = wheelMaker.makeWheel();
        return new RaceCar(wheel,...);
    }
}
  1. 扩展点自适应 有些扩展并不想初始化时被加载,希望作用在扩展方法被调用时根据参数进行加载。
  • 静态指定扩展点实现 @SPI
    • SPI注解
    • 配置
  • 动态指定扩展点实现 @Adaptive
    • 运行时决定
@SPI(RandomLoadBalance.NAME) // 静态默认指定random随机算法
public interface LoadBalance {
    @Adaptive(loadbalance) //运行时可以根据@Adaptive的值或者url的参数修改为ruby轮询算法
    <T> Invoker<T> select(List<Invoke<T>> invokers, URL url, Invocation invocation) throw RpcException;
}
  1. 扩展点自动激活 对于集合类扩展点,例如Filter,每个Filter实现不同的功能,需要多个同时激活,可以用自动激活来简化配置。
@Activate(group = Constants.PROVIDER, value = Constants.ACCESS_LOG_KEY)
public class AccessLogFilter implements Filter {
@Activate(group = {Constants.CONSUMER,Constants.PROVIDER}, value = Constants.CACHE_KEY)
public class CacheFilter implements Filter {
@Activate(group = Constants.PROVIDER, order = -30000)
public class ClassLoaderFilter implements Filter {

通过group分组,将相同实现放入链表结构中,通过order保证排序,实现自动激活