JavaEE 企业级分布式高级架构师(九)Dubbo(2.7)学习笔记(1)

Dubbo概述

分布式技术图谱

在这里插入图片描述

系统架构的发展

单体系统架构

在这里插入图片描述

  • 当站点功能与流量都很小时,只需一个应用,将所有功能都集中在一个工程中,并部署在一台机器上,以减小部署节点和成本。例如,将用户模块、订单模块、支付模块等都做在一个工程中,以一个应用的形式部署在一台服务器上。

集群架构

在这里插入图片描述

  • 当站点流量增加而单体架构无法应用其访问量时,可通过搭建集群增加主机的方式提升系统的性能。这种方式为水平扩展

分布式架构

在这里插入图片描述

  • 当访问量逐渐增大,集群架构的水平扩展,其所带来的效率提升越来越小。此时可以将项目拆分成多个功能相对独立的子工程提升效率。例如用户工程、订单工程、支付工程等。这种称为垂直扩展

微服务架构

在这里插入图片描述

  • 当子工程越来越多时,发现它们可能同时都拥有某功能相同或相似的模块,于是将这些在整个项目中冗余的功能模块抽取出来作为单独的工程,这些工程就是专门为那些调用它们的工程服务的。那么这些抽取出来的功能称为微服务,微服务应用称为服务提供者,而调用微服务的应用就称为服务消费者。

流动计算架构

在这里插入图片描述

  • 随着功能的扩张,微服务就需要越来越多;随着 PV 的增涨,消费者工程就需要越来越多;随着消费者的扩张,为其提供服务的提供者服务器就需要越来越多,且每种提供者都要求创建为集群。这样的话,消费者对于提供者的访问就不能再采用直连方式进行了,此时就需要服务注册中心了。提供者将服务注册到注册中心,而消费者通过注册中心进行消费,消费者无需再与提供者绑定了。提供者的宕机,对消费者不会产生直接的影响。
  • 随着业务的增多,在一些特殊时段(例如双11)就会出现服务资源浪费的问题:有些服务的 QPS(Query Per Second) 很低,但其还占用着很多系统资源,而有些 QPS 很高,已经出现了资源紧张,用户体验骤降的情况。此时就需要服务治理中心了。让一些不重要的服务暂时性降级,或为其分配较低的权重等,对整个系统资源进行统一调配。
  • 这里的资源调配分为两种:预调配与实时调调配。
    • 预调配:根据系统架构师的“系统容量预估”所进行的调配,是一种经验,是一种预处理,就像每年双11期间的 PV(Page View) 与 UV(Unique Visitor) 都会很高,就需要提前对各服务性能进行调配。
    • 实时调配:根据服务监控中心所提供的基于访问压力的实时系统容量评估数据,对各服务性能进行实时调配的方案。

小插曲-架构师的基本素养

常用术语

系统容量与系统容量预估
  • 系统容量指系统所能承受的最大访问量,而系统容量预估则是在峰值流量到达之前系统架构师所给出的若干技术指标值。常用的技术指标值有:QPS、PV、UV、并发量、带宽、CPU使用率、内存硬盘占用率等。系统容量预估是架构师必备的技能之一。
QPS
  • QPS:Query Per Second,每秒查询量。在分布式系统中 QPS 的定义是:单个进程每秒请求服务器的成功次数。QPS一般可以通过压力测试工具测得,例如LoadRunner、Apache JMeter、NeoLoad、http_load等。
  • QPS = 总请求数 / 进程总数 / 请求时间 = 总请求数 / (进程总数*请求时间)
PV
  • PV:Page View,页面访问量。指一定时间范围内,打开或刷新页面的次数,一般以 24 小时计算。
UV
  • UV:Unique Visitor,独立访客数量。指一定时间范围内,站点访问所来自的 IP 数量。同一IP多次访问站点只计算一次,一般以 24 小时计算。

系统容量预估计算

带宽计算
  • 平均带宽的计算公式:
平均带宽 = 总流量数(bit)/产生这些流量的时长(秒)= (PV * 页面平均大小 * 8) / 统计时间(秒)
说明:公式中的 8 指的是将Byte转换为bit,即8b/B,因为带宽的单位是bps(比特率),即bit per second,每秒二进制位数,而容量单位一般使用Byte。
  • 假设某站点的日均 PV 是 10w,页面的平均大小为 0.4M,那么其平均带宽需求是:平均带宽 = (10w * 0.4M * 8) / (60*60*24) = 3.7Mbps
  • 以上计算的仅仅是平均带宽,我们在进行容量预估时需要的时峰值带宽,即必须要保证站点在峰值时能够正常运转。假设,峰值流量是平均流量的 5 倍,这个 5 倍称为峰值因子。安照这个计算,实际需要的带宽大约在 3.7Mbps*5 = 18.5Mbps。
  • 峰值带宽 = 平均带宽 * 峰值因子
并发量计算
  • 并发量,也称为并发连接数,一般是指单台服务器每秒处理的连接数。平均并发连接数的计算公式是:
平均并发连接数 = (站点PV * 页面平均衍生连接数) / (统计时间 * web服务器数量)
说明:页面平均衍生连接数指的是,一个页面请求所产生的 http 连接数量,如对静态资源的 css、js、images等的请求数量。这个值需要根据实际情况而定。
  • 例如,一个由 5 台 web 主机构成的集群,其日均 PV 50w,每个页面平均 30 个衍生连接,则其平均并发连接数为:平均并发量 = (50w * 30) / (60*60*24*5) = 35;若峰值因子为6,则峰值并发量为:峰值并发量 = 平均并发量 * 峰值因子 = 35 * 6 = 210
服务器预估量
  • 根据往年同期获得的日均 PV、并发量、页面衍生连接数,及公司业务扩展所带来的流量增涨率,就可以计算出服务器预估值。
  • 注意,今年的页面衍生连接数与往年的可能不一样。
服务器预估值 = 站点每秒处理的总连接数 / 单机并发连接数 = (PV * 页面衍生连接数 * (1 + 增涨率)) / 统计时间 / 单机并发连接数
注意:统计时间,即PV的统计时间,一般为一天。

Dubbo简介

官网简介

  • Dubbo官网为:http://dubbo.apache.org。该官网是 Dubbo 正式进入 Apache 开源孵化器后改的。Dubbo原官网为:http://dubbo.io。
  • Dubbo官网已做过了中英文国际化,用户可以在中英文间任意切换。

在这里插入图片描述

什么是RPC?

  • RPC(Remote Procedure Call Protocol)——远程过程调用协议,是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型(OSI七层网络模型,OSI:Open System Interconnection,开放系统互联)中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
  • RPC采用客户机/服务器模式(即C/S模式)。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。

Dubbo重要时间点

Dubbo发展过程中的重要时间点:

  • 2011年开源,之后就迅速成为了国内该开源项目的佼佼者。2011年时,优秀的、可在生成环境使用的RPC框架很少,Dubbo的出现迅速给人眼前一亮的感觉,迅速受到了开发者的亲睐。
  • 2012年10月之后,就基本停止了重要升级,改为阶段性维护。
  • 2014年10月30日发布2.4.11版本后,Dubbo停止更新。
  • 2017年10月云栖大会上,阿里宣布Dubbo被列入集团重点维护开源项目,这也就意味着Dubbo起死回生,开始重新进入快车道。
  • 2018年2月15日,大年三十,进过一系列紧张的投票,宣布 Dubbo 正式进入 Apache 孵化器。

Dubbo入门篇

Dubbo演示项目框架搭建

  • 源码 github 地址:https://github.com/shouwangyw/dubbo
  • 项目工程名:dubbo-example-parent
  • Dubbo版本号与zk客户端:Dubbo 在 2.6.0 及其以前版本时,默认使用的客户端为 ZkClient。2.6.1 版本,将默认客户端由 ZkClient 修改为 Curator。至于 Curator 的版本,与 Dubbo 及所要连接的 Zookeeper 的版本有关。目前其支持的版本为 2.x.x 版本,最高版本为 2.13.0。
  • Dubbo与Spring的版本号:Dubbo 的使用是基于 Spring 环境下的,即 Dubbo 是依赖于 Spring 框架的。Dubbo2.7.0依赖的 Spring 是4.3.16,所以在Dubbo的开发过程中最好使用与该Spring版本相同的 Spring,这样可以避免可能的版本冲突问题。
<!-- dubbo依赖 -->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo</artifactId>
    <version>2.7.0</version>
</dependency>

第一个Dubbo程序(直连式)

创建业务接口模块 00-api

  • 业务接口名即服务名称。无论是服务提供者向服务注册中心注册服务,还是服务消费者从注册中心索取服务,都是通过接口名称进行注册与查找的。即,提供者与消费者都依赖于业务接口。所以,一般情况下,会将业务接口专门定义为一个工程,让提供者与消费者依赖。
