dubbo学习笔记(简洁版)

本文大部分参考视频:https://www.bilibili.com/video/BV1ns411c7jV?from=search&seid=11893179849005063053
在这里插入图片描述

1. 分布式基础理论

1.1 什么是分布式系统?

  《分布式系统原理与范型》定义:“分布式系统(distributed system)是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统”(多个计算机结合在一起,为用户提供系统服务,让用户感觉就像在用一个计算机一样)。

1.2 为什么需要分布式系统?

  规模的逐步扩大和业务的复杂,单台计算机扛不住双十一那样的流量,俗话说,三个臭皮匠顶个诸葛亮。

1.3 集群和分布式

  集群:许多计算机一起,干一样的事。
  分布式:许多计算机一起,干不一样的事。这些不一样的事,合起来是一件大事。

1.4 发展演变

在这里插入图片描述

单一应用架构:

在这里插入图片描述  所有功能都在一个war包。就像我们在大学期间写的项目,都是小型的应用,访问量又很小,把它打包统统放在一台服务器上完全没问题,就不需要搞什么大型的分布式系统,一个服务器就搞定,开发简单,部署也简单,只需要打包为应用往服务器一塞就行了,这就是单一应用架构。当访问量有点大的时候,就增加一台服务器(相当于克隆,分身)来分担压力,但是单一应用架构它有一个缺点,就是扩展不容易,我们要对一个应用里面修改一个功能(可以把功能理解为模块),或者添加一个功能,都会导致我们整个应用都要重新打包,重新部署到服务器上,如果有多台,就不好办了,一个功能的扩展,拉着整个应用也扩展(其他不需扩展的功能),这就是可伸缩性差,扩展性差。第二个缺点,就是协同开发不容易,大家都去改同一个应用,有可能改乱,不利于我们的维护。如果项目越来越大,让一个服务器来跑这一整个应用,也是压力蛮大的,即使你想对硬件进行提升,但成本太高,同时光靠增加服务器也是不行的。还有一个缺点,就是模块与模块之间的耦合度极高,一个模块出现问题,比如用户模块要用到订单模块,如果订单模块出现错误了,就会影响到用户模块,这就是可靠性差。
  除了以上说到的几个缺点,还有像测试成本高呀,迭代困难呀,甚至是跨语言程度差,所谓的跨语言程度差就是我们的项目既然打成了jar包,那么语言上肯定是统一的,不可能这个模块用了这个语言写,其它模块用其它语言写。

垂直应用架构/MVC模式(将项目拆分成模块):

在这里插入图片描述  将刚才那个大的单体应用拆成几个互不相干的小应用(一般按照业务拆分),每一个小应用独立部署到每一台服务器上,好处就是模块与模块之间的耦合度减低了,哪一个小应用功能增加了,我们只改那个小应用,如果增加了,还可继续拆分部署。每一个小应用从头到尾都是完整的,从页面,业务逻辑,数据库都是完整的。跟单体应用相比,协同开发变的容易了,扩展也好了。当然也有缺点,就是不能将界面和业务逻辑分离开来,美工天天动界面,整个应用又要重新部署。同时,每一个应用不可能做到完全独立,可能某个模块要用到另一个模块。

分布式服务架构(将模块拆分成各个服务):

在这里插入图片描述  因为垂直应用架构中页面的问题,所以将核心业务抽取出来。而服务器与服务器之间的调用叫RPC(远程过程调用),注意,不是本地调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。这个架构的难度,就是如何进行远程过程调用,以及如何拆分业务,提供业务的复用程度。

流动计算架构(SOA架构/面向服务的架构):

在这里插入图片描述  随着业务代码的增多,增加的服务器也越来越多,这样,会引发一个问题,就是资源浪费,有的服务器很闲,而有的服务器一直在工作,比如,用户业务访问量比较小,却有100台服务器在跑,而商品业务访问量较大,却只有10台服务器在跑,所以我们需要一个基于访问压力的调度中心,实时监控数据,来动态的调度,提高资源的利用率,这个时候就要采用我们的流动计算架构。也就是说,我们应用不会直接去调服务,而是会通过一个中介,由中介(ESB服务总线)来进行服务之间的交互,返回结果给应用。

微服务架构:

  微服务是一种架构风格,一个大型的复杂软件应用,由一个或者多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的,每个微服务仅关注于完成一件任务并很好的完成该任务,微服务就是一个轻量级的服务治理方案,对比SOA架构,使用注册中心代替ESB服务总线,注册中心相比服务总线来说,更加轻量级,代表技术:SpringCloud,Dubbo等等。

总结

  早期,一个项目比较小,访问量也小,一台计算机完全应付的过来,但是,随着功能的增多,项目也越来越大,一台计算机很难跑动,所以就需要把项目拆分成各个模块分散到各个服务器上来减小压力,后来,随着界面的兴起,一个功能的界面发生变化,会带动整个应用的重新部署,所以,往后,就要让界面和业务逻辑分离开来,就成了一种分布式架构,而各个服务器之间的复杂关系需要一个治理系统来维护,就是后来的流动计算架构,可以这么说,流动计算架构是分布式架构的一种升级。

1.5 为什么Dubbo说自己性能高?

  高性能要从底层的原理说起,既然是一个RPC(Remote Procedure Call)框架,主要干的就是远程过程(方法)调用,那么提升性能就要从最关键,最耗时的两个方面入手:序列化和网络通信
  序列化:我们学习Java网络开发的时候知道,本地的对象要在网络上传输,必须要实现Serializable接口,也就是必须序列化,我们序列化的方案很多:xml、json、二进制流…其中效率最高的就是二进制流(因为计算机就是二进制的)。然而Dubbo采用的就是效率最高的二进制。
  网络通信:不同于HTTP需要进行7步走(三次握手和四次挥手),Dubbo采用Socket通信机制,一步到位,提升了通信效率,并且可以建立长连接,不用反复连接,直接传输数据。

1.6 其它的RPC框架有哪些?

  Spring Cloud,Motan,gRPC,Thrift,HSF…

1.7 RPC调用流程

  RPC是远程过程调用,是进程中的通信,它是一种思想。而不是规范,由下图,将左块命名为A服务器,右块为B服务器,如果A服务器想要调用B服务器里的一个方法,那么,A服务器就要拜托它身边的小助手,然后它身边的小助手就会与B服务器建立一个socket连接,然后把A服务器想要调用的方法名,参数什么的发到B服务器的小助手手里,B服务器的小助手就会根据你的信息去帮你调用相关的方法,并把处理完后的返回值通过连接返回给你,这就是RPC的整个过程,说白了,就类似于客户端与服务端之间的交流。

