newfarhui 2020-01-30
Apache Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力;面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
dubbo官网:点我直达
<?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>org.cyb</groupId> <artifactId>02-first-provider</artifactId> <version>1.0-SNAPSHOT</version> <!--编译器依赖--> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>13</maven.compiler.source> <maven.compiler.target>13</maven.compiler.target> <!--自定义版本号--> <spring-version>4.3.16.RELEASE</spring-version> </properties> <dependencies> <!--自定义工程依赖--> <dependency> <groupId>org.cyb</groupId> <artifactId>01-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!--dubbo依赖--> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.7</version> </dependency> <!--netty--> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.32.Final</version> </dependency> <!--spring依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</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-context</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-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-tx</artifactId> <version>${spring-version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring-version}</version> </dependency> </dependencies> </project>
<?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="02-first-provider"/> <!--注册Service,将来服务的提供者--> <bean id="someService" class="com.cyb.service.SomeServiceImpl"></bean> <!--暴露服务,采用直连的方式--> <dubbo:service interface="com.cyb.service.SomeService" ref="someService" registry="N/A"></dubbo:service> </beans>
package com.cyb.service; public class SomeServiceImpl implements SomeService{ @Override public String hello(String name) { System.out.println("Dubbo World Welcome You"+name); return "chenyanbin"; } }
package com.cyb.run; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.io.IOException; public class RunProvider { public static void main(String[] args) throws IOException { //创建spring容器 ApplicationContext ac = new ClassPathXmlApplicationContext("spring-dubbo-provider.xml"); //启动spring容器 ((ClassPathXmlApplicationContext) ac).start(); //将当前主线程阻塞 System.in.read(); } }
<?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>org.cyb</groupId> <artifactId>02-first-consumer</artifactId> <version>1.0-SNAPSHOT</version> <!--编译器依赖--> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>13</maven.compiler.source> <maven.compiler.target>13</maven.compiler.target> <!--自定义版本号--> <spring-version>4.3.16.RELEASE</spring-version> </properties> <dependencies> <!--自定义工程依赖--> <dependency> <groupId>org.cyb</groupId> <artifactId>01-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!--dubbo依赖--> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.7</version> </dependency> <!--netty--> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.32.Final</version> </dependency> <!--spring依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</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-context</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-aop</artifactId> <version>${spring-version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring-version}</version> </dependency> </dependencies> </project>
<?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="02-first-consumer"/> <!--消费引用--> <dubbo:reference id="someSerivce" interface="com.cyb.service.SomeService" url="dubbo://localhost:20880"></dubbo:reference> </beans>
注:端口号20880固定
package com.cyb.run; import com.cyb.service.SomeService; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class RunConsumer { public static void main(String[] args) { //创建Spring容器 ApplicationContext ac=new ClassPathXmlApplicationContext("spring-dubbo-consumer.xml"); SomeService service = (SomeService) ac.getBean("someSerivce"); service.hello("tom"); } }
地址:http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html
配置参考地址:点我直达
<!-- zk客户端依赖:curator-framework--> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>4.0.1</version> </dependency>
<?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="03-provider-zk"/> <!--注册Service,将来服务的提供者--> <bean id="someService" class="com.cyb.service.SomeServiceImpl"></bean> <!-- 声明zk服务中心 --> <!-- 单机版 --> <!-- 方式一 --> <dubbo:registry address="zookeeper://192.168.1.111:2181"/> <!-- 方式二 --> <!-- <dubbo:registry protocol="zookeeper" address=""/> --> <!-- 集群配置 --> <!-- 方式一 --> <!-- <dubbo:registry address="zookeeper://10.20.153.10:2181?backup=10.20.153.11:2181,10.20.153.12:2181" /> --> <!-- 方式二 --> <!-- <dubbo:registry protocol="zookeeper" address="10.20.153.10:2181,10.20.153.11:2181,10.20.153.12:2181" /> --> <!-- 同一Zookeeper,分成多组注册中心 --> <!-- <dubbo:registry id="chinaRegistry" protocol="zookeeper" address="10.20.153.10:2181" group="china" /> <dubbo:registry id="intlRegistry" protocol="zookeeper" address="10.20.153.10:2181" group="intl" /> --> <!-- 暴露服务,将服务暴露给zk服务中心 --> <!-- <dubbo:service interface="com.cyb.service.SomeService" ref="someService" registry="N/A"></dubbo:service> --> <dubbo:service interface="com.cyb.service.SomeService" ref="someService"></dubbo:service> </beans>
<!-- zk客户端依赖:curator-framework--> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>4.0.1</version> </dependency>
<?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="03-consumer-zk"/> <!-- 声明zk服务中心 --> <!-- 单机版 --> <dubbo:registry address="zookeeper://192.168.1.111:2181"/> <!--消费引用--> <!-- <dubbo:reference id="someSerivce" interface="com.cyb.service.SomeService" url="dubbo://localhost:20880"></dubbo:reference> --> <dubbo:reference id="someSerivce" interface="com.cyb.service.SomeService"></dubbo:reference> </beans>
为了进一步提高消费者对用户的响应速度,减少提供者的压力。Dubbo提供了基于结果的声明式缓存。该缓存是基于消费者端的,所以使用很简单,只需修改消费者配置文件,与提供者无关。
仅需在<dubbo:reference />中添加cache="true"即可
先不加缓存,发现提供者执行2次,加上缓存提供者只执行一次
<?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="03-consumer-zk"/> <!-- 声明zk服务中心 --> <!-- 单机版 --> <dubbo:registry address="zookeeper://192.168.1.111:2181"/> <!-- <dubbo:reference id="someSerivce" interface="com.cyb.service.SomeService" url="dubbo://localhost:20880"></dubbo:reference> --> <!-- <dubbo:reference id="someSerivce" interface="com.cyb.service.SomeService"></dubbo:reference> --> <!--消费引用:基于服务级别的声明式缓存(结果缓存)--> <!--<dubbo:reference id="someSerivce" interface="com.cyb.service.SomeService" cache="true"></dubbo:reference>--> <!--消费引用:基于方法级别的声明式缓存(结果缓存)--> <dubbo:reference id="someSerivce" interface="com.cyb.service.SomeService" > <!--lru--> <dubbo:method name="hello" cache="lru"></dubbo:method> <!--threadlocal--> <dubbo:method name="hello" cache="threadlocal"></dubbo:method> </dubbo:reference> </beans>
Dubbo的结果缓存可以应用在查询结果不变的场景。即不能使用在如下场景:消费者A调用的业务方法后从DB查询到一个结果a,此后,消费者B对DB中的该结果相关数据进行了修改,已使该查询结果变为b,但由于使用了结果缓存,消费者A中调用业务方法后的查询结果将长时间为a,直到该结果由于缓存空间满而被消除,否则,永远无法得到更新过的结果b。
Dubbo的多版本控制指的是,服务名称(接口名)相同的情况下提供不同的服务(实现类不同) ,然而消费者是通过服务名称(接口名)进行服务查询并进行消费的。提供者所提供的服务名称相同,如何让消费者通过名称进行服务查找呢?为服务添加一个版本号,使用“服务名称”+“版本号”的方式来唯一确定一个服务。
多版本控制主要的应用场景是:当一个接口的实现类需要升级时,可以使用版本号进行过渡(根据开闭原则,不能直接修改原实现类,只能添加新的实现类)。需要注意的是,版本号不同的服务间是不能互相引用的,因为新版本存在的目的是替换老版本。在生产环境中若存在多个提供者需要升级,一般不会一次性全部进行升级,而是会在低压力时间段先升级一部分,然后在下次再进行部分升级,直到全部升级完成。那么,这期间就需要使用版本号进行过渡。
拷贝项目
服务分组与多版本控制的使用方式几乎是相同的,只要将version替换为group即可。但使用目的不同。使用版本控制的目的是为了升级,将原有老版本替换掉,将来不再提供老版本的服务,所以不同版本间不能出现相互调用。而分组的目的则不同,其也是针对相同接口,给出了多种实现类,但不同的是,这些不同实现并没有替换掉谁的意思,是针对不同需求,或针对不同功能模块锁给出的实现。这些实现所提供的服务是并存的,所以他们间可以出现相互调用关系。例如,支付服务的实现,可以有微信支付实现与支付宝实现等。
如果我们的服务启动过程中需要warmup事件(预热事件,与JVM重启后的预热过程相似,在启动后一小段事件后性能才能达到最佳状态)。比如初始化缓存,等待相关资源就位等。可以使用deplay进行延迟暴露。
值需要在服务提供者的<dubbo:service/>标签中添加delay属性,其值若为整数,则单位为毫秒,表示在指定事件后再发布服务;若为-1,则表示在spring初始化完毕后再暴露服务。
很多时候一个项目中会有多个注册中心。
同一个服务可以注册到不同地域的多个注册中心,以便为不同地域的服务消费者提供更为快捷的服务。
修改服务提供者配置文件。多个注册中心之间使用逗号分隔。
注:不是集群!!!
修改服务提供者配置文件
同一个消费者需要调用两个不同中心服务,而调用的该服务的名称(接口)、版本等都是相同的。不同中心的这个相同名称的服务调用时不同数据库中的数据,即相同服务最终执行的结果是不同的。
修改服务消费者配置文件
前面的示例中,服务提供者与服务消费者都是通过zookeeper连接协议连接上ZooKeeper注册中心的。
提供者与消费者均连接上了注册中心,那么消费者就理所当然的可以享受提供者提供的服务了么?
实际情况并不是这样的。前述ZooKeeper协议,是消费者/提供者连接注册中心的连接协议,而非消费者与提供者间的连接协议。
当消费者连接上注册中心后,在消费服务之前,首先需要连接上这个服务提供者。虽然消费者通过注册中心可以获取到服务提供者,但提供者对于消费者来说却是透明的,消费者并不知道真正的服务提供者是谁。不过,无论提供者是谁,消费者都必须连接上提供者才可以获取到真正的服务,而这个连接也是需要专门的连接协议的。这个协议称为服务暴露协议。
可是我们之前的代码示例中并没有看到服务暴露协议的相关配置,但仍可正常运行项目,这是为什么呢?因为采用了默认的暴露协议:Dubbo服务暴露协议。处理Dubbo服务暴露协议外,Dubbo框架还支持另外七种服务暴露协议:Hessian协议、HTTP协议、RMI协议、WebService协议、Thrift协议、Memcached协议、Redis协议;但在实际生产中,使用最多的就是Dubbo服务暴露协议。
Dubbo服务暴露协议,适合于小数据量大并发的服务调用,以及服务消费者主机数远大于服务提供者主机数的情况。
在服务提供者的spring配置文件中首先需要注册暴露协议,然后在暴露服务时具体制定所使用的已注册的暴露协议。
注:protocol属性用于指定当前服务所使用的暴露协议
直接修改服务提供者的配置文件
直接修改服务提供者配置文件
提供者上尽量多配置消费者端的属性,让提供者实现着一开始就思考提供者服务特点、服务质量等问题。因为作服务的提供者,比服务使用方更清楚服务性能参数,如调用的超时时间、合理的重试次数等。在提供者端配置后,消费者不配置则会使用提供者端的配置值,即提供者配置可以作为消费者的缺省值。否则,消费者会使用消费者端的全局设置,这对提供者是不可控的,并且往往不合理的。
以下属性在<dubbo:method/>上则是针对指定方法,配置在<dubbo:service/>上则是针对整个服务。