Dubbo

目前分布式开发应用中,使用较多的一个RPC框架。

基础知识

分布式基础理论

分布式系统

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

架构发展演变

Dubbo_第1张图片

单一应用架构

当网站流量很小的时候,只需要一个应用,将所有的功能部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。

  • 优点
    • 可以将整个应用打包部署在一台服务器中,开发、部署简单
    • 可以部署多台,分担流量压力
  • 缺点
    • 不易扩展,添加一个功能,需要重新打包整个应用,重新部署到多台服务器
    • 协作开发不容易
    • 单台服务器跑很大的程序时,压力会比较大,已经不能带来性能上的提升了

垂直应用架构

可以将单体应用独立拆分为单个小的应用,并独立部署到不同的服务器上

  • 优点
    • 分工合作容易,每个人开发单独的应用,互不干扰
    • 易扩展,可以单独部署某个小的应用集群,之前是带着一整个完整的应用扩展
  • 缺点
    • 市场上对页面需要经常改动,改动页面就需要重新部署这个应用
    • 应用不可能完全独立,也需要应用之间相互交互

分布式应用架构

随着吹着应用越来越多,应用之间还需要交互,可以将核心功能抽取
A服务器调B服务器,代码之间调用为RPC(远程过程调用)
难点:

  • 如何进行远程过程调用
  • 如何拆分业务,提升复用性

流动计算架构

Dubbo_第2张图片
调度中心负责维护调度之间的复杂关系,以及实施管理服务集群。
基于访问压力,实时管理集群容量,提高集群利用率。

RPC简介

Dubbo_第3张图片

RPC基本原理

Dubbo_第4张图片
网络之间传递数据,都需要进行序列化。
Dubbo_第5张图片
影响RPC框架:

  • 通信效率:能否快速建立连接
  • 序列化效率:序列化与反序列化的速度

Dubbo核心概念

官网

Dubbo架构

Dubbo_第6张图片

注册中心

Dubbo_第7张图片
官方推荐Zookeeper。
Zookeeper注册中心官网地址

Zookeeper(注册中心)

下载

官网
下载列表地址
Dubbo_第8张图片
下载了3.4.11版本
3.4.11下载链接

安装

tar -zxvf zookeeper-3.4.11.tar.gz

启动服务端

进入bin目录

cd bin

启动(Linux)

./zkServer.sh start

报错:
Dubbo_第9张图片
原因:
zoo.cfg文件在conf目录下没有
Dubbo_第10张图片
复制zoo_sample.cfg目录复制一份,并修改名称为zoo.cfg

cp zoo_sample.cfg zoo.cfg

创建data文件夹,并在zoo.cfg配置中指定目录用来存储文件

mkdir data

Dubbo_第11张图片
再次启动即可。

./zkServer.sh start


使用zookeeper的客户端来连接

启动客户端

./zkCli.sh

常用命令

zookeeper是个树形目录结构
Dubbo_第12张图片
查看根节点数据

get /

Dubbo_第13张图片
查看根节点下的节点

ls /

image.png
创建临时节点

#创建一个临时节点 test001 值为abc
create -e /test001 abc

Dubbo_第14张图片
搭建测试完成!

Monitor(监控中心)

可选,不影响操作, 帮助用户通过可视化界面管理服务,查看情况。
Dubbo_第15张图片

下载

生产者工程环境搭建

Dubbo_第16张图片
pom

    <modelVersion>4.0.0modelVersion>

    <groupId>com.xiu.gmallgroupId>
    <artifactId>user-service-providerartifactId>
    <version>1.0-SNAPSHOTversion>

    <dependencies>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.4version>
        dependency>
    dependencies>

实体类:

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

import java.io.Serializable;