/**
 * 业务接口
 */
public interface SomeService {
    String hello(String name);
}

创建提供者模块 01-provider

  • 创建 01-provider 的 Maven 工程,在 pom.xml 中引入业务接口依赖
<dependency>
  <groupId>com.yw.dubbo.example</groupId>
  <artifactId>00-api</artifactId>
  <version>1.0</version>
</dependency>
  • 业务接口实现类:
public class SomeServiceImpl implements SomeService {
    @Override
    public String hello(String name) {
        System.out.println(name + ",我是提供者");
        return "Hello Dubbo World! " + name;
    }
}
  • spring-provider.xml 配置文件
└── resources
    ├── META-INF
    │   └── spring
    │       └── spring-provider.xml  // 启动方式二: Dubbo Main方式启动
    └── spring-provider.xml // 启动方式一: 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://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">
  <!-- 指定当前工程在Monitor中显示的名称,一般与工程名相同 -->
  <dubbo:application name="01-provider" />
  <!-- 指定服务注册中心:不指定注册中心 -->
  <dubbo:registry address="N/A" />
  <!-- 注册服务执行对象 -->
  <bean id="someService" class="com.yw.dubbo.example.provider.SomeServiceImpl" />
  <!-- 服务暴露 -->
  <dubbo:service interface="com.yw.dubbo.example.service.SomeService" ref="someService" />
</beans>
  • 提供者启动类:
public class ProviderRun {
    // 启动方式一:
//    public static void main(String[] args) throws Exception {
//        // 创建Spring容器
//        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring-provider.xml");
//        // 启动Spring容器
//        ac.start();
//        // 使主线程阻塞
//        System.in.read();
//    }
    // 启动方式二:要求Spring配置文件必须要放到类路径下的 META-INF/spring 目录中
    public static void main(String[] args) {
        Main.main(args);
    }
}

创建消费模块 01-consumer

  • spring-consumer.xml 配置文件:
<!-- 指定当前工程在Monitor中显示的名称,一般与工程名相同 -->
<dubbo:application name="01-consumer"/>
<!-- 指定服务注册中心:不指定注册中心 -->
<dubbo:registry address="N/A"/>
<!-- 订阅服务:采用直连式连接消费者 -->
<dubbo:reference id="someService" interface="com.yw.dubbo.example.service.SomeService" url="dubbo://localhost:20880"/>
  • 消费者启动类:
public class ConsumerRun {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring-consumer.xml");
        SomeService service = (SomeService) ac.getBean("someService");
        String hello = service.hello("China");
        System.out.println(hello);
    }
}
  • 分别启动运行 01-provider 和 01-consumer,查看结果

Zookeeper注册中心

  • 在生产环境下使用最多的注册中心为Zookeeper,当然Redis也可以做注册中心。下面就来学习Zookeeper作为注册中心的用法。

创建提供者02-provider-zk

  • 创建工程:复制前面的提供者工程01-provider,并更名为02-provider-zk。
  • 修改父工程 pom.xml文件,并在其中添加 Zookeeper 客户端依赖 curator。
<!-- zk客户端依赖:curator -->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>2.13.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>2.13.0</version>
</dependency>
  • 修改 spring 配置文件:指定服务注册中心地址
<!-- 指定服务注册中心:zk单机 -->
<dubbo:registry address="zookeeper://192.168.254.120:2181" />
<!--  <dubbo:registry protocol="zookeeper" address="192.168.254.120:2181" />  -->

<!-- 指定服务注册中心:zk集群 -->
<!--  <dubbo:registry address="zookeeper://192.168.254.128:2181?backup=192.168.254.130:2181,192.168.254.132:2181,192.168.254.129:2181"/> -->
<!--  <dubbo:registry protocol="zookeeper" address="192.168.254.128:2181,192.168.254.130:2181,192.168.254.132:2181,192.168.254.129:2181"/> -->

创建消费者02-consumer-zk

  • 创建工程:复制前面的提供者工程01-consumer,并更名为02-consumer-zk。
  • 修改 spring 配置文件:
<!-- 指定服务注册中心:不指定注册中心 -->
<dubbo:registry address="zookeeper://192.168.254.120:2181"/>
<!-- 订阅服务 -->
<dubbo:reference id="someService" check="false" 
                 interface="com.yw.dubbo.example.service.SomeService" />

