Dubbo笔记

摘自Dubbo入门介绍及学习笔记总结

分布式概述

分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统。

老式系统(单一应用架构)就是把一个系统,统一放到一个服务器当中然后每一个服务器上放一个系统,如果说要更新代码的话,每一个服务器上的系统都要重新去部署十分的麻烦。

而分布式系统就是将一个完整的系统拆分成多个不同的服务,然后在将每一个服务单独的放到一个服务器当中。

系统架构演进

PRC

RPC:Remote Procedure Call ——远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。

RPC基本原理

RPC基本原理
  1. Client像调用本地服务似的调用远程服务;
  2. Client stub接收到调用后,将方法、参数序列化
  3. 客户端通过sockets将消息发送到服务端
  4. Server stub 收到消息后进行解码(将消息对象反序列化)
  5. Server stub 根据解码结果调用本地的服务
  6. 本地服务执行(对于服务端来说是本地执行)并将结果返回给Server stub
  7. Server stub将返回结果打包成消息(将结果消息对象序列化)
  8. 服务端通过sockets将消息发送到客户端
  9. Client stub接收到结果消息,并进行解码(将结果消息发序列化)
  10. 客户端得到最终结果。

RPC 调用分以下两种:
同步调用:客户方等待调用执行完成并返回结果。
异步调用:客户方调用后不用等待执行结果返回,但依然可以通过回调通知等方式获取返回结果。若客户方不关心调用返回结果,则变成单向异步调用,单向调用不用返回结果。

SOA(Service-Oriented Architecture)
流动计算架构:在分布式应用架构的基础上增加了一个调度、治理中心基于访问压力实时管理集群容量、提高集群的利用率,用于提高机器利用率的资源调度和治理中心(SOA) 是关键 (不浪费计算机资源)

Dubbo

官网https://dubbo.apache.org/zh/

Apache Dubbo 提供了六大核心能力:面向接口代理的高性能RPC调用,智能容错和负载均衡,服务自动注册和发现,高度可扩展能力,运行期流量调度,可视化的服务治理与运维。

六大核心能力

Dubbo设计架构

Dubbo设计架构

服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。
服务消费者(Consumer): 调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。推荐使用zookeeper。
监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

Docker搭建Zookeeper + Dubbo-admin

拉取zookeeper:3.4.11,并运行

docker pull zookeeper:3.4.11

docker run --name zookeeper -p 2181:2181 -v /home/zookeeper/data:/data  -d zookeeper:3.4.11

拉取chenchuxin/dubbo-admin,并运行

docker pull chenchuxin/dubbo-admin

docker run -d --name dubbo-admin -p 8081:8080 -e dubbo.registry.address=zookeeper://172.17.0.2:2181 -e dubbo.admin.root.password=root -e dubbo.admin.guest.password=123 chenchuxin/dubbo-admin

#(dubbo.registry.address=zookeeper://172.17.0.2:2181)这个地址为你zookeeper容器的地址,如何获取zookeeper容器地址:输入命令:docker inspect 容器名/容器id,“IPAddress”:就是zookeeper的ip地址

完成并访问http://120.79.28.120:8081/

服务提供者配置

官方教程https://dubbo.apache.org/zh/docs/v2.7/user/quick-start/

  • 定义服务接口
package com.zhang.service;

public interface DemoService {
    String sayHello(String name);
}

  • 在服务提供方实现接口
package com.zhang.serviceImpl;
import com.zhang.service.DemoService;

public class DemoServiceImpl implements DemoService {
    @Override
    public String sayHello(String name) {
        return "Hello" + name;
    }
}
  • 用 Spring 配置声明暴露服务


    
    
    

    
    
    
    
    
    
    
    
    

  • 加载 Spring 配置
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;

public class Provider {
    public static void main(String[] args) throws IOException {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("provider.xml");
        context.start();
        System.in.read();
    }
}

服务消费者配置

  • 通过 Spring 配置引用远程服务



    
    

    
    

    
    
    

  • 加载Spring配置,并调用远程服务