/**
 * @author zhangzengxiu
 * @date 2023/3/18
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserAddress implements Serializable {
    private static final long serialVersionUID = -5242080009018461173L;

    private Integer id;

    /**
     * 用户地址
     */
    private String userAddress;

    /**
     * 用户id
     */
    private String userId;

    /**
     * 收货人
     */
    private String consignee;

    /**
     * 电话号码
     */
    private String phoneNum;

    /**
     * 是否默认地址
     */
    private String isDefault;

}

接口:

import com.xiu.bean.UserAddress;

import java.util.List;

/**
 * @author zhangzengxiu
 * @date 2023/3/18
 */
public interface UserService {

    /**
     * 获取用户地址列表
     *
     * @param userId 用户id
     * @return
     */
    public List<UserAddress> getUserAddressList(String userId);

}

接口实现类:

import com.xiu.bean.UserAddress;
import com.xiu.service.UserService;

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

/**
 * @author zhangzengxiu
 * @date 2023/3/18
 */
public class UserServiceImpl implements UserService {

    public List<UserAddress> getUserAddressList(String userId) {
        UserAddress userAddress1 = new UserAddress(1, "北京市xxxxx", "1", "张三", "12345678902", "Y");
        UserAddress userAddress2 = new UserAddress(2, "杭州市xxxxx", "1", "张三", "12345678902", "N");
        return Arrays.asList(userAddress1, userAddress2);
    }
}

消费者工程环境搭建

pom
接口:
此时需要调用user-service中的接口,但是该服务部署在其他服务器上,所以并不能通过本地jar包的形式进行引用,需要调用需要如下操作:
将provider中的bean及其接口复制一份到consumer中:
Dubbo_第17张图片
此时会出现的问题:
Dubbo_第18张图片

  • 本次调用会失败,因为接口的真正实现类在provider中;
  • 如果多个服务都调用了UserService接口,需要在多个项目中都要维护该接口,十分繁琐

我们可以将通用的bean和接口抽取到一个工程中:
dubbo官方也建议我们将服务模型,接口,服务异常等均放在我们的api包下。
其他需要用到的服务进行依赖api包。

公共模块抽取

将接口和bean放在公共模块中,其他服务依赖该服务。
Dubbo_第19张图片
坐标:

      	<dependency>
            <groupId>com.xiu.gmallgroupId>
            <artifactId>gmall-interfaceartifactId>
            <version>1.0-SNAPSHOTversion>
        dependency>

因为我们依赖的接口的真正实现是在其他的服务中,到时候甚至会部署到不同的服务器上,此时就需要进行远程调用才行。

Dubbo进行改造

Dubbo_第20张图片步骤:

  • 将服务提供者注册到注册中心(暴露服务)
  • 消费者去注册中心订阅服务提供者的地址

将服务注册到注册中心,需要导入dubbo的依赖。

导入dubbo和zookeeper的依赖

2.6dubbo及以后版本的zookeeper用的是

<dependency>
		<groupId>org.apache.curatorgroupId>
  	<artifactId>curator-frameworkartifactId>
  	<version>2.12.0version>
dependency>

2.6之前用的是

<dependency>
	<groupId>com.101tecgroupId>
  <artifactId>zkclientartifactId>
  <version>0.10version>
dependency>

配置服务提供者

pom

    <groupId>com.xiu.gmallgroupId>
    <artifactId>user-service-providerartifactId>
    <version>1.0-SNAPSHOTversion>

    <dependencies>
        <dependency>
            <groupId>com.xiu.gmallgroupId>
            <artifactId>gmall-interfaceartifactId>
            <version>1.0-SNAPSHOTversion>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.4version>
        dependency>
        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>dubboartifactId>
            <version>2.6.7version>
        dependency>
        
        <dependency>
            <groupId>org.apache.curatorgroupId>
            <artifactId>curator-frameworkartifactId>
            <version>2.6.0version>
        dependency>
        
        <dependency>
            <groupId>io.nettygroupId>
            <artifactId>netty-allartifactId>
            <version>4.1.32.Finalversion>
        dependency>
    dependencies>

官网配置:

配置文件