添加日志文件

  • 通过前面的运行可知,无论是提供者还是消费者,控制台给出的提示信息都太少,若想看到更多的信息,可以在提供者与消费者工程的类路径 src/main/resources 下添加日志文件。可以添加 log4j.xml,即使用 log4j2 日志技术;也可以添加 log4j.properties,即使用 log4j 日志技术。我们这里添加 log4j.properties 文件。
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%-5p] %m%n

log4j.rootLogger=info,console
  • 提供者添加日志文件:运行后可以看到如下日志输出。其中最为重要的的是 provider://xxx,这里显示的就是当前工程所提供的能够被订阅的服务描述,即服务元数据信息。另外,我们还可以看到当前应用于与 **qos-server(Quality of Service服务器,即Dubbo的管控平台)**进行通信的端口号为 22222

在这里插入图片描述

  • 消费者添加日志文件:运行后在控制台的日志输出中可以看到报错。其报错内容是,消费者连接 qos-server 的端口号被占用了。其与 qos-server 通信的端口号默认也为 22222,已经被提供者给占用了。当然,原因主要是由于消费者与提供者都在同一主机,若分别存在于不同的主机也就不会报错了。

在这里插入图片描述

  • 所以解决方案就是为消费者修改与 qos-server 通信的端口号。有两种修改方式,方式一是在 src/main/resources 中新建一个 dubbo.properties 文件,文件内容仅需加入一行:
dubbo.application.qos.port=3333
  • 方式二就是直接修改 spring-consumer.xml 文件:
<dubbo:application name="02-consumer-zk">
	<!-- 如果 dubbo.properties 中配置了,则不会使用这里配置的 -->
    <dubbo:parameter key="qos.port" value="4444"/>
</dubbo:application>

将Dubbo应用到web工程

  • 前面所有提供者与消费者均是 Java 工程,而在生产环境中,它们都应是 web 工程,Dubbo 如何应用于 Web 工程呢?

创建提供者 03-provider-web

  • 创建工程:复制 02-provider-zk 工程,然后在此基础上修改,packaging方式设置为 war。
  • 导入依赖:在父工程添加 springmvc、servlet与jsp依赖
<!-- SpringMVC相关依赖 -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>${spring-version}</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>${spring-version}</version>
</dependency>
<!-- Servlet依赖 -->
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>3.1.0</version>
</dependency>
<!-- JSP依赖 -->
<dependency>
  <groupId>javax.servlet.jsp</groupId>
  <artifactId>javax.servlet.jsp-api</artifactId>
  <version>2.2.1</version>
</dependency>
  • 定义web.xml:webapp/WEB-INF 目录下
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
  <!-- 注册Spring配置文件 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring-*.xml</param-value>
  </context-param>

  <!-- 注册ServletContext监听器 -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
</web-app>

创建消费者 03-consumer-web

  • 创建工程:复制02-consumer-zk 工程,然后在此基础上修改,依赖与提供者工程中的依赖相同
  • 定义处理器:
@Controller
public class SomeController {
    @Autowired
    private SomeService service;

    @RequestMapping("/some.do")
    public String someHandle(){
        String result = service.hello("China");
        System.out.println("消费者端接收到 = " +  result);
        return "/welcome.jsp";
    }
}
  • welcome.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h2>Welcome you!</h2>
