基础知识

分布式基础理论

  • 分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统;
  • 分布式系统(distributed system)是建立在网络之上的软件系统。
  • 随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。

    应用框架发展演变

    image.png

    单一应用架构

    image.png
    当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。
    适用于小型网站,小型管理系统,将所有功能都部署到一个功能里,简单易用。
    缺点: 1、性能扩展比较难;
    2、协同开发问题;
    3、不利于升级维护;

    垂直应用架构

    image.png

  • 当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。

  • 通过切分业务来实现各个模块独立部署,降低了维护和部署的难度,团队各司其职更易管理,性能扩展也更方便,更有针对性。
  • 缺点: 公用模块无法重复利用,开发性的浪费;

    分布式服务架构

    image.png
    当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC:远程过程调用)是关键。

    流动计算架构

    image.png
    当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)[ Service Oriented Architecture]是关键

    RPC

    什么是RPC

    RPC【Remote Procedure Call】是指远程过程调用,是一种进程间通信方式,他是一种技术的思想,而不是规范。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。

    RPC基本原理

    image.png
    image.png
    RPC两个核心模块:通讯序列化

    dubbo核心概念

    简介

    Apache Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:

  • 面向接口的远程方法调用,

  • 智能容错和负载均衡,
  • 以及服务自动注册和发现。
  • 官网Dobbo

    dubbo设计架构

    image.png

  • 服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。

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