<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:dubb0="http://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    
    <dubbo:application name="user-service-provider"/>

    
    <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"/>
    
    

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

    
    <dubbo:service interface="com.xiu.service.UserService" ref="userService"/>

    <bean id="userService" class="com.xiu.service.impl.UserServiceImpl"/>

beans>

1、配置服务名称:一般与项目名称保持一致

    
    <dubbo:application name="user-service-provider"/>

2、指定注册中心

配置方式一:

  	
    <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"/>

配置方式二:

  	
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>

protocol不止zookeeper一种,还有其他多种注册中心可以选择。
其他注册中心:注册中心地址
Dubbo_第21张图片

3、指定两个服务之间的通信规则(协议、端口)

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

name指定协议名称,port指定端口。
同样dubbo也有多种协议可供选择:多种协议(官网)
Dubbo_第22张图片

4、暴露接口

  	
    <dubbo:service interface="com.xiu.service.UserService" ref="userService"/>

dubbo对外的暴露的接口,interface指定接口,ref指向真正的实现,该实现需要时Spring容器的bean
将该实现类放在在Spring容器中:

    <bean id="userService" class="com.xiu.service.impl.UserServiceImpl"/>

启动服务提供者

import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.io.IOException;

/**
* @author zhangzengxiu
* @date 2023/3/18
*/
public class App {
    public static void main(String[] args) throws IOException {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("provider.xml");
        context.start();
        //
        System.in.read();
    }
}

观察控制台:

dubbo控制台

image.png
控制台中已经展示了应用及其暴露的接口。

配置服务消费者

pom

    <groupId>com.xiu.gmallgroupId>
    <artifactId>order-service-consumerartifactId>
    <version>1.0-SNAPSHOTversion>

    <dependencies>
        <dependency>
            <groupId>com.xiu.gmallgroupId>
            <artifactId>gmall-interfaceartifactId>
            <version>1.0-SNAPSHOTversion>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.4version>
        dependency>
        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>dubboartifactId>
            <version>2.6.7version>
        dependency>
        
        <dependency>
            <groupId>org.apache.curatorgroupId>
            <artifactId>curator-frameworkartifactId>
            <version>2.6.0version>
        dependency>
        
        <dependency>
            <groupId>io.nettygroupId>
            <artifactId>netty-allartifactId>
            <version>4.1.32.Finalversion>
        dependency>
    dependencies>

配置文件


<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:dubb0="http://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/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.xiu"/>
    
    <dubbo:application name="order-service-consumer"/>

    
    <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"/>
    
    

    
    <dubbo:reference interface="com.xiu.service.UserService" id="userService"/>

beans>

不同的配置:

生成远程服务代理:申明需要调用的远程服务
    
    <dubbo:reference interface="com.xiu.service.UserService" id="userService"/>

申明需要远程调用的服务接口,并放到IOC容器中,service用到时直接注入即可。
service:

import com.xiu.bean.UserAddress;
import com.xiu.service.OrderService;
import com.xiu.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author zhangzengxiu
 * @date 2023/3/18
 */
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    UserService userService;

    @Override
    public void createOrder(String userId) {
        System.out.println("createOrder userId=" + userId);
        //查询用户收货地址
        List<UserAddress> list = userService.getUserAddressList(userId);
        list.stream().forEach(x -> System.out.println(x.getUserAddress()));
    }
}

Monitor简单配置

服务提供者与消费者进行配置:

     
    
    <dubbo:monitor protocol="registry"/>

    

配置方式:
方式一:

   
    <dubbo:monitor protocol="registry"/>

方式二:

	
	<dubbo:monitor address="127.0.0.1:7001"/>

监控中心

提供者:
Dubbo_第23张图片
消费者:
Dubbo_第24张图片

Dubbo与SpringBoot整合

配置服务提供者

导入dubbo-starer依赖

        
        <dependency>
            <groupId>com.alibaba.bootgroupId>
            <artifactId>dubbo-spring-boot-starterartifactId>
            <version>0.2.0version>
        dependency>

