Dubbo攻略

Dubbo介绍

背景

随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。

在这里插入图片描述

单一应用架构

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

垂直应用架构

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

分布式服务架构

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

流动计算架构

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

需求

在这里插入图片描述

在大规模服务化之前,应用可能只是通过 RMI 或 Hessian 等工具,简单的暴露和引用远程服务,通过配置服务的URL地址进行调用,通过 F5 等硬件进行负载均衡。

当服务越来越多时,服务 URL 配置管理变得非常困难,F5 硬件负载均衡器的单点压力也越来越大。 此时需要一个服务注册中心,动态的注册和发现服务,使服务的位置透明。并通过在消费方获取服务提供方地址列表,实现软负载均衡和 Failover,降低对 F5 硬件负载均衡器的依赖,也能减少部分成本。

当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。 这时,需要自动画出应用间的依赖关系图,以帮助架构师理清理关系。

接着,服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器? 为了解决这些问题,第一步,要将服务现在每天的调用量,响应时间,都统计出来,作为容量规划的参考指标。其次,要可以动态调整权重,在线上,将某台机器的权重一直加大,并在加大的过程中记录响应时间的变化,直到响应时间到达阈值,记录此时的访问量,再以此访问量乘以机器数反推总容量。

以上是 Dubbo 最基本的几个需求。

架构

在这里插入图片描述

节点 角色说明
Container 服务运行的容器
Provider 暴露服务的服务提供方
Registry 服务注册与发现的注册中心
Consumer 调用远程服务的服务消费方
Monitor 统计服务的调用次数和调用时间的监控中心

调用关系说明:

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

Dubbo 架构具有以下几个特点,分别是连通性、健壮性、伸缩性、以及向未来架构的升级性。

连通性

  • 注册中心负责服务地址与注册查找,相当于目录服务,服务提供方和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小。
  • 监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表表示。
  • 服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销
  • 服务消费者向注册中心获取服务器提供者地址列表,并根据负载算法直接调用提供者,同时汇报调用时间到监控中心,此时间包含网络开销
  • 注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外
  • 注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将产即推送事件通知消费者
  • 注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表
  • 注册中心和监控中心都是可选的,服务消费者可以直接连接服务提供者

健壮性

  • 监控中心宕机不影响使用,只是丢失部分采样数据
  • 数据库宕机后,注册中心仍能通过缓存提供服务列表查询,但不能注册
  • 注册中心对等集群,任意一台宕机后,将自动切换到另一台
  • 注册中心全部宕机后,服务提供者和服务消费者仍能通过本地缓存通讯
  • 服务提供者无状态,任意一台宕机后,不影响使用
  • 服务提供者全部宕机后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复

伸缩性

  • 注册中心对等集群,可动态增加机器部署实例,所有客户端将自动发现新的注册中心
  • 服务提供者无状态,可动态增加机器部署实例,注册中心将推送新的服务提供者信息给消费者

Dubbo+Spring+Zookeeper

Docker安装Zookeeper

前面文章介绍了如何在虚拟机上搭建Zookeeper集群,此节我们改用windows7下Docker安装Zookeeper。

  1. 下载zookeeper镜像
    docker pull zookeeper或docker pull zookeeper:3.4.10#前者下载最新版本,后者下载指定版本。
  2. 查看zookeeper镜像
    docker images
    在这里插入图片描述
  3. 后台方式运行zookeeper镜像,并添加端口映射
    docker run -itd -p 2181:2181 --name zookeeper zookeeper
  4. 查询容器
    docker ps -a
    在这里插入图片描述
    可选操作:docker exec -it 容器id bash (容器必须正在运行)
    进入容器系统,/conf/zoo.cfg为配置文件

    在这里插入图片描述

编写Provider和Consumer

1.创建一个maven quick-start项目:
在这里插入图片描述

  • pom.xml
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.ssm.api</groupId>
    <artifactId>spring_mvc_api</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>

    <name>spring_mvc_common Maven Webapp</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>

    <dependencies>
        <!--Dubbot相关依赖 start-->
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.18.1-GA</version>
        </dependency>
        <dependency>
            <groupId>com.101tec</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.9</version>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.10</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.5.7</version>
            <exclusions>
                <exclusion>
                    <artifactId>spring</artifactId>
                    <groupId>org.springframework</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--Dubbot相关依赖 end-->
    </dependencies>
</project>
  • 定义服务接口MailService:
package com.ssm.api.service;