调用关系说明:

  • 服务容器负责启动,加载,运行服务提供者。
  • 服务提供者在启动时,向注册中心注册自己提供的服务。
  • 服务消费者在启动时,向注册中心订阅自己所需的服务。
  • 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  • 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  • 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

    dubbo环境搭建(spring)

    首先保证虚拟机已经安装过zookeeper,并且已经部署dubbo管理控制台

    公共接口模块(gmall-interface)

    这里保存了bean对象,service接口;
    image.png

    bean

    1. public class UserAddress implements Serializable {
    2. private Integer id;
    3. private String userAddress; //用户地址
    4. private String userId; //用户id
    5. private String consignee; //收货人
    6. private String phoneNum; //电话号码
    7. private String isDefault; //是否为默认地址 Y-是 N-否
    8. public UserAddress() {
    9. super();
    10. }
    11. public UserAddress(Integer id, String userAddress, String userId,
    12. String consignee, String phoneNum,String isDefault) {
    13. super();
    14. this.id = id;
    15. this.userAddress = userAddress;
    16. this.userId = userId;
    17. this.consignee = consignee;
    18. this.phoneNum = phoneNum;
    19. this.isDefault = isDefault;
    20. }
    21. public Integer getId() {
    22. return id;
    23. }
    24. public void setId(Integer id) {
    25. this.id = id;
    26. }
    27. public String getUserAddress() {
    28. return userAddress;
    29. }
    30. public void setUserAddress(String userAddress) {
    31. this.userAddress = userAddress;
    32. }
    33. public String getUserId() {
    34. return userId;
    35. }
    36. public void setUserId(String userId) {
    37. this.userId = userId;
    38. }
    39. public String getConsignee() {
    40. return consignee;
    41. }
    42. public void setConsignee(String consignee) {
    43. this.consignee = consignee;
    44. }
    45. public String getPhoneNum() {
    46. return phoneNum;
    47. }
    48. public void setPhoneNum(String phoneNum) {
    49. this.phoneNum = phoneNum;
    50. }
    51. public String getIsDefault() {
    52. return isDefault;
    53. }
    54. public void setIsDefault(String isDefault) {
    55. this.isDefault = isDefault;
    56. }
    57. }

    service

    public interface OrderService {
      //初始化订单
      public void initOrder(String userId);
    }
    
    public interface UserService {
      /**
       * 按照用户id返回所有的收货地址
       */
      public List<UserAddress> getUserAddressList(String userId);
    }
    

    服务提供者(user-service)

    将服务提供者(user-service)注册到注册中心
    image.png

    pom.xml

    导入dubbo依赖(版本2.6.2)和操作zookeeper的客户端curator的依赖(版本2.12.0)

    <!--引入上面自己创建的公共接口-->
    <dependency>
      <groupId>com.atguigu.gmall</groupId>
      <artifactId>gmall-interface</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
    <!--引入dubbo依赖-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>dubbo</artifactId>
      <version>2.6.2</version>
    </dependency>
    <!--注册中心采用的是zookeeper,引入操作zookeeper的客户端-->
    <dependency>
      <groupId>org.apache.curator</groupId>
      <artifactId>curator-framework</artifactId>
      <version>2.12.0</version>
    </dependency>
    

    spring格式配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
         xsi:schemaLocation="http://www.springframework.org/schema/beans 
             http://www.springframework.org/schema/beans/spring-beans.xsd 
             http://code.alibabatech.com/schema/dubbo 
             http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
      <!--1、指定当前应用的名字(同样的服务名字相同,不要和别的服务同名)-->
      <dubbo:application name="user-service-provider"></dubbo:application>
      <!--2、指定注册中心的位置(2种方法)-->
      <!--<dubbo:registry address="
    zookeeper://192.168.80.130:2181?backup=192.168.80.131:2181,192.168.80.132:2181
          "></dubbo:registry>-->
      <dubbo:registry protocol="zookeeper" 
         address="192.168.80.130:2181,192.168.80.131:2181,192.168.80.132:2181"
      ></dubbo:registry>
      <!--3、指定通信规则(通信协议,通信端口)-->
      <dubbo:protocol name="dubbo" port="20080"></dubbo:protocol>
      <!--4、暴露服务(ref:指向服务的真正实现)-->
      <dubbo:service interface="com.atguigu.gmall.service.UserService" 
                     ref="userService"></dubbo:service>
      <!--服务的实现-->
      <bean class="com.atguigu.gmall.service.impl.UserServiceImpl" 
            id="userService"></bean>
    </beans>
    

    接口实现类UserServiceImpl

    模拟service接口实现类,配置服务提供者;

    @Service
    public class UserServiceImpl implements UserService {
      @Override
      public List<UserAddress> getUserAddressList(String userId) {
          System.out.println("UserServiceImpl.....old...");
          // TODO Auto-generated method stub
          UserAddress address1 = new UserAddress(
              1, "北京市昌平区宏福科技园综合楼3层", "1", 
              "李老师", "010-56253825", "Y");
          UserAddress address2 = new UserAddress(
              2, "深圳市宝安区西部硅谷大厦B座3层(深圳分校)",
              "1", "王老师", "010-56253825", "N");
          return Arrays.asList(address1,address2);
      }
    }
    

    主程序

    public class MainApplication {
      public static void main(String[] args) throws IOException {
          ClassPathXmlApplicationContext ioc=
              new ClassPathXmlApplicationContext("provider.xml");
          ioc.start();
          System.in.read();//阻塞一下,使得有时间观测
      }
    }
    

    服务消费者(order-service)

    让服务消费者(order-service)去注册中心订阅服务提供者的服务地址
    image.png

    pom.xml

    同服务提供者

    <!--引入上面自己创建的公共接口-->
    <dependency>
      <groupId>com.atguigu.gmall</groupId>
      <artifactId>gmall-interface</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
    <!--引入dubbo依赖-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>dubbo</artifactId>
      <version>2.6.2</version>
    </dependency>
    <!--注册中心采用的是zookeeper,引入操作zookeeper的客户端-->
    <dependency>
      <groupId>org.apache.curator</groupId>
      <artifactId>curator-framework</artifactId>
      <version>2.12.0</version>
    </dependency>
    

    spring格式配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
         xmlns:context="http://www.springframework.org/schema/context"
         xsi:schemaLocation="http://www.springframework.org/schema/beans 
                             http://www.springframework.org/schema/beans/spring-beans.xsd 
                             http://code.alibabatech.com/schema/dubbo 
                             http://code.alibabatech.com/schema/dubbo/dubbo.xsd 
                             http://www.springframework.org/schema/context 
                    http://www.springframework.org/schema/context/spring-context.xsd">
      <!--包扫描-->
      <context:component-scan base-package="com.atguigu.gmall.service.impl">
      </context:component-scan>
      <dubbo:application name="order-service-consumer"></dubbo:application>
      <dubbo:registry protocol="zookeeper" address="
          192.168.80.130:2181,192.168.80.131:2181,192.168.80.132:2181"></dubbo:registry>
      <!--声明需要调用的远程服务的接口,生成远程服务代理-->
      <dubbo:reference interface="com.atguigu.gmall.service.UserService" id="userService">
      </dubbo:reference>
    </beans>
    

    接口实现类OrderServiceImpl

  • 服务消费者调用服务提供者提供的服务来进行运行;

  • 即:OrderService接口调用userService接口的方法;
    @Service
    public class OrderServiceImpl implements OrderService {
      @Autowired
      UserService userService;
      @Override
      public void initOrder(String userId) {
          System.out.println("id是:"+userId);
          //1、查询用户收货地址
          List<UserAddress> list = 
              userService.getUserAddressList(userId);
          list.forEach(System.out::println);
      }
    }
    

    主程序

    public class MainApplication {
      @SuppressWarnings("resource")
      public static void main(String[] args) throws IOException {
          ClassPathXmlApplicationContext ioc=
              new ClassPathXmlApplicationContext("consumer.xml");
          OrderService orderService = ioc.getBean(OrderService.class);
          orderService.initOrder("1");
          System.out.println("调用结束");
          System.in.read();//阻塞一下
      }
    }
    

    结果

    将服务提供者和服务消费者的主程序均进行启动,之后在dubbo管理控制台就可发现如下:
    image.png

    dubbo环境搭建(springboot)

    公共接口模块(gmall-interface)

    这里保存了bean对象,service接口;

    service

    public interface OrderService {
      //初始化订单
      public List<UserAddress> initOrder(String userId);
    }
    

    其余同上公共接口模块

    服务提供者(boot-user-service-provider)

    image.png

    pom.xml

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    <!--导入dubbo-starter-->
    <dependency>
      <groupId>com.alibaba.boot</groupId>
      <artifactId>dubbo-spring-boot-starter</artifactId>
      <version>0.2.0</version>
    </dependency>
    <!--引入自己创建的公共接口-->
    <dependency>
      <groupId>com.atguigu.gmall</groupId>
      <artifactId>gmall-interface</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
    

    application.yaml

    dubbo:
    application:
      name: boot-user-service-provider
    registry:
      protocol: zookeeper
      address: 192.168.80.130:2181,192.168.80.131:2181,192.168.80.132:2181
    protocol:
      name: dubbo
      port: 20080
    

    service.impl

    这里面的@Service注解来自于import com.alibaba.dubbo.config.annotation.Service;
    @Service//暴露服务
    //这个@Service注解并非spring里面的,
    //而是import com.alibaba.dubbo.config.annotation.Service;
    @Component
    public class UserServiceImpl implements UserService {
      @Override
      public List<UserAddress> getUserAddressList(String userId) {
          System.out.println("UserServiceImpl.....old...");
          // TODO Auto-generated method stub
          UserAddress address1 = new UserAddress(1,
                  "北京市昌平区宏福科技园综合楼3层", "1", 
                   "李老师", "010-56253825", "Y");
          UserAddress address2 = new UserAddress(2, 
                  "深圳市宝安区西部硅谷大厦B座3层(深圳分校)", "1",
                    "王老师", "010-56253825", "N");
          return Arrays.asList(address1,address2);
      }
    }
    

    主程序类

    @EnableDubbo//开启基于注解的dubbo功能
    @SpringBootApplication
    public class BootUserServiceProviderApplication {
      public static void main(String[] args) {
          SpringApplication.run(
            BootUserServiceProviderApplication.class, args);
      }
    }
    

    服务消费者(boot-order-service-consumer)

    基于web的服务消费者模块;

    pom.xml

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    <!--导入dubbo-starter-->
    <dependency>
      <groupId>com.alibaba.boot</groupId>
      <artifactId>dubbo-spring-boot-starter</artifactId>
      <version>0.2.0</version>
    </dependency>
    <!--引入自己创建的公共接口-->
    <dependency>
      <groupId>com.atguigu.gmall</groupId>
      <artifactId>gmall-interface</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
    

    application.yaml

    dubbo:
    application:
      name: boot-order-service-consumer
    registry:
      protocol: zookeeper
      address: 192.168.80.130:2181,192.168.80.131:2181,192.168.80.132:2181
    

    service.impl

    @Service
    public class OrderServiceImpl implements OrderService {
    //    @Autowired
      @Reference//dubbo提供的注解,帮我们远程引入这个服务,它自己会从注册中心来发现
      UserService userService;
      @Override
      public List<UserAddress> initOrder(String userId) {
          System.out.println("id是:"+userId);
          //1、查询用户收货地址
          List<UserAddress> list = userService.getUserAddressList(userId);
          return list;
      }
    }
    

    控制层

    @Controller
    public class OrderController {
      @Autowired
      OrderService orderService;
      @ResponseBody
      @RequestMapping("/initOrder")
      public List<UserAddress> initOrder(@RequestParam("uid")String userId){
          return orderService.initOrder(userId);
      }
    }
    

    主程序类

    ```java @EnableDubbo//开启基于注解的dubbo功能 @SpringBootApplication public class BootOrderServiceConsumerApplication { public static void main(String[] args) {
      SpringApplication.run(BootOrderServiceConsumerApplication.class, args);
    
    } }
<a name="J3R6J"></a>
## spring与springboot搭建dubbo环境总结
<a name="qa4Md"></a>
### 服务提供者
![image.png](https://cdn.nlark.com/yuque/0/2022/png/23158036/1649169533217-c72127ac-6968-4272-8852-d91997d870b0.png#clientId=u4405144a-4c0b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=362&id=uc900d0ba&margin=%5Bobject%20Object%5D&name=image.png&originHeight=407&originWidth=996&originalType=binary&ratio=1&rotation=0&showTitle=false&size=50726&status=done&style=none&taskId=u1e02eea8-002e-41a7-8425-ee469862bcd&title=&width=885.3333333333334)
<a name="ooOb9"></a>
### 服务消费者
![image.png](https://cdn.nlark.com/yuque/0/2022/png/23158036/1649169981175-b078abe2-c09d-4da9-b1a7-5328de48a9d6.png#clientId=u4405144a-4c0b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=287&id=u1b38721b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=323&originWidth=1101&originalType=binary&ratio=1&rotation=0&showTitle=false&size=36750&status=done&style=none&taskId=ub9eb0cd3-5550-469d-9a21-faba487f984&title=&width=978.6666666666666)
<a name="uAYQy"></a>
# dubbo配置
[--->>官方介绍](https://dubbo.apache.org/zh/docs/advanced/preflight-check/)
<a name="gFIgL"></a>
## 配置原则
![image.png](https://cdn.nlark.com/yuque/0/2022/png/23158036/1649211089165-8a2406ab-cf5f-4ede-87d0-09c0a80f18cd.png#clientId=u763d24a2-02f4-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=309&id=u28d7d429&margin=%5Bobject%20Object%5D&name=image.png&originHeight=348&originWidth=371&originalType=binary&ratio=1&rotation=0&showTitle=false&size=66440&status=done&style=none&taskId=ubfe2c3df-6948-48ca-a164-387893da030&title=&width=329.77777777777777)

- **JVM**启动`-D`参数优先,这样可以使用户在部署和启动时进行参数重写,比如在启动时需改变协议的端口。
- **XML**次之,如果在XML中有配置,则`dubbo.properties`中的相应配置项无效。
- **Properties**最后,相当于缺省值,只有XML没有配置时,`dubbo.properties`的相应配置项才会生效,通常用于共享公共配置,比如应用名。

**注意:**application全局配置文件与xml文件优先级同一级别;
<a name="u0xmk"></a>
## 启动时检查
开发中有一个服务提供者,一个服务消费者,服务提供者负责将自己的服务注册到注册中心(zookeeper),服务消费者要从注册中心获取到服务提供者的调用地址,如果注册中心没有服务提供者的话,服务消费者启动的时候就会报错,因为服务消费者进行了启动时检查(`check="true"`),可以关闭启动时检查`check="false"`,这样服务消费者启动时就可以跳过检查,直接启动,只有在调用服务的时候服务消费者才到注册中心检查服务是否可用。
<a name="fJimF"></a>
### spring
```xml
<!--配置某个服务启动时不检查-->         
<dubbo:reference check="false" ...
<!--或者可以通过下面的方法,统一配置消费者的统一规则,所有的服务都不检查-->         
<dubbo:consumer check="false"/>