依赖传递导入的dubbo其他依赖
Dubbo_第25张图片
dubbo提供的版本之间的对应关系:
Dubbo_第26张图片

配置dubbo相关信息

配置provider

通过使用application进行配置

server.port=8001
#服务名称
dubbo.application.name=boot-user-service-provider
#注册中心位置
dubbo.registry.protocol=zookeeper
dubbo.registry.address=127.0.0.1:2181
#通信规则
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
#监控中心
dubbo.monitor.protocol=registry

与原生Spring配置的区别:
Dubbo_第27张图片
配置对外暴露的接口:
Dubbo_第28张图片
我们实际开发中,需要对外暴露的接口会非常的多,不可能会挨个手动去配置,使用注解进行配置:
SpringBoot配置方式:是Dubbo的包不是Spring的包!!!
image.png
需要在主启动类上添加注解:@EnableDubbo:开启基于注解的Dubbo功能
Dubbo_第29张图片
启动服务:
查看监控台:
Dubbo_第30张图片

配置consumer

pom
dubbo的相关坐标与provider相同

        
        <dependency>
            <groupId>com.alibaba.bootgroupId>
            <artifactId>dubbo-spring-boot-starterartifactId>
            <version>0.2.0version>
        dependency>

使用application配置问价配置其他配置项:
Dubbo_第31张图片
配置远程调用的接口:
使用纯Spring配置Dubbo远程接口:
image.png
实际开发过程中需要远程调用的接口会很多,不会去挨个配置,还是使用注解的方式配置。
使用SpringBoot配置远程调用的接口:
Dubbo_第32张图片
主启动配置:开启Dubbo注解支持
Dubbo_第33张图片
启动服务:
请求:http://192.168.10.105:8002/order/createOrder?userId=1
image.png
总结:

  • 引用dubbo-start依赖坐标
  • 配置application配置文件,配置相关信息
  • 暴露服务使用Service注解,远程调用使用Reference注解(都是Dubbo的包)

Dubbo配置

dubbo配置文件属性加载顺序

我们最初是通过spring的配置文件进行dubbo相关信息的配置 配置项参考手册

覆盖策略

JVM---->application.properties------>dubbo.properties
Dubbo_第34张图片
Dubbo_第35张图片
从下到下是优先级顺序。
我们可以单独建一个dubbo的配置文件,将公共的配置抽取到该配置文件中:
Dubbo_第36张图片
如果启动JVM的时候指定参数,方式为:
以配置通信规则端口为例:
Dubbo_第37张图片
JVM优先级最高,如果JVM中没有,会找application的配置文件,该配置文件中也没有会去找dubbo.properties的配置文件。
后续,我们可以将公共的配置放到dubbo.properties中,额外需要改变的放到application.properties,临时需要改变的放在JVM参数中。

启动时检查

默认服务消费者启动时会去检查自己依赖的提供者是否可用,如果不可用,会抛出异常,阻止Spring初始化完成,默认check=true。
我们可以修改配置为false,关闭检查。当服务之间进行调用时,才会进行请求检查。
默认开启检查配置:
直接启动消费者,启动报错:

配置方式

单个配置

配置文件配置方式:
配置在Consumer端

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

image.png
SpringBoot注解配置方式:

    @Reference(check = false)
    UserService userService;

Dubbo_第38张图片

统一配置
    
    <dubbo:consumer check="false"/>

SpringBoot启动检查配置

#配置当前所有服务启动都不检查
dubbo.consumer.check=false

image.png

通过Spring配置文件配置

关闭某个服务的启动时检查(没有服务提供者时报错)

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

关闭所有服务的启动时检查(没有服务提供者时报错)

<dubbo:consumer check="false"/>

关闭注册中心启动时检查(注册订阅失败时报错)

<dubbo:registry check="false"/>