</body>
</html>
  • 定义web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
  <!-- 对于2.6.4版本,其Spring配置文件必须指定从<context-param>中加载 -->
  <!--<context-param>-->
  <!--<param-name>contextConfigLocation</param-name>-->
  <!--<param-value>classpath:spring-*.xml</param-value>-->
  <!--</context-param>-->

  <!-- 字符编码过滤器 -->
  <filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>utf-8</param-value>
    </init-param>
    <init-param>
      <param-name>forceEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <!-- 注册中央调度器 -->
  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-*.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <!-- 不能写/*,不建议写/,建议扩展名方式 -->
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>
  • 修改spring-consumer.xml:注册处理器
<beans ...略
  <!-- 注册处理器 -->
  <mvc:component-scan base-package="com.yw.dubbo.example" />
</beans>

部署运行

  • 修改Tomcat默认端口号:由于Dubbo管控台端口为8080,所以这里将Tomcat默认端口号修改为8088。

在这里插入图片描述

  • 启动运行Tomcat进行测试:接口 http://localhost:8088/consumer/some.do

在这里插入图片描述

Dubbo管理控制台

  • 2019年初,官方发布了 Dubbo 管控台 0.1 版本。结构上采取了前后端分离的方式,前端使用 Vue 和 Vuetify 分别作为Javascript框架和UI框架,后端采用Spring Boot框架。

下载

配置

  • 在下载的zip文件的解压目录的 dubbo-admin-server\src\main\resources 下,修改配置文件 application.properties,主要就是修改注册中心、配置中心与元数据中的 zk 地址。
# centers in dubbo2.7
admin.registry.address=zookeeper://192.168.254.120:2181
admin.config-center=zookeeper://192.168.254.120:2181
admin.metadata-report.address=zookeeper://192.168.254.120:2181
  • 这是一个springboot工程,默认端口号为8080,若要修改端口号,则在配置文件中增加形如 server.port=8888 的配置。

打包

  • 在命令行窗口中进入到解压目录根目录,执行打包命令:mvn clean package,当看到以下提示时表示打包成功:

在这里插入图片描述

  • 打包结束后,进入到解压目录的 dubbo-admin-distribution 目录下的target目录。目录下有个 dubbo-admin-0.2.0.jar 文件。该jar包文件即为 Dubbo 管理控制台的运行文件,可以将其放到任意目录下运行。

运行

  • 先启动 zk,再启动管控台:
java -jar dubbo-admin-0.2.0.jar -d
  • 访问:在浏览器地址栏中输入 http://localhost:8080,即可看到 Dubbo 管理控制台界面。

在这里插入图片描述

关闭服务检查

问题复现

  • 修改工程 02-consumer-zk的启动类ConsumerRun,将对消费者调用提供者的服务方法注释掉,使消费者不调用该方法。

在这里插入图片描述

  • 运行测试:会发生报错,提示服务状态不可用,没有可用的提供者。

在这里插入图片描述

  • 错误原因是检查 SomeService 的状态失败,可以通过如下修改防止报错:再运行消费者工程就不会报错了。

在这里插入图片描述

分析

  • 默认情况下,若服务消费者先于服务提供者启动,则消费者会报错。因为默认情况下,消费者会在启动时检查其要消费的服务提供者是否已经注册,若未注册则抛出异常。可以在消费者端的spring配置文件中添加 **check=“fasle”**属性,关闭服务检查功能。

在这里插入图片描述

  • 只要注意启动顺序,该属性看似可以不使用。但在循环消费场景下是必须要使用的。即A消费B服务,B消费C服务,而C消费A服务。这是典型的循环消费。在该场景下必须至少有一方关闭服务检查功能,否则将无法启动任何一方。

多版本控制

  • 对于整个系统的升级,为了保证系统升级的稳定性与安全性,一般并不会让所有消费者一下全部都改为调用新的实现类,而是有个“灰度发布(又称为金丝雀发布)”过程,即有个新老交替的过程。即在低压力时段,让一部分消费者先调用新的提供者实现类,其余的仍然调用老的实现类,在新的实现类运行没有问题的情况下,逐步让所有消费者全部调用成新的实现类。而多版本控制就是实现灰度发布的

创建提供者 04-provider-version

  • 创建工程:复制前面的提供者工程 02-provider-zk,并更名为 04-provider-version。
  • 定义两个接口实现类:删除原来的 SomeServiceImpl类,并新建两个实现类:
public class OldSomeServiceImpl implements SomeService {
    @Override
    public String hello(String name) {
        System.out.println("执行【老】的提供者OldSomeServiceImpl的hello()");
        return "OldSomeServiceImpl";
    }
}
public class NewSomeServiceImpl implements SomeService {
    @Override
    public String hello(String name) {
        System.out.println("执行【新】的提供者NewSomeServiceImpl的hello()");
        return "NewSomeServiceImpl";
    }
}
  • 修改配置文件:指定版本 0.0.1 对应的是 oldService 实例,而版本 0.0.2 对应的是 newService 实例。
<!-- 指定当前工程在Monitor中显示的名称,一般与工程名相同 -->
<dubbo:application name="04-provider-version" />

<!-- 指定服务注册中心:zk单机 -->
<dubbo:registry address="zookeeper://192.168.254.120:2181" />

<!-- 注册Service实现类 -->
<bean id="oldService" class="com.yw.dubbo.example.provider.OldSomeServiceImpl" />
<bean id="newService" class="com.yw.dubbo.example.provider.NewSomeServiceImpl" />
<!-- 服务暴露 -->
<dubbo:service interface="com.yw.dubbo.example.service.SomeService" ref="oldService" version="0.0.1" />
<dubbo:service interface="com.yw.dubbo.example.service.SomeService" ref="newService" version="0.0.2"/>

创建消费者 04-consumer-version

  • 创建工程:复制前面的提供者工程 02-consumer-zk,并更名为 04-consumer-version。
  • 修改配置文件:
<!--  &lt;!&ndash;  订阅服务:指定消费0.0.1版本,即oldService提供者 &ndash;&gt;-->
<!--  <dubbo:reference id="someService" check="false" version="0.0.1" protocol="dubbo"-->
<!--                   interface="com.yw.dubbo.example.service.SomeService" />-->

<!--  订阅服务:指定消费0.0.2版本,即newService提供者 -->
<dubbo:reference id="someService" version="0.0.2" interface="com.yw.dubbo.example.service.SomeService"/>

服务分组

  • 服务分组与多版本控制的使用方式几乎是相同的,只要将 version 替换为 group 即可。但使用目的不同,使用版本控制的目的是为了升级,将原有老版本替换掉,将来不再提供老版本的服务,所以不同版本不能出现相互调用。而分组的目的则不同,其也是针对不同需求,给出了多种实现。但是不同的是,这些不同实现并没有谁替换掉谁的意思,是针对不同的需求,或针对不同功能模块所给出的不同实现。这些实现所提供的服务是并存的,所以它们间可以出现相互调用关系。例如,对于支付服务的实现,可以有微信支付实现与支付宝实现等。

创建提供者 05-provider-group

  • 创建工程:复制前面的提供者工程 04-provider-version,并更名为 05-provider-group。
  • 定义两个接口实现类:删除原来的两个接口实现类,重新定义两个新的实现类。
public class WeixinServiceImpl implements SomeService {
    @Override
    public String hello(String name) {
        System.out.println("使用【微信】支付");
        return "WeixinServiceImpl";
    }
}
public class ZhifubaoServiceImpl implements SomeService {
    @Override
    public String hello(String name) {
        System.out.println("使用【支付宝】支付");
        return "ZhifubaoServiceImpl";
    }
}
  • 修改配置文件:
<!-- 指定当前工程在Monitor中显示的名称,一般与工程名相同 -->
<dubbo:application name="05-provider-group"/>
<!-- 指定服务注册中心:zk单机 -->
<dubbo:registry address="zookeeper://192.168.254.120:2181"/>
<!-- 注册Service实现类 -->
<bean id="weixinService" class="com.yw.provider.WeixinServiceImpl"/>
<bean id="zhifubaoService" class="com.yw.provider.ZhifubaoServiceImpl"/>
<!-- 服务暴露 -->
<dubbo:service interface="com.yw.service.SomeService" ref="weixinService" group="pay.weixin"/>
<dubbo:service interface="com.yw.service.SomeService" ref="zhifubaoService" group="pay.zhifubao"/>

创建消费者 05-consumer-group

  • 创建工程:复制前面的提供者工程 04-consumer-version,并更名为 05-consumer-group。
  • 修改配置文件:
<-- ...略 -->
<!--  订阅服务:指定调用微信支付服务 -->
<dubbo:reference id="weixin" group="pay.weixin" interface="com.yw.dubbo.example.service.SomeService" />

<!--  订阅服务:指定调用支付宝支付服务 -->
<dubbo:reference id="zhifubao" group="pay.zhifubao" interface="com.yw.dubbo.example.service.SomeService"/>
  • 修改消费者类:
public class ConsumerRun {
    public static void main(String[] args){
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring-consumer.xml");

        // 使用微信支付
        SomeService weixinService = (SomeService) ac.getBean("weixin");
        String weixin = weixinService.hello("China");
        System.out.println(weixin);

        // 使用支付宝支付
        SomeService zhifubaoService = (SomeService) ac.getBean("zhifubao");
        String zhifubao = zhifubaoService.hello("China");
        System.out.println(zhifubao);
    }
}

多协议支持

  • 除了 Dubbo 服务暴露协议外,Dubbo框架还支持另外 8 种服务暴露协议:RMI协议Hessian协议HTTP协议WebService协议Thrift协议Memcached协议Redis协议Rest协议。但在实际生产中,使用最多的就是 Dubbo 服务暴露协议。

各个协议的特点

  • 大数据小并发用短连接协议,小数据大并发用长连接协议
dubbo协议
  • Dubbo默认传输协议
  • 连接个数:单连接
  • 连接方式:长连接
  • 传输协议:TCP
  • 传输方式:NIO异步传输
  • 适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单个消费者无法压满提供者,尽量不要用 dubbo 协议传输大文件或超大字符串。
rmi协议
  • 采用 JDK 标准的 java.rmi.* 实现
  • 连接个数:多连接
  • 连接方式:短连接
  • 传输协议:TCP
  • 传输方式:BIO同步传输
  • 使用范围:传入传出参数数据包大小混合,消费者与提供者个数差不多,可传文件。
hession协议
  • 连接个数:多连接
  • 连接方式:短连接
  • 传输协议:HTTP
  • 传输方式:BIO同步传输
  • 使用范围:传入传出参数数据包较大,提供者比消费者个数多,提供者抗压能力较大,可传文件
http协议
  • 连接个数:多连接
  • 连接方式:短连接
  • 传输协议:HTTP
  • 传输方式:BIO同步传输
  • 使用范围:传入传出参数数据包大小混合,提供者比消费者个数多,可用浏览器查看,可用表单或URL传入参数,暂不支持传文件。
webService协议
  • 连接个数:多连接
  • 连接方式:短连接
  • 传输协议:HTTP
  • 传输方式:BIO同步传输
  • 使用范围:系统集成,跨语言调用
thrift协议
  • thrift 是 Facebook 捐给 Apache 的一个 RPC 框架,其消息传递采用的协议即为 thrift 协议。当前 dubbo 支持的 thrift 协议是对 thrift 原生协议的扩展。thrift 协议不支持 null 值的传递。
memcached协议与redis协议
  • 它们都是高效的 KV 缓存服务器。它们会对传输的数据使用相应的技术进行缓存。
rest协议
  • 若需要开发具有 Restful 风格的服务,则需要使用该协议。

用法

  • 对于多协议的用法有两种,一种是同一个服务支持多种协议,一种是不同的服务使用不同的协议。
同一服务支持多种协议
  • 应用场景:系统在使用过程中其使用场景逐渐发生了变化,例如,由原来的消费者数量多于提供者数量,变为消费者数量与提供者数量差不多了,并且原来系统不用传输文件,现在的系统需要传输文件了。此时就将原来默认的 dubbo 协议更换为 rmi 协议。目的是为了兼容老工程,扩展新功能。
  • 修改提供者配置文件:直接在 04-provider-version 工程中进行修改。在提供者中首先要先声明新添加的协议,然后再服务 <dubbo:service /> 标签中再增加该新添加的协议。若不指定,默认为 dubbo 协议。

在这里插入图片描述

  • 这里需要理解这个服务暴露协议的意义。其是指出,消费者要连接当前的服务,就需要通过这里指定的协议及端口号进行访问。这里的端口号可以是任意的,不一定非要使用默认的端口号(Dubbo默认为20880,rmi默认为1099)。这里指定的协议名称及端口号,在当前服务注册到注册中心时会一并写入到服务映射表中。当消费者根据服务名称查找到相应的主机时,其同时会查询出消费此服务的协议、端口号等信息。其底层就是一个 Socket 编程,通过主机名和端口号进行连接。
  • 修改消费者配置文件:直接在 04-consumer-version 工程中进行修改。在消费者引用服务时要指出所要使用的协议:

在这里插入图片描述

不同服务使用不同协议
  • 应用场景:同一个系统中不同的业务具有不用的特点,所以它们的传输协议就应该根据它们的特点选择不同的协议。例如,对于前面使用服务分组实现的“微信支付”与“支付宝支付”,就可以针对不同支付方式,使用不同的协议。
  • 修改提供者配置文件:直接在 05-provider-group 工程中进行修改。在提供者中首先要先声明新添加的协议,然后再服务 <dubbo:service /> 标签中通过 protocol 属性指定所要使用的服务协议。若不指定,默认为 dubbo 协议。

在这里插入图片描述

  • 修改消费者配置文件:直接在 05-consumer-group 工程中进行修改。然后在消费者端通过 <dubbo:reference/> 引用服务时通过添加 protocol 属性指定要使用的服务协议。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

讲文明的喜羊羊拒绝pua

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值