image.png

springboot

dubbo:
  consumer:
    check: false

超时

消费者调用提供者的方法,方法执行后返回结果给消费者,如果消费者长时间不能收到结果,就会立即终止方法,防止阻塞线程,服务消费者可以设置超时时间(默认1000ms)来实现这一功能;

spring

同样

<!--配置单个服务超时时间-->
<dubbo:reference timeout="5000" ...
<!--配置所有服务超时时间-->
<dubbo:consumer timeout="5000"/>

springboot

dubbo:
  consumer:
    timeout: 6000

配置覆盖关系

—->>官方文档
以 timeout 为例,其中,服务提供方配置,通过 URL 经由注册中心传递给消费方。
image.png
总结:

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

    重试次数

    一般与超时时间配合使用,一般用于当出现网络波动而出现超时情况时造成的超时
    <dubbo:reference timeout="5000" retries="66" interface=...
    <!--retries:重试次数必须是整数,但不包括第一次-->
    
    注意:
  1. 对于多个服务提供方提供同一服务的情况,如果服务消费方获取这个服务超时,并且重试次数不为1时,服务消费方会尝试从不同服务提供方获取这一服务(不会一棵树上吊死),直到重试次数用尽。
  2. 对于幂等操作(方法不管运行多少次产生的效果都不变,比如数据查询、删除、修改)设置重试次数,对于非幂等操作(方法每次运行都会产生新的效果,比如数据新增)不设置重试次数;

    多版本

    当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。可以按照以下的步骤进行版本迁移:

  3. 在低压力时间段,先升级一半提供者为新版本;

  4. 再将所有消费者升级为新版本;
  5. 然后将剩下的一半提供者升级为新版本;

    服务提供方配置

    version:提供多个版本的远程服务

    <!--4、暴露服务1(ref:指向服务的真正实现)-->
    <dubbo:service interface="com.atguigu.gmall.service.UserService"
                ref="userService1" version="1.0.0"></dubbo:service>
    <!--服务的实现1-->
    <bean class="com.atguigu.gmall.service.impl.UserServiceImpl" id="userService1"></bean>
    <!--暴露服务2-->
    <dubbo:service interface="com.atguigu.gmall.service.UserService"
                ref="userService2" version="2.0.0"></dubbo:service>
    <!--服务的实现2-->
    <bean class="com.atguigu.gmall.service.impl.UserServiceImpl2" id="userService2"></bean>
    

    服务消费方配置

    <!--version:声明需要调用的特定版本远程服务的接口,生成远程服务代理-->
    <dubbo:reference timeout="6000" retries="3" version="2.0.0" interface=
    "com.atguigu.gmall.service.UserService" id="userService"></dubbo:reference>
    

    本地存根

    服务消费者远程调用服务提供者的功能,功能的实现在服务提供者中,服务消费者中只有远程调用的接口,有时候远程调用之前需要做一些比如参数验证,要做缓存,此时就要编写本地存根;
    image.png

    服务消费方

    本地存根代码

    /*本地存根代码*/
    public class UserServiceStub implements UserService {
     private final UserService userService;
     /**
      * 有参构造对象,传入的是userService远程的代理对象
      */
     public UserServiceStub(UserService userService) {
         this.userService = userService;
     }
     @Override
     public List<UserAddress> getUserAddressList(String userId) {
         System.out.println("调用了本地存根:UserServiceStub");
         if(!StringUtils.isEmpty(userId)){
             //如果传入的userId不是空串时再进行远程调用
             return userService.getUserAddressList(userId);
         }
         return null;
     }
    }
    

    配置文件注册本地存根

    <!--声明需要调用的远程服务的接口,生成远程服务代理-->
    <dubbo:reference interface="com.atguigu.gmall.service.UserService" id="userService"
                               timeout="6000" retries="3" version="2.0.0" 
                               stub="com.atguigu.gmall.service.impl.UserServiceStub"/>
    

    springboot整合dubbo的3种方式

  6. 导入dubbo-starter,在application配置文件中配置属性:

    1. 使用@Service注解暴露服务;
    2. 使用@Reference引用服务;
    3. 主程序类配置@EnableDubbo开启dubbo注解功能或者application配置文件中配置如下:
      dubbo:  
      scan:
      base-packages: com.atguigu
      
  7. 保留dubbo.xml配置文件,application配置文件中就不需要配置dubbo相关内容了,只需在主程序类中配置@ImportResource(locations = "classpath:dubbo.xml")即可;

  8. 使用注解API的方式,导入dubbo-starter,不需要dubbo.xml配置文件,application配置文件中就不需要配置dubbo相关内容,将每一个组件手动创建到容器中(配置类),并在主配置类上面配置@EnableDubbo(scanBasePackages = "com.atguigu.gmall")注解,并在方法实现类上面标注@Service(import com.alibaba.dubbo.config.annotation.Service;)注解即可;
    1. 配置类如下:
      @Configuration
      public class MyDubboConfig {
      @Bean
      //代替”<dubbo:application...“
      public ApplicationConfig applicationConfig(){
        ApplicationConfig applicationConfig=new ApplicationConfig();
        applicationConfig.setName("boot-provider-666");
        return applicationConfig;
      }
      @Bean
      //代替<dubbo:registry...
      public RegistryConfig registryConfig(){
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setProtocol("zookeeper");
        registryConfig.setAddress(
            "192.168.80.130:2181,192.168.80.131:2181,192.168.80.132:2181");
        return registryConfig;
      }
      @Bean
      //代替<dubbo:protocol...
      public ProtocolConfig protocolConfig(){
        ProtocolConfig protocolConfig=new ProtocolConfig();
        protocolConfig.setName("dubbo");
        protocolConfig.setPort(20080);
        return protocolConfig;
      }
      @Bean
      //代替<dubbo:service
      public ServiceConfig<UserService> serviceConfig(UserService userService){
        ServiceConfig<UserService> serviceConfig=new ServiceConfig<>();
        serviceConfig.setInterface(UserService.class);
        serviceConfig.setRef(userService);
        //由于接口实现类已经使用"@Component"注解注册到ioc容器中了
        //所以这里方法形参里可以直接得到
        serviceConfig.setTimeout(2000);
        serviceConfig.setVersion("1.0.0");
        //配置每一个method的信息
        MethodConfig methodConfig = new MethodConfig();
        methodConfig.setName("getUserAddressList");//具体方法名
        methodConfig.setTimeout(1000);
        //将单个method的设置保存在serviceConfig里面
        List<MethodConfig> methodConfigs=new ArrayList<>();
        methodConfigs.add(methodConfig);
        serviceConfig.setMethods(methodConfigs);
        return serviceConfig;
      }
      //其他的配置以此类推
      }
      

      高可用

      zookeeper宕机与dubbo直连

      zookeeper宕机

      现象:zookeeper注册中心宕机,还可以消费dubbo暴露的服务。
      原因:
  • 健壮性
    • 监控中心宕掉不影响使用,只是丢失部分采样数据;
    • 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务;
    • 注册中心对等集群,任意一台宕掉后,将自动切换到另一台;
    • 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯;
    • 服务提供者无状态,任意一台宕掉后,不影响使用;
    • 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复;

      dubbo直连

      服务消费者可以绕过注册中心,直接与服务提供者连接;
      通过@Reference(url="192.168.80.1:20080")
      直接与服务提供者连接;

      集群下dubbo负载均衡配置

      —->>>官方文档

      《负载均衡策略》

      Random LoadBalance

      随机,按权重设置随机概率。
      image.png
      在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。

      RoundRobin LoadBalance

      轮循,按公约后的权重设置轮循比率。
      image.png
      存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。

      LeastActive LoadBalance

      最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
      image.png
      使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。

      ConsistentHash LoadBalance

      一致性Hash,相同参数的请求总是发到同一提供者。
      image.png
      当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。

      dubbo的负载均衡机制

      默认是**random**随机的负载均衡机制:
      image.png

      更改dubbo的负载均衡机制

      public class RandomLoadBalance extends AbstractLoadBalance {
      public static final String NAME = "random";
      ...
      public class LeastActiveLoadBalance extends AbstractLoadBalance {
      public static final String NAME = "leastactive";
      ...  
      public class RoundRobinLoadBalance extends AbstractLoadBalance {
      public static final String NAME = "roundrobin";
      ...
      
      服务端服务级别
      <dubbo:service interface="..." loadbalance="roundrobin" />
      
      客户端服务级别
      <dubbo:reference interface="..." loadbalance="roundrobin" />
      
      服务端方法级别
      <dubbo:service interface="...">
      <dubbo:method name="..." loadbalance="roundrobin"/>
      </dubbo:service>
      
      客户端方法级别
      <dubbo:reference interface="...">
      <dubbo:method name="..." loadbalance="roundrobin"/>
      </dubbo:reference>
      
      动态调整权重
      可以通过dubbo控制台直接更改:
      image.png

      整合hystrix-服务熔断与降级处理

      服务降级

      介绍

      当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作【对于服务消费者】。

      服务器降级的两种方式

      (可以在dubbo控制台直接进行调整;)
  1. mock=force:return+null表示消费方对该服务的方法调用都直接返回null值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
    1. image.png
  2. 还可以改为mock=fail:return+null表示消费方对该服务的方法调用在失败后,再返回null值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。

    1. image.png

      集群容错

      在集群调用失败时,Dubbo提供了多种容错方案,缺省为failover重试;

      集群容错模式

      Failover Cluster
      失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过retries="2"来设置重试次数(不含第一次)。
      重试次数配置如下:
      <dubbo:service retries="2"/><dubbo:reference retries="2"/>
      <dubbo:reference>
      <dubbo:method name="findFoo" retries="2" />
      </dubbo:reference>
      
      Failfast Cluster
      快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
      Failsafe Cluster
      失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
      Failback Cluster
      失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
      Forking Cluster
      并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过forks="2"来设置最大并行数。
      Broadcast Cluster
      广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。

      集群模式配置

      按照以下示例在服务提供方和消费方配置集群模式:
      <dubbo:service cluster="failsafe" />
      或
      <dubbo:reference cluster="failsafe" />
      

      整合hystrix

      Hystrix旨在通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备拥有回退机制和断路器功能的线程和信号隔离,请求缓存和请求打包,以及监控和配置等功能
      spring-cloud-starter-[hystrix](https://so.csdn.net/so/search?q=hystrix&spm=1001.2101.3001.7020)已经弃用】

      原理

      RPC原理

      image.png
      一次完整的RPC调用流程(同步调用,异步另说)如下:
  3. 服务消费方(client)调用以本地调用方式调用服务;

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

RPC框架的目标就是要2~8这些步骤都封装起来;

netty通信原理

Netty是一个异步事件驱动的网络应用程序框架, 用于快速开发可维护的高性能协议服务器和客户端。它极大地简化并简化了TCP和UDP套接字服务器等网络编程。

BIO:(Blocking IO)

image.png

NIO:(Non-Blocking IO)

image.png
Selector一般称为选择器,也可以翻译为多路复用器,用来判断应用执行状态,根据状态再来分配线程来执行下一步,四种状态如下:

  • Connect(连接就绪);
  • Accept(接受就绪);
  • Read(读就绪);
  • Write(写就绪);

    Netty基本原理

    基于NIO的多路复用模型来做的;
    image.png
    boss线程组:用来监视主线程,监听来自于dubbo.protocol.port=20882的所有连接准备就绪事件;
    worker线程组:当准备就绪之后要做什么工作,worker来做这些工作;

    dubbo原理

    1、框架设计

    —->>>官方文档

    2、标签解析

    spring来解析配置文件中的标签都会有一个总接口BeanDefinitionParser
    image.png
    public void init() {//各个标签的名称在源码里写死了
      this.registerBeanDefinitionParser("application", 
            new DubboBeanDefinitionParser(ApplicationConfig.class, true));
      //这里解析"application"标签并将标签里的内容封装到ApplicationConfig里
      this.registerBeanDefinitionParser("module", 
            new DubboBeanDefinitionParser(ModuleConfig.class, true));
      this.registerBeanDefinitionParser("registry", 
            new DubboBeanDefinitionParser(RegistryConfig.class, true));
      this.registerBeanDefinitionParser("monitor", 
            new DubboBeanDefinitionParser(MonitorConfig.class, true));
      this.registerBeanDefinitionParser("provider", 
            new DubboBeanDefinitionParser(ProviderConfig.class, true));
      this.registerBeanDefinitionParser("consumer", 
            new DubboBeanDefinitionParser(ConsumerConfig.class, true));
      this.registerBeanDefinitionParser("protocol", 
            new DubboBeanDefinitionParser(ProtocolConfig.class, true));
      this.registerBeanDefinitionParser("service", 
            new DubboBeanDefinitionParser(ServiceBean.class, true));
      this.registerBeanDefinitionParser("reference", 
            new DubboBeanDefinitionParser(ReferenceBean.class, false));
      this.registerBeanDefinitionParser("annotation", 
            new AnnotationBeanDefinitionParser());
    }
    
    private static BeanDefinition parse(Element element, 
                                      ParserContext parserContext, 
                                      Class<?> beanClass, 
                                      boolean required) {
    RootBeanDefinition beanDefinition = new RootBeanDefinition();
      //最终调用上面DubboNamespaceHandler(dubbo的名称空间)方法
    ...
    if (ProtocolConfig.class.equals(beanClass))...
      //下面进行各种标签校验
    

    3、服务暴露

    public class ServiceBean<T> extends ServiceConfig<T> 
        implements InitializingBean, DisposableBean,ApplicationContextAware,
                  ApplicationListener<ContextRefreshedEvent>,BeanNameAware {...
    
    image.png

    4、服务引用

    reference标签对应ReferenceBean解析类;
    image.png

    5、服务调用

    image.png