通过dubbo.properties配置

#配置当前所有服务启动都不检查
dubbo.consumer.check=false
dubbo.reference.com.xiu.service.UserService.check=false
dubbo.reference.check=false
dubbo.registry.check=false

超时配置

服务消费者在引用服务提供方时,执行一个方法可能需要很长时间,很长时间都没有返回,导致线程阻塞,会引发性能下降,我们可以指定超时时间,在规定时间内未返回,就立即终止。
配置方式:
Spring配置,通过timeout属性配置,默认时间单位是毫秒。
默认使用的是dubbo:consumer/中的timeout,默认是:1000ms

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

全部配置timeout

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

timeout不仅仅可以配置在全局和接口上,也可以配置在具体的某个方法上

    <dubbo:reference check="false" timeout="3000" interface="com.xiu.service.UserService" id="userService">
        <dubbo:method name="getUserAddressList" timeout="3000"/>
    dubbo:reference>

如果有多处配置了超时时间,作用优先级:
以timeout为例,显示了配置的查找顺序,其他retries,loadbalanc,actives类似:

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

其中如果服务提供方配置,通过URL经由注册中心传递给消费者。
总结:越精确,优先级越高,级别一样,消费方优先级高。
比如消费方配置在了消费者接口级别,提供者配置在了方法级,结论:方法级优先,因为方法级比接口级别优先级更高!!!
Dubbo_第39张图片
Dubbo_第40张图片
SpringBoot配置,引用超时时间:

    @Reference(check = false, timeout = 3000)
    UserService userService;

方法级别配置:

    @Reference(check = false, parameters = {
        "createOrder.timeout", "3000"})
    UserService userService;

全局配置

dubbo.consumer.timeout=3000

重试次数

一般配合timeout一起使用,是一个整数,但是,不包含第一次调用
服务之间调用时,由于网络不佳或者缓慢等原因,会出现超时的情况,我们可以通过配置重试次数来进行重试,但是,并不是所有的接口都能配置重试次数:幂等方法可以设置、非幂等方法不能设置

  • 幂等方法就是:无论执行多少次结果都是一样的,如查询操作
  • 非幂等操作:每次执行都不一样,比如数据库新增,或 update user set age=age+1,将重试设置为0次即可。

配置方式

    <dubbo:reference check="false" timeout="3000" interface="com.xiu.service.UserService" id="userService">
        <dubbo:method name="getUserAddressList" timeout="3000" retries="3"/>
    dubbo:reference>

服务提供者集群的情况下,如果调用失败,重试时会去调用其他服务提供者。

多版本

当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。
我们可以先让一部分人先用新版本,一部人仍旧使用老版本。灰度发布的概念。
可以按照以下步骤进行版本迁移:
0、在低压力时间段,先升级一半提供者为新版本
1、再将所有消费者升级为新版本
2、然后将剩下的一半提供者升级为新版本

配置方式

服务提供者

spring配置:

<dubbo:service version="1.0.0" interface="com.xiu.service.UserService" ref="userService"/>

SpringBoot注解配置

import com.alibaba.dubbo.config.annotation.Service;
@Service(version = "1.0.0")
服务消费者

Spring配置:

    <dubbo:reference version="1.0.0" check="false" timeout="3000" interface="com.xiu.service.UserService" id="userService">
    dubbo:reference>

SpringBoot配置:

@Reference(check = false, version = "1.0.0")
UserService userService;

可以配置为*,代表随机调用

@Reference(check = false, version = "*")
UserService userService;

本地存根

我们可以在真正发起远程调用实现之前进行一些验证、缓存等操作
远程引用的接口在我们的服务消费者本地也有一些存根代码。
当满足我们的要求时再去发起远程调用,否则就可以不发起远程调用。
Dubbo_第41张图片
我们需要在服务消费方实现一个远程接口的本地存根实现:
必须有一个有参构造器,参数为远程接口的代理实现,Dubbo默认会传进来。
代码:

/**
 * @author zhangzengxiu
 * @date 2023/3/19
 */
public class UserServiceStub implements UserService {

    private UserService userService;

    /**
     * Dubbo会自动创建创建本地存根对象
     * 自动传入UserService远程代理对象
     *
     * @param userService
     */
    public UserServiceStub(UserService userService) {
        this.userService = userService;
    }

    @Override
    public List<UserAddress> getUserAddressList(String userId) {
        if (!StringUtils.isEmpty(userId)) {
            return userService.getUserAddressList(userId);
        }
        return null;
    }
}

配置:
通过dubbo:servic配置,是配置在服务提供者里

    
    <dubbo:reference version="1.0.0" check="false" timeout="3000" interface="com.xiu.service.UserService" id="userService" stub="com.xiu.service.impl.UserServiceStub">
    dubbo:reference>

@Reference(check = false, version = "1.0.0",parameters = {"createOrder.timeout", "3000"},stub = "com.xiu.service.UserServiceStub")
UserService userService;

配置与SpringBoot进行整合

注解不能配置某个方法的详细配置

方式一

导入dubbo-stater,在application.properties中进行配置
@Service进行服务暴露
@Reference引用服务
以上需要在主启动类开启@EnableDubbo
也可以在配置中配置dubbo.scan.base-packages来指定包

方式二

保留原生的dubbo的配置方式,application中的配置全部都不要了
主启动类的@EnableDubbo也不再需要
通过@ImportResource(locations=“provider.xml”)来导入,也不再需要@service和Reference方式

方式三

使用API的方式
我们可以将所有的配置以配置类的形式进行配置,将每一个组件配置
举例:
配置服务名称:

<dubbo:application name="boot-user-service-provider"/>
    @Bean
    public ApplicationConfig getApplicationConfig() {
        ApplicationConfig applicationConfig = new ApplicationConfig();
        applicationConfig.setName("boot-user-service-provider");
        return applicationConfig;
    }

配置注册中心位置

<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"/>
    @Bean
    public RegistryConfig getRegistryConfig() {
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setProtocol("zookeeper");
        registryConfig.setAddress("127.0.0.1:2181");
        return registryConfig;
    }

配置通信规则

<dubbo:protocol name="dubbo" port="20880"/>
    @Bean
    public ProtocolConfig getProtocolConfig() {
        ProtocolConfig protocolConfig = new ProtocolConfig();
        protocolConfig.setName("dubbo");
        protocolConfig.setPort(20880);
        return protocolConfig;
    }

配置接口

    <dubbo:reference version="1.0.0" check="false" timeout="3000" interface="com.xiu.service.UserService" id="userService" stub="com.xiu.service.UserServiceStub">
        <dubbo:method name="getUserAddressList" timeout="3000" retries="3"/>
    dubbo:reference>

部分属性省略配置:

    @Bean
    public ServiceConfig<UserService> userServiceServiceConfig(UserService userService) {
        ServiceConfig<UserService> serviceConfig = new ServiceConfig<UserService>();
        serviceConfig.setInterface(UserService.class);
        serviceConfig.setRef(userService);
        serviceConfig.setVersion("1.0.0");
        MethodConfig methodConfig = new MethodConfig();
        methodConfig.setName("getUserAddressList");
        methodConfig.setTimeout(3000);
        List<MethodConfig> methodConfigs = new ArrayList<MethodConfig>();
        methodConfigs.add(methodConfig);
        serviceConfig.setMethods(methodConfigs);
        return serviceConfig;
    }

我们需要在主启动类上配置Dubbo的接口的扫描路径:

@DubboComponentScan(basePackages = "com.xiu")

其实EnableDubbo注解本质上还是DubboComponentScan
通过EnableDubbo也可以配置,配置scanBasePackages属性

高可用

分布式系统中,可以通过架构设计,减少系统不能提供服务的时间