/**
 * Project Name: Web_App
 * Des:
 * Created by Even on 2018/11/16
 */
public interface MailService {
    String sendMail(String from,String to,String subject,String content);
}
  1. 创建一个spring web项目,作为Provider:
    在这里插入图片描述
  • pom.xml添加spring-api的依赖
        <dependency>
            <groupId>com.ssm.api</groupId>
            <artifactId>spring_mvc_api</artifactId>
            <version>1.0</version>
        </dependency>
  • 实现服务接口:
package com.ssm.ser.service;

import com.ssm.api.service.MailService;
import org.springframework.stereotype.Service;
/**
 * Project Name: Web_App
 * Des:
 * Created by Even on 2018/11/16
 */
@Service("mailService")
public class MailServiceImpl implements MailService {
    @Override
    public String sendMail(String from, String to, String subject, String content) {
        return "send mail from " + from + " to " + to + "\n" +
                "主题:" + subject + "\n" + "内容:" + content;
    }
}
  • 配置Spring声明暴露服务(dubbo-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">
    <!-- 此为服务接口的别名,要求通俗易懂 -->
    <dubbo:application name="ssmSer-C"/>
    <!-- zookeeper的服务器host:post,在window7安装docker,实际上docker是运行在一个虚拟机上的,因此,ip是虚拟机的ip而非window本机的ip -->
    <dubbo:registry address="zookeeper://192.168.99.100:2181"/>
    <!-- 在20880端口上发布dubbo协议的服务,此端口可自定义 -->
    <dubbo:protocol name="dubbo" port="20880"/>
    <!-- 服务接口的实现,类中加了@Service注解后,这里实际不需要再添加 -->
    <bean id="userService" class="com.ssm.ser.service.MailServiceImpl"/>
    <!-- 服务接口 -->
    <dubbo:service interface="com.ssm.api.service.MailService" ref="mailService"/>
</beans>
  1. 创建一个spring项目,作为Consumer:
  • pom.xml添加spring-api的依赖
        <dependency>
            <groupId>com.ssm.api</groupId>
            <artifactId>spring_mvc_api</artifactId>
            <version>1.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">
    <!--Consumer别名-->
    <dubbo:application name="ssmWeb-C"/>
    <!--zookeeper服务器-->
    <dubbo:registry address="zookeeper://192.168.99.100:2181"/>
    <!--开启注解,可以使用@Reference来调用服务-->
    <dubbo:annotation package="com.ssm.dubbo"/>
    <!--<dubbo:reference interface="com.ssm.api.service.MailService" timeout="5000"/>-->
    <dubbo:consumer timeout="5000"/>
</beans>
  • 调用服务:
package com.ssm.dubbo;

import com.alibaba.dubbo.config.annotation.Reference;
import com.ssm.api.service.MailService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * Project Name: Web_App
 * Des:
 * Created by Even on 2018/11/16
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-context-dubbo.xml"})
public class MailServiceTest {
    @Reference
    private MailService mailService;

    @Test
    public void sendMailTest() throws Exception {
        System.out.println(mailService.sendMail("[email protected]", "[email protected]", "dubbo 测试发送邮件", "我是要成为海贼王的男人"));
    }
}
  1. 查看zookeeper服务器目录,可以看到服务接口生成的znode,其中consumers记录了服务消费者的信息,configurations记录了配置信息,routers记录了路由信息,providers记录了服务提供者的信息。
    在这里插入图片描述

服务治理

Dubbo官网提供了一个用于服务治理的web项目,新版的参照官网提供的代码:https://github.com/apache/incubator-dubbo-ops
在这里插入图片描述
其中,也可以在dubbo-admin-backend/src/main/resources/dubbo-admin.xml指定注册中心地址:
<dubbo:registry address=“zookeeper://192.168.99.100:2181”/>
在这里插入图片描述

新版本的服务治理项目功能不全,可以暂时使用旧版的。
在这里插入图片描述

  1. 下载后解压缩,修改配置文件dubbo-admin\src\main\webapp\WEB-INF\dubbo.properties,指定注册中心。
  2. 在dubbo-admin目录运行:
#-Dmaven.skip.test=true是为了跳过测试
mvn  package -Dmaven.skip.test=true
  1. 把dubbo-admin\target\dubbo-admin-2.6.0.war复制到tomcat的webapp下,启动tomcat,在启动前,需要启动注册中心。
  2. 打开项目,用户名密码默认root:在这里插入图片描述

启动Provider后,可以在服务治理中看到服务:
在这里插入图片描述

Docker安装Zookeeper集群

 #1. 创建一个网络,以便集群间服务器通信使用。
docker network create zoo
 #2. 创建一个docker-compose.yml
 #主机名
zoo1:
   image: zookeeper:latest
   restart: always
   net: zoo
#容器名
   container_name: zoo1
   ports:
      - "2184:2181"
   environment:
      ZOO_MY_ID: 1
      ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888
zoo2:
   image: zookeeper:latest
   restart: always
   net: zoo
   container_name: zoo2
   ports:
      - "2185:2181"
   environment:
      ZOO_MY_ID: 2
      ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888
zoo3:
   image: zookeeper:latest
   restart: always
   net: zoo
   container_name: zoo3
   ports:
      - "2186:2181"
   environment:
      ZOO_MY_ID: 3
      ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888

3. 在docker-compose.yml文件的当前目录下运行:
docker-compose up -d
4. 查看是否启动成功:
docker exec -it 容器id bash
zkServer.sh status

在这里插入图片描述
修改provider和consumer配置:
<dubbo:registry protocol=“zookeeper” address==“zoo1_host:2181zoo2_host:2182,zoo3_host:2183”/>

服务治理项目修改配置文件:
<dubbo:registry address=“zookeeper://192.168.99.100:2181?backup=192.168.99.100:2182,192.168.99.100:2183”/>

Dubbo 集群容错

在Dubbo源码中,把集群容错分成了四个部分,分别为服务目录Directory、服务路由Router、集群Cluster和负载均衡LoadBalance。

服务目录

  1. 介绍
    服务目录用于存储一些和服务提供者相关的信息,以便服务消费者可以获取到服务提供者的信息。通过这些信息,服务消费者可以通过Netty等客户端进行远程调用。

服务目录的功能与注册中心的功能有点相似,只是注册中心是搭建在另一台服务器上的用于统一管理服务提供者信息的地方,而服务目录则是服务消费者方自己维护的。

前面提及到,当服务提供者注册后,注册中心会通知服务消费者更新服务列表信息,服务消费者会把这些配置信息一对一生成一个Invoker对象,此对象是一个具有远程调用功能的对象,也即是服务目录最终持有的对象。因为,我们可以把服务目录理解为Invoker集合,且该集合会随着注册中心的变化而变化。

  1. 服务目录内置的两个实现:StaticDirectory和RegistryDirectory,它们均是AbstractDirectory的子类。AbstractDirectory实现了Directory接口,该接口包含了一个重要的方法定义,即list(Invocation),用于获取Invoker列表。

在这里插入图片描述
AbstractDirectory.list(Invocation):

public List<Invoker<T>> list(Invocation invocation) throws RpcException {
        if (this.destroyed) {
            throw new RpcException("Directory already destroyed .url: " + this.getUrl());
        } else {
            List<Invoker<T>> invokers = this.doList(invocation);
            List<Router> localRouters = this.routers;
            /*迭代路由列表*/
            if (localRouters != null && localRouters.size() > 0) {
                Iterator i$ = localRouters.iterator();

                while(i$.hasNext()) {
                    Router router = (Router)i$.next();

                    try {
                    /*服务路由,是为了过滤出可用invoker,runtime为true,代表每次调用服务前都需要执行服务路由*/
                        if (router.getUrl() == null || router.getUrl().getParameter("runtime", true)) {
                            invokers = router.route(invokers, this.getConsumerUrl(), invocation);
                        }
                    } catch (Throwable var7) {
                        logger.error("Failed to execute router: " + this.getUrl() + ", cause: " + var7.getMessage(), var7);
                    }
                }
            }

            return invokers;
        }
    }
    /*由子类实现的模块方法*/
    protected abstract List<Invoker<T>> doList(Invocation var1) throws RpcException;

StaticDirectory即存储静态的Invoker的List,该类较简单,doList(Invoaction)直接返回Invoker列表,当不使用注册中心时,服务目录就是StaticDirectory实现的。:

    @Override
    protected List<Invoker<T>> doList(Invocation invocation) throws RpcException {
        // 列举 Inovker,也就是直接返回 invokers 成员变量
        return invokers;
    }

RegistryDirectory是一种动态服务目录,实现了NotifyListener接口,用于监听注册中心服务配置变化,当注册中心服务配置发生变化后,会收到通知,RegistryDirectory根据变更信息刷新Invoker列表,对应的doList(Invocation):

public List<Invoker<T>> doList(Invocation invocation) {
        if (this.forbidden) {
            throw new RpcException(4, "No provider available from registry " + this.getUrl().getAddress() + " for service " + this.getConsumerUrl().getServiceKey() + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", may be providers disabled or not registered ?");
        } else {
            List<Invoker<T>> invokers = null;
            Map<String, List<Invoker<T>>> localMethodInvokerMap = this.methodInvokerMap;
            if (localMethodInvokerMap != null && localMethodInvokerMap.size() > 0) {
                /*获取方法名*/
                String methodName = RpcUtils.getMethodName(invocation);
                /*获取参数列表*/
                Object[] args = RpcUtils.getArguments(invocation);
                
                if (args != null && args.length > 0 && args[0] != null && (args[0] instanceof String || args[0].getClass().isEnum())) {
                    /*根据方法名和参数列表第一个获取Invoker列表*/
                    invokers = (List)localMethodInvokerMap.get(methodName + "." + args[0]);
                }

                if (invokers == null) {
                    /*根据方法名获取Invoker*/
                    invokers = (List)localMethodInvokerMap.get(methodName);
                }

                if (invokers == null) {
                    /*根据星号获取Invoker列表*/
                    invokers = (List)localMethodInvokerMap.get("*");
                }

                if (invokers == null) {
                    /* 上面方法都获取不到时,遍历所有的inovker列表,获取最后一个Invoker列表*/
                    Iterator<List<Invoker<T>>> iterator = localMethodInvokerMap.values().iterator();
                    if (iterator.hasNext()) {
                        invokers = (List)iterator.next();
                    }
                }
            }
            return (List)(invokers == null ? new ArrayList(0) : invokers);
        }
    }

其中methodInvokerMap 变量的是父类的变量,此处doList()对其进行了读操作。当接收到注册中心服务变更通知时,会调用notify(List)方法,该方法是对methodInvokerMap 变量的写操作。

public synchronized void notify(List<URL> urls) {
        List<URL> invokerUrls = new ArrayList();
        List<URL> routerUrls = new ArrayList();
        List<URL> configuratorUrls = new ArrayList();
        Iterator i$ = urls.iterator();

        while(true) {
            while(true) {
                while(i$.hasNext()) {
                    URL url = (URL)i$.next();
                    String protocol = url.getProtocol();
                    String category = url.getParameter("category", "providers");
                    if (!"routers".equals(category) && !"route".equals(protocol)) {
                        if (!"configurators".equals(category) && !"override".equals(protocol)) {
                            if ("providers".equals(category)) {
                                invokerUrls.add(url);
                            } else {
                                logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " + this.getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost());
                            }
                        } else {
                            configuratorUrls.add(url);
                        }
                    } else {
                        routerUrls.add(url);
                    }
                }

                if (configuratorUrls != null && configuratorUrls.size() > 0) {
                    /*把url列表转成Configurtar列表*/
                    this.configurators = toConfigurators(configuratorUrls);
                }

                List localConfigurators;
                if (routerUrls != null && routerUrls.size() > 0) {
                    localConfigurators = this.toRouters(routerUrls);
                    if (localConfigurators != null) {
                       /*把url列表转成Router列表*/
                        this.setRouters(localConfigurators);
                    }
                }

                localConfigurators = this.configurators;
                this.overrideDirectoryUrl = this.directoryUrl;
                Configurator configurator;
                if (localConfigurators != null && localConfigurators.size() > 0) {
                    for(Iterator i$ = localConfigurators.iterator(); i$.hasNext(); this.overrideDirectoryUrl = configurator.configure(this.overrideDirectoryUrl)) {
                        configurator = (Configurator)i$.next();
                    }
                }
				/*刷新Invoker列表*/
                this.refreshInvoker(invokerUrls);
                return;
            }
        }
    }

notify方法,根据category来把对应的url列表转成Router和Configurator列表,最后再刷新Invoker列表。

服务路由

服务目录在刷新 Invoker 列表的过程中,会通过 Router 进行服务路由,筛选出符合路由规则的服务提供者。服务路由包含一条路由规则,路由规则决定了服务消费者的调用目标,即规定了服务消费者可调用哪些服务提供者。Dubbo 目前提供了三种服务路由实现,分别为条件路由 ConditionRouter、脚本路由 ScriptRouter 和标签路由 TagRouter。其中条件路由是我们最常使用的。
条件路由规则的格式:
[服务消费者匹配条件] => [服务提供者匹配条件]

如:host = 10.20.153.10 => host = 10.20.153.11,表示IP为10.20.153.10 的服务消费者只可调用IP为10.20.153.11的服务提供者。

注意:路由规则写入到注册中心是由监控中心或治理中心的页面完成的。
在这里插入图片描述

代码写入路由规则:

RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181"));
registry.register(URL.valueOf("condition://0.0.0.0/com.foo.BarService?category=routers&dynamic=false&rule=" + URL.encode("host = 10.20.153.10 => host = 10.20.153.11")));

其中:

  • condition:// 表示路由规则的类型,支持条件路由规则和脚本路由规则,可扩展,必填。
  • 0.0.0.0 表示对所有 IP 地址生效,如果只想对某个 IP 的生效,请填入具体 IP,必填。
  • com.foo.BarService 表示只对指定服务生效,必填。
  • group=foo 对指定服务的指定group生效,不填表示对未配置group的指定服务生效
  • version=1.0对指定服务的指定version生效,不填表示对未配置version的指定服务生效
  • category=routers 表示该数据为动态配置类型,必填。
  • dynamic=false 表示该数据为持久数据,当注册方退出时,数据依然保存在注册中心,必填。
  • enabled=true 覆盖规则是否生效,可不填,缺省生效。
  • force=false 当路由结果为空时,是否强制执行,如果不强制执行,路由结果为空的路由规则将自动失效,可不- 填,缺省为 false。
  • runtime=false 是否在每次调用时执行路由规则,否则只在提供者地址列表变更时预先执行并缓存结果,调用时直- 接从缓存中获取路由结果。如果用了参数路由,必须设为 true,需要注意设置会影响调用的性能,可不填,缺省为 false。
  • priority=1 路由规则的优先级,用于排序,优先级越大越靠前执行,可不填,缺省为 0。
  • rule=URL.encode(“host = 10.20.153.10 => host = 10.20.153.11”) 表示路由规则的内容,必填。

集群

为了避免单点故障,一个应用一般都会部署在多台服务器上。当服务提供者数量大于1时,服务消费者需要决定选择哪一个服务提供者进行调用。另外,如果调用服务失败时,也需要一些处理措施,如重试,异常抛出等。为了解决这些问题,Dubbo定义了集群接口Cluster以及Cluster Invoker,集群Cluster把多个服务提供者合并为一个Cluster Invoker,并将这个Invoker暴露给服务消费者。服务消费者通过Cluster Invoker来调用服务,并把所有的问题交给集群模块进行处理。

集群容错的组件包括Cluster、Cluster Invoker、Directory、Router和LoadBalance等。

在这里插入图片描述

上图中把集群工作为成了两个阶段:

  1. 第一个阶段是消费者初始化期间,集群Cluster实现类调用merge方法创建Cluster Invoker实例。
  2. 第二个阶段Cluster Invoker会调用Directory的list方法来获取Invoker列表,其中Registry和Static是Directory的两个实现类。
  3. 获取Invoker列表后,会再调用Router的route方法进行路由。该方法调用代码在AbstractDirectory的list()方法中。
  4. Cluster Invoker从Directory得到经路由过滤后的Invoker列表,会通过LoadBalance从Invoker列表中选择一个Invoker,最后Cluster Invoker把参数传给LoadBalance选择出的Inovker实例的invoker方法,进行真正的远程调用。

Dubbo主要的几种容错方式有:

在这里插入图片描述

通过<dubbo:service cluster=“failfast” />(服务提供方)或<dubbo:reference cluster=“failfast” />(服务消费方)来指定

负载均衡

LoadBalance即负载均衡,职责是将网络请求,或者其他形式的负载“均摊”到不同的机器上。避免集群中部分服务器压力过大。LoadBalance发生在服务路由之后,选择Invoker时。Dubbo 需要对服务消费者的调用请求进行分配,避免少数服务提供者负载过大。服务提供者负载过大,会导致部分请求超时。因此将负载均衡到每个服务提供者上,是非常必要的。

Dubbo 提供了4种负载均衡实现,分别是基于权重随机算法的 RandomLoadBalance、基于最少活跃调用数算法的 LeastActiveLoadBalance、基于 hash 一致性的 ConsistentHashLoadBalance,以及基于加权轮询算法的 RoundRobinLoadBalance。这四种方式在此不作解释,需要了解的可以查看官方文档

样例:

#服务端服务级别
<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>

参考文献

猜你喜欢

转载自blog.csdn.net/weixin_37581297/article/details/86065403