import com.zhang.service.DemoService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Consumer {
    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("consumer.xml");
        context.start();
        DemoService demoService = (DemoService)context.getBean("demoService"); // 获取远程服务代理
        String hello = demoService.sayHello("world"); // 执行远程方法
        System.out.println( hello ); // 显示调用结果
        System.in.read();
    }
}

docker搭建Dubbo-monitor

此处搭建失败,留坑

dubbo配置文件的映射规则、覆盖策略

官方文档 https://dubbo.apache.org/zh/docs/v2.7/user/configuration/properties/

如下,是一个典型的 dubbo.properties 配置样例。

dubbo.application.name=foo
dubbo.application.owner=bar
dubbo.registry.address=10.20.153.10:9090

覆盖策略

优先级从高到低:

  • JVM -D 参数:当你部署或者启动应用时,它可以轻易地重写配置,比如,改变 dubbo 协议端口;
  • XML:XML 中的当前配置会重写 dubbo.properties 中的;
  • Properties:默认配置,仅仅作用于以上两者没有配置时。

如果在 classpath 下有超过一个 dubbo.properties 文件,比如,两个 jar 包都各自包含了 dubbo.properties,dubbo 将随机选择一个加载,并且打印错误日志。

启动时检查

Dubbo 会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check="true"。

可以通过 check="false" 关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动。

另外,如果你的 Spring 容器是懒加载的,或者通过 API 编程延迟引用服务,请关闭 check,否则服务临时不可用时,会抛出异常,拿到 null 引用,如果 check="false",总是会返回引用,当服务恢复时,能自动连上。

application.properties添加如下配置

#dubbo.consumer表示所有的 消费者服务 启动时不检查服务依赖
dubbo.consumer.check=false

不同粒度配置的覆盖关系

以 timeout 为例,下图显示了配置的查找顺序,其它 retries, loadbalance, actives 等类似:

  • 方法级优先,接口级次之,全局配置再次之。
  • 如果级别一样,则消费方优先,提供方次之。

其中,服务提供方配置,通过 URL 经由注册中心传递给消费方。

(建议由服务提供方设置超时,因为一个方法需要执行多长时间,服务提供方更清楚,如果一个消费方同时引用多个服务,就不需要关心每个服务的超时设置)。

理论上 ReferenceConfig 中除了interface这一项,其他所有配置项都可以缺省不配置,框架会自动使用ConsumerConfig,ServiceConfig, ProviderConfig等提供的缺省配置。

重试次数配置

配置dubbo服务重试次数.
Dubbo 服务在尝试调用一次之后,如出现非业务异常(服务突然不可用、超时等),Dubbo 默认会进行额外的最多2次重试.

重试次数支持两种自定义配置:

  1. 通过注解/xml进行固定配置;
  2. 通过上下文进行运行时动态配置。
  • 通过注解/xml进行固定配置

    
    
  • 通过RpcContext进行运行时动态配置,优先级高于注解/xml 进行的固定配置(两者都配置的情况下,以RpcContext配置为准).

// dubbo服务调用前,通过RpcContext动态设置本次调用的重试次数
RpcContext rpcContext = RpcContext.getContext();
rpcContext.setAttachment("retries", 5);

重试的配置应该保证 幂等性,即操作多次的结果都一样。
幂等:查询、删除、修改
非幂等:新增(retries="0")

多版本

在 Dubbo 中为同一个服务配置多个版本,实现灰度发布。

当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。

   
   
    
    

    
    
    
#消费者使用服务1.0.0版本


# 不指定版本,消费者随机使用服务版本

本地存根

在 Dubbo 中利用本地存根在 客户端 执行部分逻辑

远程服务后,客户端通常只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,比如:做 ThreadLocal 缓存,提前验证参数,调用失败后伪造容错数据等等。
Consumer调用远程服务之前,先调用本地的Stub ,再由Stub决定要不要去调Dubbo的 Proxy,进而调用 Provider 提供的服务。

在消费者的 consumer.xml 中配置如下

    

并在consumer端,提供Stub的实现

public class DemoServiceStub implements DemoService {
    private final DemoService demoService;
    //传入的是DemoService远程的代理对象
    public DemoServiceStub(DemoService demoService) {
        super();
        this.demoService = demoService;
    }