zookeeper宕机

  • 监控中心当即不影响使用,只是会丢失部分采样数据
  • 注册中心集群,宕机掉其中一台,会自动切换到别的机器
  • 当注册中心全部宕机后,服务消费者和提供者仍然能与消费者通过本地缓存进行通讯

我们也可以绕过注册中心,使用dubbo直连的形式。

@Reference(url = "127.0.0.1:20880")        
UserService userService;

问:如果没有这册中心是否能完成调用?
答:可以,

  • 如果注册中心部分宕机,其他部分仍然可以对外提供服务;
  • 如果全部宕机,仍然有本地缓存可以进行通讯;
  • 也可以绕过注册中心直连

负载均衡

负载均衡

算法 特性 备注
Weighted Random LoadBalance 加权随机 默认算法,默认权重相同
RoundRobin LoadBalance 加权轮询 借鉴于 Nginx 的平滑加权轮询算法,默认权重相同,
LeastActive LoadBalance 最少活跃优先 + 加权随机 背后是能者多劳的思想
Shortest-Response LoadBalance 最短响应优先 + 加权随机 更加关注响应速度
ConsistentHash LoadBalance 一致性哈希 确定的入参,确定的提供者,适用于有状态请求

Weighted Random LoadBalance

我们可以为每台服务设置权重,访问时会根据一定的概率访问每台服务。
Dubbo_第42张图片

RoundRobin LoadBalance

访问是有顺序的,而且要考虑权重。

LeastActive LoadBalance

服务会统计上次的调用时间,会去找上次处理请求最快的服务器。

ConsistentHash LoadBalance

一致性hash,比如getId=1的去找1号机器,id=2的去找二号机器,id=3的去找3号机器等等。

默认负载均衡

Dubbo_第43张图片
Dubbo_第44张图片

配置方式

服务端
@Reference(loadbalance = "leastactive")

value来源AbstractLoadBalance实现类里的值
配置按照权重随机

@Reference(loadbalance = "random")

配置权重方式一:
暴露Dubbo接口时配置,缺点:写死了,固定无法调节

@Service(weight = 100)

方式二:通过管理控制台配置
Dubbo_第45张图片

服务降级

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

直接返回为空

当我们去发起远程调用时,返回为空,不发起远程调用,用来屏蔽不重要的服务不可用时候对调用方的影响。
管理控制台配置:消费者侧配置
Dubbo_第46张图片

当调用失败后返回为空

消费方调用服务失败后,返回为空,不抛出异常,用来容忍不重要服务不稳定时对调用方的影响。
管理控制台配置:消费者侧配置
Dubbo_第47张图片

服务容错

集群容错模式

  • Failover Cluster(默认):失败自动切换,当出现失败,重试其他服务器,通常用于读操作,但重试会带来更长延迟
  • Failfast Cluster:快速失败,只发起一次调用,失败立即报错。用于非幂等性的操作,比如新增
  • Failsafe Cluster:失败安全,出现异常时,直接忽略。通常用于写入日志等操作
  • Failback Cluster:失败自动恢复,后台记录失败请求,定时重发。用于消息通知操作
  • Forking Cluster:并行调用多个服务器,只要一个返回成功即可,用于实时性要求较高的读操作,可以通过forks来设置最大并行数,比较浪费资源。
  • Broadcast Cluster:广播调用所有提供者,逐个调用,只要一台报错,就认为本次调用失败,常用语所有通知者更新缓存或日志等。
配置方式
服务提供者
<dubbo:service cluster="failsafe"  interface="com.xiu.service.UserService" ref="userService" version="1.0.0"/>

或者

@Service(weight = 100,version = "1.0.0",cluster = "failsafe")
服务消费者
<dubbo:reference cluster="failsafe"/>

或者

@Reference(cluster = "failsafe")
Hystrix

实际开发中,是通过整合Hystrix来进行设计,Hystrix也是SpringCloud中默认的服务容错解决方案
依赖导入

        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-hystrixartifactId>
            <version>1.4.4.RELEASEversion>
        dependency>