在这里插入图片描述
  案例:如下图,A服务器上有一个方法名叫hello,在这个方法的方法体中有段代码B.hi(new User(“张三”));发现是要调B服务器上的方法,那么它就要拜托他身边的小助手,告诉小助手,我呢,需要调用B服务器的hi方法,参数为user。接到命令后,小助手就会携带这些信息进行序列化,方便在网络上传输。发送到B服务器上的小助手,B服务器上的小助手接到信息后,就会把接收的信息进行反序列化,然后调用本地相关的方法,调用完后,会有处理结果的返回值,就把这个返回值返回到B服务器的小助手,B服务器的小助手再把结果进行序列化,发送到A服务器,A服务器接收到后反序列化,调用赋给msg,最后打印输出,整个流程完毕。
  说白了就是A方法要调用B方法,但B方法在另一台机器上,那按照以前学的肯定是调用不起来的,也就是两方法分隔异地,那么要想调用中间必定要经过网络,而RPC就类似http一样,是一种通信方式。
在这里插入图片描述  小插曲:本来是想把尚硅谷的那张图搞过来的,但奈何图片有点模糊,看不惯,所以只有自己画咯┓(;´_`)┏。

1.8 dubbo的前世今生

  • dubbo之前一直都作为Alibaba公司内部使用的框架。
  • 2011年,dubbo被托管到了GitHub上(开源)。
  • 2014年11月发布2.4.11版本后宣布停止更新,此后一段时间很多公司开源了自己基于Dubbo的变种版本(例如当当网的Dubbo X,网易考拉的Dubbo K)
  • 2017年SpringCloud横空出世,Dubbo感觉到压力后连续更新了几个版本。
  • 2018年1月,阿里公司联合当当网将Dubbo和Dubbo X合并,发布了2.6版本。
  • 2018年除夕夜阿里将Dubbo贡献给了Apache基金会。
  • 2018除夕夜至今,Apache维护和更新Dubbo。

1.9 dubbo概念

  Apache Dubbo是一款高性能,轻量级的开源Java RPC框架,它提供了三大核心功能:面向接口的远程方法调用,智能容错和负载均衡,以及服务(所谓的服务就是我们要调用远程service里的某个方法,那个方法就是服务,暴露服务就是暴露方法)自动注册和发现。
  Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,服务治理方案。
  具有以下特性:

在这里插入图片描述

  • 面向接口代理的高性能RPC调用:表示我们在用dubbo的时候,A服务器上要调用B服务器上面的代码,我们只需要将B服务器(对外暴露接口)上的功能接口拿来,调一下接口所在的方法,dubbo就会自动的去找B服务器的代码帮我们来调用,为我们屏蔽了远程的整个调用细节,就类型我们在用mybatis的一样,我们操作数据库的时候我们只需要些mybatis的dao接口,我们调用接口就行了,不需要关心内部的实现逻辑。

  • 智能负载均衡(让每个服务器分担一定的压力,不偏心):当业务非常多的时候,比如用户业务模块,当用户业务模块访问量比较多的时候,一台服务器,不够用,增加几台服务器(让这几台服务器都跑用户业务模块),当用户web模块想要调用用户业务的时候,随便调那个都可以,但是如果这台服务器有100个请求,另外一台服务器20个请求,那么用户web模块就会调用只有20个请求的服务器,这就是负载均衡。

  • 服务自动注册与发现:随业务非常多,每一块的访问量都非常大,如用户业务在1号,2号,3号,4号服务器都有,支付的业务在9号,10号,13号的服务器都有等等,那么我们订单的web想要调用支付业务,我们rpc框架怎么知道我们支付都在哪些服务器上,还有,万一,9号,10号,13号服务器那一台服务器有问题了,我们rpc框架又如何自动的知道这些事呢?我们可以引入一种机制叫注册中心,如下:
    在这里插入图片描述为了动态感知,我们可以把业务,前端web都注册到注册中心里去,相当于弄了一个清单,比如用户业务在哪些服务器有,如果有台服务器出现问题了,就把这台服务器的编号在清单里删掉。总结,A服务器想要调用某个业务,这个业务存在另一台服务器上,但具体这个业务在哪台服务器上有,rpc不知道,就去注册中心找业务对应的服务器列表,至于要调用哪个,由负载均衡机制来调用访问量较小的哪台服务器。

  • 可视化的服务治理和运维:通过可视化的web界面,来动态查询我们服务器的信息,它的健康状况,它的调用统计记录等。

什么是灰度发布?

  比如用户服务在100台服务器同时再跑,我们用户服务做了升级,怕升级不稳定,可选定20台服务器,让它们用新版本的用户服务,剩下的80台用旧版本的服务,等哪20台用的没问题了,我们再选20台,以此类推,慢慢过渡,直到100台都用到了新的用户服务,由旧服务过渡到新服务的过程就叫灰度发布。

dubbo的设计架构

   图中async箭头表示异步,sync为同步:在这里插入图片描述

  • Container负责启动,加载,运行Provider。
  • Provider启动时,向Registry注册自己的服务,报告增加服务节点。
  • Cousumer启动时,向Registry订阅自己的服务,收到报告,有几台服务供我调用。
  • Registry提供Provider列表给Consumer,实时推送变动情况。其实没Registry也是可以的,只不过这次Cousumer要调Provider得采用直连的方式,也就是直接根据ip和端口去定位,但前提是你要记住这么多的ip号,但是有了Registry这个注册中心,我们就不用过多的去关注ip,端口这些了。反正就是最好不要直连,因为这耦合度你也是知道的,在配置文件写死了,如果依靠Registry,那么耦合度才不会那么高,因为你要调用的服务一旦宕机,说不定Registry还会自动为你选择其它的服务,防止调用失败。
  • Consumer根据Provider列表,按负载算法选一台Provider调用。
  • Monitor统计rpc的调用频次。每台服务接口的信息,都会反映到Monitor。以application的名称标识属于哪个应用。

  这个架构贯穿我们将来的整个应用,dubbo将应用分成了以上几种角色,第一个Registry,就是注册中心,Provider,服务提供者(为我们提供服务的),与之对应,就要服务消费者Consumer,对应到我们之前讲的,用户业务是Provider,而web界面,就是Consumer,Container是dubbo框架容器,Monitor是监控中心,服务者和消费者的监控信息都会发布到监控中心去,整个流程是这样只的,我们dubbo容器启动,服务提供者会将自己提供的信息(比如,对那个业务服务)注册到注册中心里边,这样注册中心就知道有哪些服务器上线了,服务消费者启动,它就会去服务中心来订阅它所需要的服务,如果哪台服务器有了变更,下线了,那么注册中心就会跟消费者基于长链接的方式,注册中心将这次变更推送给我们服务消费者,消费者就实时的知道哪台服务器不能调了,如果要去调的服务跑在多个计算机上,会根据负载均衡算法算出调哪台服务器,并且他们调用的时间,调用服务的一些信息,会定时的,每隔一分钟将信息统计,发送到我们的监控中心去,监控中心就能监控到我们服务的运行状态了。所以,在写dubbo应用的时候,要编写服务提供者,然后把服务放在注册中心,编写消费者在去注册中心里去调,再进行测试消费者调用提供者的方法。

dubbo支持的协议(消费者调用提供者的时候)

  支持多种协议:dubbo,hessian,rmi,http,webservice,thrift,memcached,redis,dubbo官方推荐使用dubbo协议。dubbo协议默认端口20880
  使用dubbo协议,spring配置文件加入:

<dubbo:protocol name="dubbo" port="20880">

2.0 Zookeeper注册中心

  dubbo推荐使用zookeeper为注册中心,https://archive.apache.org/dist/zookeeper/zookeeper-3.4.14/下载zookeeper-3.4.14.tar.gz ,此处为windows版解压后bin目录是一些二进制文件,里面有zkServer.cmd,windows下直接来启动它就可以了,在该目录下输入cmd,书写命令zkServer.cmd,会有一个错误说找不到zoo.cfg,所以要在conf下复制zoo_smaple.cfg的副本,改名为zoo.cfg,打开文件,有clientPort=2181,这是zookeeper的端口号。可以在bin的同级目录下建立一个文件夹,叫data,然后在zoo.cfg中找到dataDir的key,把value改为…/data,表示临时数据的存放位置,再次在cmd书写zookeeper.cmd,在结尾有zookeeper的2181端口,那么这个zookeeper就启动起来了,我们可以来测试一下,点击zkCli.cmd来连接zookeeper服务器, 用get /命令来看一下根节点有哪些值没有,用ls /发现根节点下有一个节点叫zookeeper节点,用中括号括起来的。还可以创建一个临时节点create -e /atguigu 123456 值保存123456 用ls /继续查看,用get /atguigu来查看值。
  启动zookeeper服务用zkServer.cmd命令即可启动。而zkCli.cmd是做测试用的,可以获取节点的相关数据,get获取节点值,ls查看有哪些节点 ,注册中心的作用是管理服务,发现服务,就是用来发现具体的服务地址的

2.1 安装zookeeper管理控制台

  管理控制台(便于排查,比如观察那个地方出现问题,是消费方还是提供方,可以理解该管理控制台就是zookeeper的可视化界面)可以不安装,但是通过管理控制台,可以让用户通过可视化的界面来管理和维护的众多的服务,通过界面就可以直观的看到各个服务的运行情况,那怎么安装呢?地址:https://codeload.github.com/apache/dubbo-admin/zip/master。下载dubbo-admin-master.zip包,然后解压,打开,如下:
在这里插入图片描述  点开dubbo-admin目录(实际上是springboot项目),点击pom.xml,发现是打jar包的方式,在此说明一下,这是dubbo2.6版本以后的方式,也就是说,dubbo2.5之前是以war包 的方式存在的,我们这个jar包是以springboot的方式直接打成可执行的jar包。打包之前先改上一处配置,就在src\main\resources\下的application.properties文件,打开,如下:发现有这样的代码 dubbo.registry.address=zookeeper://127.0.0.1:2181,zookeeper地址现在默认是本机地址的2181端口,正确,无问题。现在回到与src同级目录下,输入cmd命令,进行打包,mvn clean package,如果出现’mvn’ 不是内部或外部命令,也不是可运行的程序或批处理文件。注意配环境变量,https://www.cnblogs.com/rgever/p/9824992.html,最后会出现BUILD SECCESS,说明打包成功,在target目录下就有dubbo-admin-0.0.1-SNAPSHOT.jar的jar包。
在这里插入图片描述  target目录就在跟src同级的目录下,进入target目录,输入cmd,用java -jar dubbo-admin-0.0.1-SNAPSHOT.jar命令来运行这个jar包,前提是zookeeper要启动。记住,端口是7001(application.properties里已经配了)。密码用户名都是root。所以,访问http://localhost:7001/,输入用户名和密码就会进入如下界面:
在这里插入图片描述

2. 编写第一个Hello World

提出需求

  某个电商系统,订单服务需要调用用户服务获取某个用户的所有地址;我们现在需要创建两个服务模块进行测试。

模块功能
订单服务web模块创建订单等
用户服务service模块查询用户地址等

  测试预期结果:
  订单服务web模块在A服务器,用户服务模块在B服务器,A可以远程调用B的功能。

编写提供者

项目结构:

在这里插入图片描述

maven坐标:

<dependency>
   <groupId>com.alibaba</groupId>
    <artifactId>dubbo</artifactId>
    <version>2.6.2</version>
</dependency>
<!--引入操作zookeeper的客户端
 注意:这是dubbo2.6.2对应的,在这之前都是用ZKClient
-->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>2.12.0</version>
</dependency>

编写实体类:

package com.cht.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class UserAddress implements Serializable {
    private Integer id;
    private String userAddress;//用户地址
    private String userId;  //用户id
    private String consignee; //收件人
    private String phoneNum; //电话号码
    private String isDefault; //是否为默认地址 Y是 N 否
}

UserService类:

package com.cht.service;

import com.cht.pojo.UserAddress;

import java.util.List;

//用户服务接口
public interface UserService {

    //功能:根据用户id,返回他的所有收货地址
    List<UserAddress> getUserAddressList(String userId);
}

UserServiceImpl类:

package com.cht.service.impl;
import java.util.Arrays;
import java.util.List;
import com.cht.pojo.UserAddress;
import com.cht.service.UserService;

public class UserServiceImpl implements UserService {
	@Override
	public List<UserAddress> getUserAddressList(String userId) {
		// TODO Auto-generated method stub
		System.out.println("UserServiceImpl..3.....");
		//这里就不写数据库
		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);
	}
}

在resources文件夹下新建provider.xml:

<?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-provider"/>
    <!--2,指定注册中心的位置 我们是吧zookeeper作为注册中心-->
    <!--还有Multicast Redis Simple 也可作为注册中心-->
    <dubbo:registry address="zookeeper://127.0.0.1:2181"></dubbo:registry>
    <!--注意:zookeeper协议名也可单独提出来-->
    <!--<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"></dubbo:registry>-->
    <!--3,指定通信规则(通信协议 ? 通信端口) 消费者如果想跟提供者进行通信,用什么协议通信,哪个端口通信-->
    <!--dubbo只是其中的一个协议,还有rmi  hessian  http  webservice thrift memcached redis rest-->
    <!--端口号随便写,此处为20080-->
    <!--用dubbo协议在20880端口暴露服务,不指定默认也是20880。也就是说,如果确定是20880的话,下面的dubbo:protocol标签可以不写-->
    <dubbo:protocol name="dubbo" port="20880"></dubbo:protocol>
    <!--4,暴露服务 要把哪个服务推到注册中心去 dubbo只要一启动,自动就会注册到注册中心去-->
    <!--也就是说,我们其实暴露的是接口服务,而真正的实现类是ref指定的-->
    <dubbo:service interface="com.cht.service.UserService" ref="userServiceImpl"></dubbo:service>
    <bean id="userServiceImpl" class="com.cht.service.impl.UserServiceImpl"></bean>
</beans>

测试:

package com.cht;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.io.IOException;

public class MainApplication {
    public static void main(String[] args) throws IOException {
        //启动ioc容器  provider.xml
        /*ApplicationContext applicationContext = new ClassPathXmlApplicationContext("provider.xml");*/
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("provider.xml");
        applicationContext.start();
        //不让程序立即终止,让它阻塞读取字符
        System.in.read();
    }
}

  如果没报错,再打开dubbo的监控中心,如下:
在这里插入图片描述

编写消费者

在这里插入图片描述

在resources文件夹下新建consumer.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <!--为了OrderServiceImpl里的注解能生效,我们配置包扫描-->
    <context:component-scan base-package="com.cht.service.impl"></context:component-scan>
    <!-- 1,指定当前服务,应用的名字(同样的服务名字相同,不要和别的服务同名)-->
    <dubbo:application name="order-consumer"/>
    <!--2,指定注册中心的位置-->
    <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"></dubbo:registry>
    <!--3,声明你需要调用的远程服务接口 , 生成远程服务代理
    interface里填的是远程服务暴露的服务接口
    给它一个id方便在本项目中引用,用@AutoWired注解将其注入
    -->
    <dubbo:reference interface="com.cht.service.UserService" id="userService"></dubbo:reference>
</beans>

编写OrderService:

package com.cht.service;

public interface OrderService {
    //初始化订单
     void initOrder(String userId);
}

编写OrderServiceImpl:

package com.cht.service.impl;

import java.util.List;

import com.cht.pojo.UserAddress;
import com.cht.service.OrderService;
import com.cht.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
 * 1、将服务提供者注册到注册中心(暴露服务)
 * 		1)、导入dubbo依赖(2.6.2)\操作zookeeper的客户端(curator)
 * 		2)、配置服务提供者
 * 
 * 2、让服务消费者去注册中心订阅服务提供者的服务地址
 * @author lfy
 *
 */
@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    UserService userService;
	@Override
	public void initOrder(String userId) {
		// TODO Auto-generated method stub
		System.out.println("用户id:"+userId);
		//1、查询用户的收货地址
		List<UserAddress> addressList = userService.getUserAddressList(userId);
		for (UserAddress userAddress : addressList) {
			System.out.println(userAddress.getUserAddress());
		}
		System.out.println(addressList);
	}
}

测试:

package com.cht;
import com.cht.service.OrderService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;

public class MainApplication {
    public static void main(String[] args) throws IOException {
        //启动ioc容器  provider.xml
        /*ApplicationContext applicationContext = new ClassPathXmlApplicationContext("provider.xml");*/
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("consumer.xml");
        OrderService bean = applicationContext.getBean(OrderService.class);
        bean.initOrder("1");
        System.out.println("调用完成");
        //不让程序立即终止,让它阻塞读取字符
        System.in.read();
    }
}

  其它的什么UserService类和实体类就不粘贴了,跟提供方的是一样的,而这里重复引入(注意提供方的UserServiceImpl没有粘贴过来,看项目结构就知道了)是为了防止编译报错。但是重复的东西我们建议提取出来,提成一个新的项目,也就是为公共项目,然后再以maven坐标的方式导入到消费者和提供者上。

编写公共项目

在这里插入图片描述  注意,实现类可不用提取到公共项目中。这样的话,其它两个消费者和提供者的项目结构就变成如下:
在这里插入图片描述
在这里插入图片描述
  要注意,因为消费者和提供者的这两个项目中的实体类和接口类被提取到公共类中了,所以在maven中要加入以下坐标,加入这个坐标的时候记得mvn install,如下:

<dependency>
    <groupId>com.cht</groupId>
    <artifactId>gmall-interface</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

  下面就开始启动项目了,让消费者去调提供者,我们知道,消费者这边是要调远程的getUserAddressList方法的,而本类有这个方法吗?是不是没有,该方法的实现结果是不是就在提供方那里呀,所以,这里调用的过程是不是就涉及到RPC远程调用呀,而RPC的调用过程dubbo是不是实现了呀,所以我们采用dubbo这门技术不就可以完成远程调用吗?即使这个方法不再本项目,在其它项目里,我照样能调到。
  启动项目的时候注意要先启动提供方,让其先暴露服务,然后才是启动我们的消费方,先后顺序别搞混了。
  提供方和消费方启动后,看消费方这边有没有成功调用到提供方的那个getUserAddressList方法,如果成功调到,说明第一个hello world成功了。最后,通过zookeeper的管理控制台我们也可以看到。

3. 安装监控中心

  之前安装的是管理控制台,dubbo-admin,现在安装的是dubbo-monitor-simple,简单的监控中心,可以监控到我们服务的调用信息。
在这里插入图片描述  同样,在src的同级目录下输入cmd,进行打包,mvn package命令,成功会出现BUILD SECCESS(观察会在src的同级目录下生成target目录) 如图:
在这里插入图片描述  打包完成进入target目录会有dubbo-monitor-simple-2.0.0.jar,但是在运行之前要修改一下配置,在target目录下有一个压缩包dubbo-monitor-simple-2.0.0-assembly.tar.gz,将它解压一下。里面有conf的配置文件dubbo.properties,打开,如下:
在这里插入图片描述   我这把8080改为8081,只改这处,然后进入assembly.bin目录进行启动,双击start.bat。提示Dubbo service server started!则成功,要访问,在浏览器输入localhost:8081,如下:
在这里插入图片描述   为了让它能够监控到我们服务的调用信息, 我们可以在消费者和提供者上的xml文件中配置监控中心,以下为参考文档:
   监控中心配置,对应的配置类:org.apache.dubbo.config.MonitorConfig

属性对应URL参数类型是否必填缺省值作用描述兼容性
protocolprotocolstring可选dubbo服务治理监控中心协议,如果为protocol=“registry”,表示从注册中心发现监控中心地址,否则直连监控中心2.0.9以上版本
address<url>string可选N/A服务治理直连监控中心服务器地址,address=“10.20.130.230:12080”1.0.16以上版本

在这里插入图片描述

4. Dubbo与Spring Boot整合

  这时候,既然要与springboot整合,那么必须导入dubbo的启动器。不管是消费者还是提供者。
  注意,引入该启动器的版本要根据springboot的版本来看,如果springboot的版本是2.0.x以上,用的是启动器的0.2.0版本,1.5.x以下的是0.1.1版本。这个启动器帮我们把dubbo,zookeeper都导进来了。

versionsjavaSpring BootDubbo
0.2.01.8+2.0.x2.6.2+
0.1.11.7+1.5.x2.6.2+

  如下:

<!--它把原先的dubbo,甚至是zookeeper的也导进来了。所以,导这一个就够了,如下图:-->
<dependency>
    <groupId>com.alibaba.boot</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>0.2.0</version>
</dependency>

在这里插入图片描述

  除了要导以上坐标,别忘了还有一个坐标,它就是引入公共项目的那个坐标,记得也要导进来。

编写提供者

  创建普通的SpringBoot项目就不说了,创建完之后,把以上的maven坐标导入后,接下来就是配置文件的问题了,以前我们是用一个xml来配置的,这时候我们采用springboot的方式,那么就得在yml中配,或者在application.properties配也是一样的,写法跟以前在xml写的有所不同,但最好对照着看。如下:

dubbo.application.name=user-service-provider
dubbo.registry.address=127.0.0.1:2181
dubbo.registry.protocol=zookeeper
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
dubbo.monitor.protocol=registry

  下一步,把实现类拷贝过来,注意,只要实现类,其它的实体类和接口是不是在公共项目那里呀。好,实现类呢去原有项目找,跟以前一样,这里我就粘贴一下吧,如下:

package com.cht.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.cht.pojo.UserAddress;
import com.cht.service.UserService;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

@Service  //注意,这个是dubbo的注解,不是spring的注解,只是同名了。dubbo的@Service注解表示要暴露该类/服务
@Component
public class UserServiceImpl implements UserService {
	@Override
	public List<UserAddress> getUserAddressList(String userId) {
		// TODO Auto-generated method stub
		System.out.println("UserServiceImpl..3.....");
		//这里就不写数据库
		UserAddress address1 = new UserAddress(1, "北京市昌平区宏福科技园综合楼3层", userId, "李老师", "010-56253825", "Y");
		UserAddress address2 = new UserAddress(2, "深圳市宝安区西部硅谷大厦B座3层(深圳分校)", userId, "王老师", "010-56253825", "N");
		return Arrays.asList(address1,address2);
	}
}

  唯一要注意的是@Service注解,注意观察,这个@Service来自哪个包,是不是dubbo包呀,作用注释说了,就是暴露服务,因为当前项目的定位是提供者。
  当然了,还有一个不同点,就是getUserAddressList的返回值不再是void,而是List<UserAddress>,这么一改,公共里的接口方法也要相应的更改,那么maven就要重新mvn install了。
  最后,在主程序类上添上@EnableDubbo注解,作用是开启基于注解的dubbo功能。如果以上都没问题,开始测试,启动项目,再打开zookeeper管理后台。

编写消费者

  一样,创建SpringBoot项目,只不过该项目在创建的时候要指定是Spring Web项目。
  看一下坐标导全了没有,如果没问题,就开始配置application.properties文件,如下:

dubbo.application.name=boot-order-service-consumer
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.monitor.protocol=registry

  编写service实现类,如下:

package com.cht.impl;

import com.alibaba.dubbo.config.annotation.Reference;
import com.cht.pojo.UserAddress;
import com.cht.service.OrderService;
import com.cht.service.UserService;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class OrderServiceImpl implements OrderService {

	//@Autowired
	@Reference
	UserService userService;
	@Override
	public List<UserAddress> initOrder(String userId) {
		// TODO Auto-generated method stub
		System.out.println("用户id:"+userId);
		//1、查询用户的收货地址
		List<UserAddress> addressList = userService.getUserAddressList(userId);
		return addressList;
	}
}

  这次要注意的是@Reference注解,表示远程注入,表示要远程调用userService。

  编写controller类,如下:

package com.cht.controller;
import com.cht.pojo.UserAddress;
import com.cht.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;

@Controller
public class OrderController {

    @Autowired
    OrderService orderService;

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

最后,还是别忘了,在启动类上加上@EnableDubbo注解,启动成功后,访问地址:http://localhost:8080/initOrder?uid=5,uid随便你等于什么,如下:
在这里插入图片描述

5. 配置

5.1 dubbo.properties&属性加载顺序

  对于dubbo的配置文件能写什么信息,或者标签,可以参考该官网手册:https://dubbo.apache.org/zh/docs/v2.7/user/configuration/xml/.
  而对于该dubbo的配置信息是否生效,就要认识下面的生效策略了,如下是比较简单的一幅图:
在这里插入图片描述  如上图,表示我们可以在应用启动的时候通过java的-D添加虚拟机参数来改变属性的值。也可以配在dubbo.xml文件(跟application.properties,application.yml是同级的,但是application.properties文件的优先级高于yml)中,或者写在dubbo.properties文件中,而这个dubbo.properties是dubbo的公共配置信息。也就是说,以上三种方式都可以配置dubbo信息,但如果这三个都配置了一样的信息,那么以谁为准,这就涉及到优先顺序了。
  看上图,优先顺序是从上到下的,也就是上面的更加优先

5.2 启动时检查

  Dubbo缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止Spring初始化完成,以便上线时,能及早发现问题,默认check=“true”。
  说白了,就是如果消费者启动的时候,发现它要调用的服务在注册中心没找到,在启动的时候就会报错,默认check=“true”,如果check=“false”,那么启动的时候不会报错,只有调用的时候才报错。
  那么加在哪?以xml为例,如下:

<dubbo:reference interface="com.cht.service.UserService" id="userService" check="false"></dubbo:reference>

  如果要引用的服务很多,就要声明多个check=“false”,麻烦,可以统一配置,如下:

<dubbo:consumer check="false"></dubbo:consumer>

  还有一种是对注册中心的检查,也是check="true"之类的,如果为true,表示如果有注册中心,启动一切正常,如果没有,启动就会报错。为false反之。如下:

<dubbo:registry check="false"/>

  application.properties的方式如下:

#设置check的缺省值(默认值),如果配置中有显式的声明,如:<dubbo:reference check="true"/>,不会受影响。
dubbo.consumer.check=false

  注解的方式:

@Reference(check = false)

5.3 超时&配置覆盖信息

  timeout为超时设置,意思是我们客户端想要引用服务端的方法,如果在服务费哪里还没有处理完,可能网络原因等等,造成线程阻塞,性能下降,在客户端迟迟等不到值,我们就 结束这次任务,假如3秒后结束就是timeout=“3000” ,以毫秒为单位,当要注意,dubbo中的timeout是有默认值的就是1000(用的是dubbo:consumer里的timeout),也就是说,如果你不配置timeout属性,也一样报错,我们可以在服务端哪里加Threed.sleep(4000)睡眠线程。
  如下:

<dubbo:reference interface="com.cht.service.UserService" id="userService" timeout="5000"></dubbo:reference>

  以上是配置单个的,如果是全局的,跟启动时检查那一节是一样的配法,如下:

<dubbo:consumer timeout="5000"></dubbo:consumer>

  那么,问题来了,在dubbo:reference配了超时时间,在dubbo:consumer也配了超时时间,那么以谁为准?有如下图:
在这里插入图片描述  一样,最上面的优先级最高。如上图,我们发现,在dubbo:reference里面有dubbo:method,表示精确到某个方法,而在这配了一个超时属性,也就是说,timeout="5000"只对某一个方法有效,对其它方法无效。注意,如果dubbo:reference也配了超时属性,那么以精确优先。
  服务提供者本身也能指定超时服务,从上图不难看出,消费者的比提供者的优先。

总结

  • 精确优先(方法级优先,接口级次之,全局配置再次之)。
  • 消费者设置优先(如果级别一样,则消费方优先,再是提供方 )。

5.4 重试次数

  timeout一般配合retries(重试次数)用,不包含第一次调用(也就是写3,那么最多调用4次),如果有多个服务器都有相同的业务方法,那么重试在这台试不了,会跑到另一台试,反正就是轮询,一个一个来。同时还要介绍一个术语,叫幂等和非幂等,幂等可以设置重试次数,例如【查询,删除,修改】,执行多次的效果跟执行一次的效果是一样的,如果是非幂等(不能设置重试次数)执行多次有不同的效果,比如【新增】,也就是说,本来只添加一条数据,而你反复重试,就有可能造成添加重复的多条数据,反正这点就要看你的业务需求了。0代表不重试。

<dubbo:reference interface="com.cht.service.UserService" id="userService" timeout="5000" retries="3"></dubbo:reference>

  以上我只是把尚硅谷的第16节用文字表达出来而已,如果看不懂可以去看视频。

5.5 多版本

  当一个功能升级时,不要让全部都升级,以免造成系统不稳定,先让一些服务器还用就版本,等到新版本稳定,旧再翻新,这种特性用到了多版本的特性。
  在提供者这边,如下:
在这里插入图片描述  那么在xml这边,如下:

<dubbo:service interface="com.cht.service.UserService" ref="userServiceImpl" version="1.0.0"></dubbo:service>
<dubbo:service interface="com.cht.service.UserService" ref="userServiceImpl1" version="2.0.0"></dubbo:service>
<bean id="userServiceImpl" class="com.cht.service.impl.UserServiceImpl"></bean>
<bean id="userServiceImpl1" class="com.cht.service.impl.UserServiceImpl1"></bean>

  注意后面的version属性。我先说下,UserServiceImpl1类是新版本,相对于UserServiceImpl来说。
  在消费者这边,如下:

<dubbo:reference interface="com.cht.service.UserService" id="userService" version="2.0.0"></dubbo:reference>

  也是要注意后面的version属性,此时version属性的值是2.0.0,说明我们调用的是新版本,也就是userServiceImpl1这个实现类,如果你把值改为1.0.0,那么调用的就是原来的版本。像这种就是前面我们说过的灰度发布。
  最后,如果把以上代码的version值改为*,代表随机选择,不管是UserServiceImpl还是UserServiceImpl1。

5.6 本地存根

  比如在调用之前,要做一下参数验证,或做一些缓存,就可以用本地存根,我们可以在消费者这边写一个本地存根,其实说白了就是一个类,我们到时候会在消费者这边的xml进行配置,也就是在dubbo:reference中指定本地存根类,那么到时候在调用远程服务的时候会先调用本地存根类,如下:

<dubbo:reference interface="com.cht.service.UserService" id="userService"  stub="com.cht.service.UserServiceStub"></dubbo:reference>

  stub属性指定的类就是本地存根类,该类需要我们自己编写,因为你这里指定了,那么到时候dubbo会自动调用的,这个你放心,如下就是UserServiceStub类:

package com.cht.impl;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.cht.pojo.UserAddress;
import com.cht.service.UserService;

import java.util.List;

public class UserServiceStub implements UserService {

	//必须定义这个接口,以便接收dubbo在调用远程服务生成的服务代理类
    private final UserService userService;

    //dubbo会自动创建本地存根对象,不用我们操心
    //同时它会自动传入UserService的代理对象
    //这个构造函数必须要提供,dubbo框架会在消费者这一方调用这个方法
    public UserServiceStub(UserService userService) {
        this.userService = userService;
    }

    @Override
    public List<UserAddress> getUserAddressList(String userId) {
        //本地存根的意义就在于在调用远程服务之前做一下小验证
        if(!StringUtils.isEmpty(userId)){  //判断userId是否为空
        	//如果不为空,调用远程服务,否则返回null
            return userService.getUserAddressList(userId);
        }
        return null;
    }
}

  注意,为什么要有本地存根,那是因为实现逻辑都在远程服务器上,如果我们有时候想在消费者这端也执行部分逻辑,那么就可以在消费者这端提供一个Stub类。

5.7 与Spring Boot整合的三种方式

  • 导入dubbo-starter,在application.properties配置属性。使用@Service来暴露服务,@Reference来引用服务。但注意要在启动类加上@EnableDubbo来开启注解,其实就是定义了包扫描规则,我们可以在properties中这样写,dubbo.scan.base-packages=com.cht

  • 第二种,是保留dubbo.xml配置文件的方式,也就是说,对于提供者而言,我们就不采用application.properties方式了,而是采用像provider.xml的方式,我们原先不就是用provider.xml的方式吗?还未与springboot整合之前,但现在,即使跟springboot整合了,依然可以用原先的方式。那好,我们就在resources文件夹下新建provider.xml,然后大家往上翻,找到原先的代码后全部复制过来即可,最后在启动类上方加上@ImportResource(locations = "classpath:provider.xml")注解,别忘了此时@EnableDubbo和@Service注解就可以注释掉了。

  • 第三种,使用注解API的方式,那么这时候,我就不采用什么xml啊,properties啊这些,而是写一个配置类,注意,是类。如下:

    package com.cht.config;
    import com.alibaba.dubbo.config.*;
    import com.cht.service.UserService;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import java.util.ArrayList;
    import java.util.List;
    
    @Configuration
    public class MyDubboConfig {
    
        @Bean
        public ApplicationConfig applicationConfig(){
            ApplicationConfig applicationConfig = new ApplicationConfig();
            applicationConfig.setName("user-service-provider"); //设置应用名
            return applicationConfig;
        }
    
        @Bean
        public RegistryConfig registryConfig(){
            RegistryConfig registryConfig = new RegistryConfig();
            registryConfig.setProtocol("zookeeper");
            registryConfig.setAddress("127.0.0.1:2181");
            return registryConfig;
        }
    
        @Bean
        public ProtocolConfig protocolConfig(){
            ProtocolConfig protocolConfig = new ProtocolConfig();
            protocolConfig.setName("dubbo");
            protocolConfig.setPort(20882);
            return protocolConfig;
        }
    
        /* <dubbo:service interface="com.cht.service.UserService" ref="userServiceImpl" version="1.0.0">
            <dubbo:method name="getUserAddressList" timeout="1000"></dubbo:method>
        </dubbo:service>*/
        @Bean
        public ServiceConfig<UserService> userServiceConfig(UserService userService){
            ServiceConfig<UserService> serviceConfig = new ServiceConfig<>();
            serviceConfig.setInterface(UserService.class);
            serviceConfig.setRef(userService);
            serviceConfig.setVersion("1.0.0");
            MethodConfig methodConfig = new MethodConfig();
            methodConfig.setName("getUserAddressList");
            methodConfig.setTimeout(1000);
            List<MethodConfig> methodConfigs = new ArrayList<>();
            methodConfigs.add(methodConfig);
            serviceConfig.setMethods(methodConfigs);
            return serviceConfig;
        }
    }
    

      最后在启动类上加上@DubboComponentScan(basePackages = "com.cht")注解, 但注意@Service这个暴露服务不要去掉。

6. 高可用

6.1 Zookeeper宕机与Dubbo直连

  高可用:通过设计,减少系统不能提供服务的时间。
  现象:zookeeper注册中心宕机,还可以消费dubbo暴露的服务。

  • 监控中心宕机不影响使用,只是丢失部分采样数据。

  • 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务。

  • 注册中心对等集群,任意一台宕掉后,将自动切换到另一台。

  • 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯。也就是说,在注册中心还没宕掉前,如果消费者已经调用过一次提供者了,那么在接下来即使注册中心宕机了,也能够再次访问。

  • 服务提供者无状态,任意一台宕掉后,不影响使用。

  • 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复。

    直连:

    @Reference(url="127.0.0.1:20882")
    

6.2 负载均衡机制

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

  • Random LoadBalance(默认的)
    在这里插入图片描述  如上图,orderService想要调userService,而userService在三台机器上都有,也就是如上的1,2,3。我们可以为每一台机器的服务设置权重,如1号机器的权重是100,二号机器的权重是200,三号机器的权重是50,那么合起来总权重就是350,而对于第一号机器来说,在概率上就是350分之100,约一下就是七分之二,也就是说,在负载均衡的情况下,大量请求过来,大约有七分之二的请求会来到第一号机器。其它的机器一样的道理。按上所述,权重就相当于概率的分布。

  • RoundRobin LoadBalance

    在这里插入图片描述  如果不看权重,只看轮询,那么轮询的意思就是一个一个来,比如先是第一号机器,然后是第二号,再是第三号,再下就是第一号,反正就是1,2,3轮流着来。那么加了权重,情况又会是怎么样的呢?假设有七次请求,在轮询的基础上,第一个请求会先来到第一号机器,第二次请求会来到第二号机器,第三次请求会来到第三号机器,第四次请求会来到第一号机器,第五次请求会来到第二号机器,到第六次请求了,注意,它不会来到第三号机器,因为三号机器的概率是七分之一,已经用完一次机会了,那么这次就会到第二号机器,为什么不会到第一号机器呢,因为第一号机器的两次机会已经用完。

  • LeastActive LoadBalance

    在这里插入图片描述  一句话总结:挑上一次请求处理速度最快的服务器。

  • ConsistentHash LoadBalance
    在这里插入图片描述  相同参数的请求总是发到同一提供方。

  配置:

<!--服务端服务级别,以下可以配在方法上-->
<dubbo:service interface="..." loadbalance="roundrobin"/>
<!--客户端服务级别-->
<dubbo:reference interface="..." loadbalance="roundrobin"/>
@Service(weight = 40) //表示在暴露服务时设置权重

  或者用管理后台的方式,如下:
在这里插入图片描述

6.3 服务降级

  当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作。也就是牺牲某一些人的利益,来达到我们的核心利益。比如A服务器上跑了用户服务相关的,还跑了订单服务,广告服务相关的,但此时A服务器现在的流量很大,正在处理大量请求,速度急剧缓慢,我们可以暂时把流量很小的广告服务先屏蔽掉,省一点资源,这样处理速度就会加快。

dubbo支持两种服务降级,如下:

  • mock=force:return+null 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。我们可以直接在管理后台点击屏蔽按钮。如下:
    在这里插入图片描述

  • 还可以改为 mock=fail:return+null 表示消费方对该服务的方法调用在失败后(比如我们设置了超时时间),再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。管理后台操作如下:
    在这里插入图片描述

6.4 集群容错

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

集群容错模式

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

  集群模式配置,按照以下示例在服务提供方和消费方配置集群模式:

<dubbo:service cluster=“failsafe” /><dubbo:reference cluster=“failsafe” />

整合hystrix进行服务容错

  Dubbo实际开发中,服务容错是通过整合hystrix实现的,Hystrix旨在通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备拥有回退机制和断路器功能的线程和信号隔离,请求缓存和请求打包,以及监控和配置等功能。

  • Spring boot官方提供了对hystrix的集成,直接在pom.xml里加入依赖,如下:
    <dependency>
         <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
          <version>1.4.4.RELEASE</version>
    </dependency>
    
  • 然后在Application类上增加@EnableHystrix来启用hystrix starter:
    @SpringBootApplication
    @EnableHystrix //开启服务容错功能
    public class ProviderApplication {		
    }
    
  • 编写提供方,修改getUserAddressList方法,如下:
    @HystrixCommand
    @Override
    public List<UserAddress> getUserAddressList(String userId) {
    	// TODO Auto-generated method stub
    	System.out.println("UserServiceImpl..3.....");
    	//这里就不写数据库
    	UserAddress address1 = new UserAddress(1, "北京市昌平区宏福科技园综合楼3层", userId, "李老师", "010-56253825", "Y");
    	UserAddress address2 = new UserAddress(2, "深圳市宝安区西部硅谷大厦B座3层(深圳分校)", userId, "王老师", "010-56253825", "N");
    	if(Math.random()>0.5){
    		throw new RuntimeException();
    	}
    	return Arrays.asList(address1,address2);
    }
    
    注意,以上方法里在执行的时候可能会抛出异常,而在该方法上加了@HystrixCommand,意味着如果出现错误,Hystrix会进行容错处理。然后我们启动项目。
  • 编写消费者,一样要导入maven坐标,一样要在启动类加入@EnableHystrix注解,一样要在对应方法上加上@HystrixCommand注解,如下:
    @HystrixCommand(fallbackMethod = "hello") //如果出错了交给hello方法处理,没出错那什么事都没发生,该怎么样还怎么样
    @Override
    public List<UserAddress> initOrder(String userId) {
    	// TODO Auto-generated method stub
    	System.out.println("用户id:"+userId);
    	//1、查询用户的收货地址
    	List<UserAddress> addressList = userService.getUserAddressList(userId);
    	return addressList;
    }
    
    public List<UserAddress> hello(String userId) {
    	//当在执行initOrder方法时,该方法里不是要进行远程调用吗?当远程调用失败后进行的逻辑处理
    	return null;
    }
    

7. 原理

  原理部分我只是把视频里的照着说了一下,毕竟在源码这里,我还要不断修炼。

7.1 框架设计

  放图:
在这里插入图片描述  官方文档:https://dubbo.apache.org/zh/docs/v2.7/dev/design/

  • 在如上图,Service那块为业务逻辑层,我们是面对接口编程,想要远程调用,也只需要调接口的方法,对于我们用户编程来说,我们只关心到这一层就结束了。剩下的都是dubbo的原理了。
  • 接下来看Config到Exchange这中间,dubbo把它称为RPC层,也就是完成远程过程调用的那一层。先看Config,它是我们的配置层,配置层主要是用来封装我们配置文件里边解析出来的一些信息,像ReferenceConfig,ServiceConfig我们以前都见过,在讲配置类的时候。也就是说,我们每一个标签都有它相应的Config来封装这些标签的信息。当我们配置层搜集到我们配置的相关数据以后,接下来是下一层。
  • 下一层就是Proxy层,也就是代理层,准确的说是服务代理层,这个代理层就是帮我们来利用代理的方式,比如生成我们客户端的代理对象,服务端的代理对象,那么代理对象就要互相的来调用方法了。
  • Registry层,这是我们的注册中心层,我们的很多服务都要注册到注册中心,包括消费者要来注册中心订阅所需要的服务进行调用。这一层就是完成注册中心的相关功能。比如服务的发现和服务的注册。
  • 下一层是Cluster,它是路由层,也是来帮我们进行负载均衡的这一层。
  • Monitor层,监控层,我们每一次的调用信息,都会发给监控层的一些数据,监控层收到数据以后呢,就可以让我们以界面的方式展现出所有的监控数据。
  • Protocol层,也就是我们的远程调用层,它是来整个封装RPC调用的,RPC调用里边呢核心的三个就是Invoker,Protocol,Exporter,它们就可以完成我们一次远程调用。当然,远程要调用,就得跟A,B两台服务器架起通信的管道,那么,通信的这些,我们包括在通信之间要传递的数据,在下一层,Remoting,也就是Exchange到Serialize。
  • Remoting就是来解决我们远程通信的这一层,看Exchange,表示信息交换层,其实就是创建一个客户端,一个服务端,两个呢架起管道,要进行数据的互联互通。
  • Transport,我们的传输层,我们真正传输数据呢是用Transporter来封装传输的,而Transporter的底层其实就是netty框架,也就是说,netty框架就在这一层工作着。
  • Serialize,序列化层,这个序列化层就是说我们在整个传输的过程中,数据要序列化出去,才能在网络传输。收到数据以后呢,还要反序列化回来,我们才能把真正的数据拿到。

  更加详细的内容以后再说,我会争取出一篇dubbo的原理介绍。

7.2 标签解析

  配置文件是如何解析的?
  首先,有一个主接口,叫BeanDefinitionParser,翻译过来就是Bean定义解析器,它有一个重要的实现类,叫DubboBeanDefinitionParser,在该类中有一个parse方法,就是这个方法来解析标签,那我们Spring容器一启动,就会挨个来解析标签。我们可以debug一下,看看有没有进parse方法就知道了。

7.3 服务暴露流程

在这里插入图片描述

7.4 服务引用流程

在这里插入图片描述

7.5 服务调用流程

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值