    @Override
    public String sayHello(String name) {
        System.out.println("DemoServiceStub ..." + name);
        if(!name.isEmpty()){
           return demoService.sayHello(name);
        } else {
            System.out.println("name is empty .....");
        }
        return null;
    }
}
  1. Stub 必须有可传入 Proxy 的构造函数。

  2. 一般在DemoService interface 旁边放一个 Stub 实现,它实现 DemoService 接口,并有一个传入远程 DemoService 实例的构造函数

SpringBoot整合dubbo的三种方式

  1. 导入dubbo-starter 依赖,在application.properties中配置属性,用@Service暴露服务,@Reference 引用服务
  2. 保留dubbo.xml配置文件,导入dubbo-starter,使用@ImportResource导入配置文件。
  3. 使用注解API的方式(https://dubbo.apache.org/zh/docs/v2.7/user/configuration/api/)

第一种:

  1. 新建两个spring Initializer 的 Module


  2. 引入依赖


            org.apache.dubbo
            dubbo-spring-boot-starter
            2.7.1
        

引入此依赖会自动引入如下依赖:


  1. application.properties配置
server.port=8081
dubbo.application.name=springboot-provider
dubbo.registry.protocol=zookeeper     #注册中心
dubbo.registry.address=120.79.28.120:2181    #地址
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
  1. 注解开发
    代码框架:
服务提供者
/**
 * @org.apache.dubbo.config.annotation.Service
 * 这个注解即暴露UserServiceImpl服务!!!!!
 */
@org.apache.dubbo.config.annotation.Service
@Component
public class UserServiceImpl implements UserService {
    @Override
    public List getUserAddressList(String userId) {
        UserAddress address1 = new UserAddress();
        UserAddress address2 = new UserAddress();
        return Arrays.asList(address1, address2);
    }
}


服务消费者
@Service
public class OrderServiceImpl implements OrderService {
    /*@Reference注解引用服务!!!!*/
    @Reference   
    UserService userService;
    @Override
    public List initOrder(String userId) {
        List addressList = userService.getUserAddressList(userId);
        return addressList;
    }
}
  1. 结果

高可用

  1. zookeeper宕机与dubbo直连

zookeeper 注册中心宕机,还可以使用dubbo暴露的服务

健壮性

  • 监控中心宕机不影响使用,只是丢失部分采样数据
  • 数据库宕机后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
  • 注册中心对等集群,任意一台注册中心宕机后,将自动切换到另外一台
  • 假设注册中心全部宕机,服务提供者和服务消费者仍能通过本地缓存通信
  • 即使没有注册中心,可以通过dubbo直连提供服务,服务消费者直接指定提供者的URL进行直连
  1. 集群下dubbo负载均衡配置

在集群负载均衡时,dubbo提供了多种均衡策略,缺省为random随机调用

负载均衡策略
此处查看 官方文档

  • random LoadBalance 随机负载均衡,是加权随机算法的具体实现
    假设我们有一组服务器 servers = [A, B, C],他们对应的权重为 weights = [5, 3, 2],权重总和为10。现在把这些权重值平铺在一维坐标值上,[0, 5) 区间属于服务器 A,[5, 8) 区间属于服务器 B,[8, 10) 区间属于服务器 C。接下来通过随机数生成器生成一个范围在 [0, 10) 之间的随机数,然后计算这个随机数会落到哪个区间上,此时返回服务器 A 即可。

  • RoundRobinLoadBalance 加权轮询负载均衡
    所谓轮询是指将请求轮流分配给每台服务器。举个例子,我们有三台服务器 A、B、C。我们将第一个请求分配给服务器 A,第二个请求分配给服务器 B,第三个请求分配给服务器 C,第四个请求再次分配给服务器 A。这个过程就叫做轮询。
    但现实情况下,我们并不能保证每台服务器性能均相近。如果我们将等量的请求分配给性能较差的服务器,这显然是不合理的。因此,这个时候我们需要对轮询过程进行加权,以调控每台服务器的负载。经过加权后,每台服务器能够得到的请求数比例,接近或等于他们的权重比。比如服务器 A、B、C 权重比为 5:2:1。那么在8次请求中,服务器 A 将收到其中的5次请求,服务器 B 会收到其中的2次请求,服务器 C 则收到其中的1次请求。

  • LeastActiveLoadBalance 最小活跃数负载均衡
    活跃调用数越小,表明该服务提供者效率越高,单位时间内可处理更多的请求。此时应优先将请求分配给该服务提供者。在具体实现中,每个服务提供者对应一个活跃数 active。初始情况下,所有服务提供者活跃数均为0。每收到一个请求,活跃数加1,完成请求后则将活跃数减1。在服务运行一段时间后,性能好的服务提供者处理请求的速度更快,因此活跃数下降的也越快,此时这样的服务提供者能够优先获取到新的服务请求、这就是最小活跃数负载均衡算法的基本思想。

  • ConsistentHashLoadBalance 一致性 hash 负载均衡
    当有查询或写入请求时,则为缓存项的 key 生成一个 hash 值。然后查找第一个大于或等于该 hash 值的缓存节点,并到这个节点中查询或写入缓存项。如果当前节点挂了,则在下一次查询或写入缓存时,为缓存项查找另一个大于其 hash 值的缓存节点即可。
    大致效果如下图所示,每个缓存节点在圆环上占据一个位置。如果缓存项的 key 的 hash 值小于缓存节点 hash 值,则到该缓存节点中存储或读取缓存项。比如下面绿色点对应的缓存项将会被存储到 cache-2 节点中。由于 cache-3 挂了,原本应该存到该节点中的缓存项最终会存储到 cache-4 节点中。

一致性 hash 在 Dubbo 中的应用。我们把上图的缓存节点替换成 Dubbo 的服务提供者,于是得到了下图:


这里相同颜色的节点均属于同一个服务提供者,比如 Invoker1-1,Invoker1-2,……, Invoker1-160。这样做的目的是通过引入虚拟节点,让 Invoker 在圆环上分散开来,避免数据倾斜问题。所谓数据倾斜是指,由于节点不够分散,导致大量请求落到了同一个节点上,而其他节点只会接收到了少量请求的情况。

比如:下图中Invoker-1 和 Invoker-2 在圆环上分布不均,导致系统中75%的请求都会落到 Invoker-1 上,只有 25% 的请求会落到 Invoker-2 上。解决这个问题办法是引入虚拟节点,通过虚拟节点均衡各个节点的请求量。

服务降级 官方文档

可以通过服务降级功能临时屏蔽某个出错的非关键服务,并定义降级后的返回策略。

  • mock=force:return+null 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
  • 还可以改为 mock=fail:return+null 表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。

集群容错 官方文档

在集群调用失败时,Dubbo提供了多种容错方案,默认是 failover 重试

  • Failover Cluster
    失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。

  • Failfast Cluster
    快速失败,只发起一次调用,失败立即报错。通常用于 非幂等性 的写操作,比如新增记录。

  • Failsafe Cluster
    失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。

  • Failback Cluster
    失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。

  • Forking Cluster
    并行调用多个服务器,只要一个成功即返回。通常用于 实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。

  • Broadcast Cluster
    广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。

  • 整合 Hystrix
    Hystrix 通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。
    SpringBoot提供了Hystrix 的集成,直接添加如下依赖:


            org.springframework.cloud
            spring-cloud-starter-netflix-hystrix
            1.4.4.RELEASE

Dubbo原理

RPC原理

一次完整的RPC调用流程(同步调用,异步另说)如下:

  1. 服务消费方(client)调用以本地调用方式调用服务
  2. client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;
  3. client stub找到服务地址,并将消息发送到服务端;
  4. server stub收到消息后进行解码;
  5. server stub根据解码结果调用本地的服务;
  6. 本地服务执行并将结果返回给server stub;
  7. server stub将返回结果打包成消息并发送至消费方;
  8. client stub接收到消息,并进行解码;
  9. 服务消费方得到最终结果。

RPC框架的目标就是要2~8这些步骤都封装起来,这些细节对用户来说是透明的,不可见的。dubbo作为RPC框架也是如此,将步骤2-8封装了,对于用户来说只需要操作1、9步骤

Dubbo原理

整体框架设计 官方文档:框架设计

略...官方已做详细说明。

你可能感兴趣的:(Dubbo笔记)