启用:主启动类上开启

@EnableHystrix

在提供者方法上添加注解

@HystrixCommand

在消费者方法上添加注解并编写回调方法

@HystrixCommand(fallbackMethod = "回调方法名")

Dubbo_第48张图片

原理

Dubbo原理

RPC原理

Dubbo_第49张图片

调用流程

一次完整的RPC调用流程,同步调用过程如下:
1、服务消费方调用以本地调用方式调用服务
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步,封装起来,这些细节对于用户来说是透明、不可见的。

Netty

Dubbo底层两台机器要建立通信,使用netty框架实现,基于Java的NIO。
Netty是一个异步时间驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。极大的简化了TCP和UDP套接字服务器等网络编程。
Netty其实就是基于NIO的多路复用模型去做的。

BIO(阻塞式的IO)

Dubbo_第50张图片
一次请求一个线程,当线程没有处理完成时,线程是得不到释放的,这样就使得服务器不能一次处理太多请求。我们可以通过NIO的形式。

NIO(非阻塞式的IO)

Dubbo_第51张图片
原理:

  • 一个Selector选择器注册进了多个Channel通道,Selector也可以翻译为多路复用
  • 监听每一个Channel通道的状态
  • 当一个通道状态准备好了就开一个线程去处理(多路复用)

Netty基本原理

Netty其实就是基于NIO的多路复用模型去做的。
Dubbo_第52张图片

  • Netty服务器启动,绑定监听某个端口,所有发给这个端口的数据,Netty就可以收到了
  • 启动后,会初始化通道,通道初始化好后会注册到Selector(多路复用)中
  • Selector负责监听Accept事件(当我们的通道准备就绪,接下来就需要处理里面的信息)
  • Netty与客户端建立连接,来生成NIO Socket Channel,也就是与我们客户端连接的通道
  • NIO Socket Channel注册到Selector中,监听read和write事件,相当于数据通道读写准备就绪
    • 读准备就绪:请求发来的数据已经接收完毕了,可以来读了
    • 写准备就绪:可以给客户端通道写响应了
  • 处理事件,读准备就绪,写准备就绪,都会将任务抛给任务队列,任务队列将任务执行完毕
  • 监听Accept也会生产任务队列,整合队列运行完成,整个Netty就运行结束了
  • Boss线程组:用来监听主线程,监听来自20880的所有连接准备就绪事件
  • worker线程组:连接准备就绪后做的工作抛给worker来做

Dubbo原理-框架设计

  • Bussiness层:业务逻辑层,只有一个Service层,面向接口编程和实现,远程调用,也只需要调用方法,用户只需要关心到这一层就可以
  • RPC层:
    • config层:封装配置文件信息解析出来的数据,如ReferenceConfig、ServiceConfig
    • proxy代理层:帮助我们生成客户端、服务端的代理对象,进行方法调用
    • Registry层:注册中心层,注册中心服务发现与注册
    • Cluster层:路由层,帮助我们负载均衡
    • Monitor层:监控层
    • Protocol层:远程调用层,发起远程调用
  • Remoting层:远程通信层
    • exchange层:信息交换,创建一个客户端、服务端,建立管道,互联互通
    • Transport层:数据传输层,底层是Netty框架
    • Serialize层:序列化层

Dubbo原理-启动解析、加载配置信息

  • Spring的配置文件,Spring解析配置文件中的每个标签,会有一个总接口:BeanDefinitionParser
  • DubboBeanDefinitionParser :会解析每个标签

Dubbo_第53张图片
每个标签都有对应的Config
Dubbo_第54张图片
流程:
Dubbo_第55张图片

Dubbo原理-服务暴露

Dubbo_第56张图片

Dubbo原理-服务引用

Dubbo_第57张图片

Dubbo原理-服务调用

你可能感兴趣的:(dubbo,服务器,java)