创建业务接口工程
我们将这个接口单独抽取出来,打成jar包被多个服务锁依赖
创建服务提供者Provider
Provider工程的pom文件如下:
<properties>
<!--2.6.4版本的duboo依赖的Spring的版本-->
<spring-version>4.3.16.RELEASE</spring-version>
</properties>
<dependencies>
<dependency>
<groupId>com.dubbo.test</groupId>
<artifactId>00-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--dubbo的依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.4</version>
</dependency>
<!--Spring的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring-version}</version>
</dependency>
</dependencies>
spring-dubbo-provider.xml的配置如下所示:
这里需要注意的是xml文件头的问题,一定要注意,否则启动会报异常:
Exception:通配符的匹配很全面, 但无法找到元素 'dubbo:application' 的声明;
<?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="01-first-provider"/>
<!--注册service ,其就是真正的服务提供者-->
<bean id="service" class="com.test.service.someServiceImpl"/>
<!--将service的服务暴露 确定接口,确定实现,使用点对点的方式连接,暂时不是用Zookeeper-->
<dubbo:service interface="com.test.service.someService"
ref="service"
registry="N/A" />
</beans>
启动类 providerRun如下所示
/**
* 启动类
*/
public class providerRun {
public static void main(String[] args) throws IOException {
//在容器启动到时候,就把我们的服务注册到注册中心,或者称之为暴露
//创建Spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-dubbo-provider.xml");
//Spring容器启动
((ClassPathXmlApplicationContext) ac).start();
//使当前主线程阻塞,以提供持续服务
System.in.read();
}
}
创建服务的消费者Consumer
pom.xml内容如下:
<properties>
<!--2.6.4版本的duboo依赖的Spring的版本-->
<spring-version>4.3.16.RELEASE</spring-version>
</properties>
<dependencies>
<dependency>
<groupId>com.dubbo.test</groupId>
<artifactId>00-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--dubbo的依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.4</version>
</dependency>
<!--Spring的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring-version}</version>
</dependency>
</dependencies>
spring-dubbo-consumer.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="01-first-consumer"/>
<!--确定服务接口,确定连接方式-->
<dubbo:reference id="someService"
interface="com.test.service.someService"
url="dubbo://localhost:20880"/>
</beans>
启动类如下所示:
/**
* consumer启动兼服务消费类
*/
public class consumerRun {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("sprin-dubbo-consumer.xml");
someService service = (someService) ac.getBean("someService");
service.hello("Toney");
}
}
运行测试
我们先启动服务提供者providerRun,再启动消费者consumerRun,观察到如下信息
我们通过消费者consumer在自己工程的Spring容器中获取到的someService引用,成功调用了provider工程的hello方法
至于中途是怎么调用的,就要着重关注我们的两个spring配置文件了
使用Zookeeper注册中心
在上一个案列中,我们使用的是直连的方式建立连接,现在我们采用ZK作为我们的注册中心
Dubbo的注册中心官方推荐的就是Zookeeper
工程的话,复制服务提供者和消费者,稍加改动即可
改造服务的提供者Provider
pom.xml文件 增添一个依赖即可:
<!-- ZK的客户端依赖:在进入到阿帕奇孵化器后由zkClient改为curator -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
配置文件如下所示:增改
使用的是集群的方式,集群机器的先后顺序没有影响
<!--声明ZK服务中心 (单机)-->
<!--<dubbo:registry address="zookeeper://192.168.159.159:2181"/>-->
<!--第二中方式声明ZK服务中心 (单机)-->
<!--<dubbo:registry protocol="zookeeper" address="192.68.159.159:2181" />-->
<!--声明ZK服务中心,ZK集群的方式-->
<dubbo:registry address="zookeeper://192.168.159.159:2181?backup=192.168.159.169:2181,192.168.159.179:2181"/>
<!--指定实列名,指定服务接口,不指定url,默认为ZK-->
<dubbo:reference id="someService"
interface="com.test.service.someService"/>
改造服务消费者Consumer
pom.xml 添加一个依赖
<!-- ZK的客户端依赖:在进入到阿帕奇孵化器后由zkClient改为curator -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
配置文件如下所示: 增改
<!--声明ZK服务中心 (单机)-->
<!--<dubbo:registry address="zookeeper://192.168.159.159:2181"/>-->
<!--第二中方式声明ZK服务中心 (单机)-->
<!--<dubbo:registry protocol="zookeeper" address="192.68.159.159:2181" />-->
<!--声明ZK服务中心,ZK集群的方式-->
<dubbo:registry address="zookeeper://192.168.159.159:2181?backup=192.168.159.169:2181,192.168.159.179:2181"/>
<!--指定实列名,指定服务接口,不指定url,默认为ZK--> <dubbo:reference id="someService" interface="com.test.service.someService"/>
启动Zookeeper的服务
我们指定了ip为192.168.159.159这台机器上的ZK,那我们就启动之,
详情翻阅:https://www.cnblogs.com/msi-chen/p/11068925.html(ZK的单机&集群环境搭建)
运行测试
首运行服务提供者。其次运行服务消费者,我们观察控制台输出
再观察消费者的控制台,我做了点修改,得到了返回值并打印:
连接单机ZK,集群ZK都已测试通过,暂时就写到这儿吧,我要做一个被写代码耽误的中华小当家(煮夫!!)
Dubbo的声明式缓存
缓存嘛?就是缓存嘛!为了减少服务提供者的压力,Dubbo提供了基于结果的声明式缓存,该缓存是基于消费者端的,只需要修改消费者配置文件即可,使用很简单。
修改消费者配置文件
我们就使用ZK的这个工程做演示
修改消费者的配置文件,添加如上属性即可
修改消费者的启动类
我们修改消费者的启动了,对同一个函数进行两次相同的访问,如下:
然后我们看服务的提供者,打印了几次,如果是一次的话,则 说明第二次函数的调用并没有经过服务的提供者的函数
查看运行结果
——
通过上面的两张图,就可以知道,我们的缓存起效果了,但在这里不得不了解了解Dubbo提供了三种结果缓存机制
当然下面三个就是cache属性的值,直接指定使用哪种缓存机制,默认是lru
-
lru:服务级别缓存的默认机制,该机制可以默认缓存1000个结果集,若超过,采用最近最少使用原则抛出删除缓存,以保证新的结果被缓存
-
threadlocal:当前线程缓存,当多个线程要对当前线程进行某一操作时首先需要查询当前线程的某个信息,通过线程缓存,可以有效减少缓存
-
jcache:可以桥接第三方缓存产品
结果缓存的运用场景
Dubbo的缓存是基于结果的,是在服务的消费方的主观能动控制的,一般用于查询结果不变的场景,因为他没有缓存失效时间控制这玩意儿,只有等缓存结果满了,pop出去最近最少使用的结果,如果一个数据是变动的,那么服务消费者获取到的永远都是没有更新的数据,除非该数据被POP
Dubbo的多版本控制
Dubbo的多版本控制指的是:服务名称(即接口名)相同的情况下提供不同的服务(实现类不同),为服务添加一个版本号,使用"服务名称"+"版本号"的方式来确定一个服务
多版本控制主要的应用场景:当一个接口的实现类需要升级时,我们可以是哟个版本号进行过渡,注意版本不同的服务之间是不能相互引用的,设想这样一个环境,一个服务的一个版本需要升级,在生产环境中一般不会一次性全部进行升级,而是在访问最少的时候先升级一部分,然后下一次访问最少的时候再升级一部分,而这一部分又一部分就可以使用版本号来进行过渡
添油加醋提供者
之前someService的实现类只有一个,现在我们增加一个,用作版本演示
我们的服务提供者由一个实现类变为了两个实现类,各自都有不同的实现,我们首先对其进行注册,其次在暴露服务时,指定自己的版本号
添油加醋消费者
而消费者在远程调用服务的时候,指定服务的版本即可调用指定了版本号的服务
运行测试
——
直接在消费端修改版本号即可消费消费相同接口下不同的服务实现
Dubbo的服务分组
简单阐述,不做测试
-
服务分组和多版本控制的使用方式基本都是一样的,知识将version改成group就好了,但是各自适用的环境有点不同,使用版本控制是为了升级,新版本替代老版本,老版本不再提供服务 : version="1.0.0" 、 version="2.0.0";
-
分组也是相同接口给出了不同的实现类,但是这些实现类并没有谁要替换掉谁的意思,针对不同的需求调用不同的服务接口,他们是并存的,就像支付服务的实现,可以有微信支付,可以有支付宝支付,还可以银联,服务并存想用那个服务消费者就指定该服务所对应的分组即可 :group="pay.weixin" 、 group="pay.zhifubao"
Dubbo的服务延迟暴露
如果我们的服务启动过程中需要预热事件(再启动一段时间后性能才能到达最佳状态)比如初始化缓存,等待相关资源就位等,可以使用deplay进行延迟暴露
-
如上所示:我们只需要在服务的提供方的<dubbo:service />标签中添加delay属性,单位毫秒
-
若值为正数,则表示延迟暴露多少毫秒,在指定时间后再发布服务,
-
若值为-1,则表示在spring初始化完成后暴露服务
Dubbo的多注册中心
很多时候一个项目会有多个注册中心
同一个服务注册到多个注册中心_[一对多]
同一个服务可以注册到多个不同地域的注册中心,为多个不同地域的服务提供服务
修改服务提供者的配置文件,多个注册中心之间使用逗号分割,如下:
当然第二个集群是假的,知道牛啃南瓜怎么下嘴就行?当然你也可以使用ZK单机模式演示
不同的服务注册到不同的注册中心_[一对一]
一个项目中,不同的服务可以注册不同的注册中心:如下
同一个服务引用自不同的注册中心_[多对一]
同一个消费者需要调用两个注册中心的服务,而被调用的服务的接口名,版本号,分组等是相同的,不同中心的这个相同名称的服务CURD的是不同的数据库,即服务名相同,但是实现是不同的,修改服务消费者如下:
Dubbo的多协议支持
服务暴露协议
在我们前面的Demo中,服务提供者和服务的消费者是通过zookeepeer连接协议连接上Zookeeper注册中心的
为什么服务的提供者和消费者连接上Zookeeper,消费者就可以消费服务了呢?
-
zookeeper协议:是提供者/消费者连接注册中心的连接协议,并不是提供者和消费者之间的连接协议
-
当消费者连接上注册中心后,在消费服务之前,首先需要连接上这个服务的提供者,虽然服务的消费者可以通过注册中心获取到服务提供者,但提供者对于消费者来说确实透明的,消费者并不知道真正的服务提供者是谁,不过无论服务的提供者是谁,消费者都需要连接上服务的提供者才可以获取到真正的服务,而这个连接也是需要专门的链接协议的,这个协议被称为服务的暴露协议
-
在我们之前的Demo中并没有看到服务暴露协议的相关配置,项目也是可以运行,因为Dubbo采用了默认的暴露协议,Dubbo服务暴露协议
-
Dubbo服务暴露协议,适用于小数据量大并发的服务调用,以及服务消费者主机数远远大于服务提供者主机的情况,服务提供少,需求多,单个主机应对高并发。
-
了解内容:除了Dubbo服务暴露协议,Dubbo框架还支持另外七种服务暴露协议,Hessian协议,Http协议,RMI协议,WebService协议,THrift协议,Memcached协议,Redis协议,在实际中使用最多的就是Dubbo服务暴露协议
服务暴露协议用法
下面我们以Dubbo服务暴露协议作为列子来说明服务暴露协议的用法
-
在服务的提供者的Spring配置文件中注册服务暴露协议,
-
然后在暴露服务时具体指定所使用的已经被注册的暴露协议
二、同一服务支持多种协议(修改服务提供者的配置文件)
三、不同服务使用不同的协议(修改服务提供者的配置文件)
牛啃南瓜知道怎么啃就行,这里我就没做过多的演示
Dubbo的高级设置以及使用建议
在Provider上尽量多的配置Consumer端的属性
使得Provider实现着一开始就考虑到Provider的服务特点、服务质量等问题,因为作为服务的提供者比服务的消费者更加清楚服务的性能参数,如调用的超时时间,合理的重试次数等,在Provider配置后,Consumer不配置则会默认使用Provider端的配置值,如果Consumer没有配置,Provider也没有配置,就会使用Consumer端的全局设置,这对于Provider而言是不可控的,也是不合理的
粒度到可以针对某一个服务也可以针对服务的同时针对某一个方法(hello方法)
-
timeout:远程服务调用超时的时限
-
retries:失败重试次数,默认为2
-
loadbalance:负载均衡算法,默认是随机random,还可以选择轮询(roundrobin)、最不活跃优先(leastactive)等
-
actives:消费者最大并发调用限制,达到上限后,新的调用会被阻塞直到超时,0表示不限制
Provider端配置合理的Provider端属性
threads:用于指定服务的线程池大小
executes:一个服务并行执行的请求上限,超过上限,新请求肯定被阻塞,可能阻塞到超时,该属性在<dubbo:method/>则是针对指定的方法,配置在<dubbo:service />上则是针对整个服务
常用性能调优参数(来自网络)
参数名 | 作用范围 | 默认值 | 说明 | 备注 |
threads | provider | 200 | 业务处理线程池大小 | |
iothreads | provider | CPU+1 | io线程池大小 | |
queues | provider | 0 |
线程池队列大小,当线程池满时,排队等待执行的队列大小, 建议不要设置,当线程程池时应立即失败, 重试其它服务提供机器,而不是排队,除非有特殊需求 |
|
connections | consumer | 0 |
对每个提供者的最大连接数, rmi、http、hessian等短连接协议表示限制连接数, Dubbo等长连接协表示建立的长连接个数 |
Dubbo协议默认共享一个长连接 |
actives | consumer | 0 | 每服务消费者每服务每方法最大并发调用数 | 0表示不限制 |
acceptes | provider | 0 | 服务提供方最大可接受连接数 | 0表示不限制 |
executes | provider | 0 | 服务提供者每服务每方法最大可并行执行请求数 | 0表示不限制 |