Dubbojava
Author: Lijbnode
Email: lijb1121@163.commysql
1.什么是Dubbogit
dubbo:是一个基于soa思想的rpc框架github
soa思想:面向服务的架构web
给每个模块暴露对应的ip和端口,当作一个服务进行运行算法
重点在于服务的管理(负载均衡,容灾模式,服务的横向扩展)spring
2.架构的演变sql
节点角色说明数据库
节点 角色说明
Provider 暴露服务的服务提供方
Consumer 调用远程服务的服务消费方
Registry 服务注册与发现的注册中心
Monitor 统计服务的调用次数和调用时间的监控中心 Container 服务运行容器
调用关系说明
4.Dubbo的入门案例
dubbo的开发方式
引入Dubbo依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.5</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo-cluster</artifactId> <version>2.6.5</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo-config-spring</artifactId> <version>2.6.5</version> </dependency> <!-- curator-framework 链接zk--> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.7.1</version> </dependency>
若是是2.6.2之前版本默认使用的zkclient链接zookeeper。
Dubbo+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"> <!--对服务起名字,通常要求服务名惟一--> <dubbo:application name="usercenter-provider"/> <!--链接zookeer,将来将服务注册到zk中--> <dubbo:registry protocol="zookeeper" address="CentOS:2181"/> <!--定制服务接受协议 启动netty-server --> <dubbo:protocol name="dubbo" port="20880"/> <!--暴露本地的bean服务--> <dubbo:service interface="com.baizhi.service.IUserService" ref="userService" /> </beans>
开发服务提供者接口
publicinterface UserService { public String findName(String name); }
开发服务提供者实现类
public classUserServiceImpl implements UserService { public String findName(String name) { System.out.println("这是根据用户名: "+name+" 查询用户的业务..."); return name; } }
启动服务提供者
publicstatic void main(String[] args) throws IOException { ClassPathXmlApplicationContext context =new ClassPathXmlApplicationContext("spring-dubbo.xml"); System.in.read(); }
服务消费方
引入依赖同服务方依赖
将服务方的接口复制到消费方项目中
publicinterface UserService { public String findName(String name); }
注意: 包结构要与服务提供方严格一致
配置文件
<?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"> <!--对服务起名字,通常要求服务名惟一--> <dubbo:application name="usercenter-consumer"/> <!--链接zookeer,将来将服务注册到zk中--> <dubbo:registry protocol="zookeeper" address="CentOS:2181"/> <!--引用远程接口,在本地产生代理bean--> <dubbo:reference id="userService" interface="com.baizhi.service.IUserService" /> </beans>
调用服务方服务
publicstatic void main(String[] args) { ApplicationContext context = newClassPathXmlApplicationContext("spring-dubbo.xml"); UserService userService = (UserService)context.getBean("userService"); System.out.println(userService.findName("张三")); }
Dubbo+Spring注解版本
服务提供端配置
import com.alibaba.dubbo.config.ApplicationConfig; import com.alibaba.dubbo.config.ProtocolConfig; import com.alibaba.dubbo.config.RegistryConfig; import com.alibaba.dubbo.config.spring.context.annotation.DubboComponentScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration //扫描的是接口实现类,该类上通常会使用@Service注解(dubbo) @DubboComponentScan(basePackages = {"com.baizhi.service.impl"}) public class Application_Provider { /* <dubbo:application name="usercenter-provider"/> */ [@Bean](https://my.oschina.net/bean) public ApplicationConfig applicationConfig(){ ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("usercenter-provider"); return applicationConfig; } /* <dubbo:registry protocol="zookeeper" address="CentOS:2181"/> * */ @Bean public RegistryConfig registryConfig(){ RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setAddress("CentOS:2181"); registryConfig.setProtocol("zookeeper"); registryConfig.setClient("curator"); return registryConfig; } /* <dubbo:protocol name="dubbo" port="20880"/> */ @Bean public ProtocolConfig protocolConfig(){ ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setName("dubbo"); protocolConfig.setPort(20880); return protocolConfig; } } -- ApplicationContext ctx= new AnnotationConfigApplicationContext(Application_Provider.class);
服务消费端
@Configuration //扫描的是接口,更具接口建立代理 @DubboComponentScan(basePackages = "com.baizhi.service") public class Application_Consumer { // <dubbo:reference id=“userService” interface=""/> @Reference private IUserService userService; @Bean public IUserService userService(){ return userService; } /* <dubbo:application name="usercenter-consumer"/> */ @Bean public ApplicationConfig applicationConfig(){ ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("usercenter-consumer"); return applicationConfig; } /* <dubbo:registry protocol="zookeeper" address="CentOS:2181"/> * */ @Bean public RegistryConfig registryConfig(){ RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setAddress("CentOS:2181"); registryConfig.setProtocol("zookeeper"); registryConfig.setClient("curator"); return registryConfig; } } --- ApplicationContext ctx= new AnnotationConfigApplicationContext(Application_Consumer.class);
Spring Boot 整合 Dubbo
pom.xml
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.17.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>0.1.0</version> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.24.Final</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.7</version> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
</dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
参考 http://start.dubbo.io/ 自动生成服务提供者和服务消费者
服务方代码
server.port= 8989 dubbo.application.name = dubbo-demo-server # Base packages to scan Dubbo Component: @com.alibaba.dubbo.config.annotation.Service dubbo.scan.basePackages = com.example ## RegistryConfig Bean dubbo.registry.id = my-registry dubbo.registry.address = CentOS:2181 dubbo.registry.client= curator dubbo.registry.protocol= zookeeper ## Legacy QOS Config dubbo.application.qosEnable=true dubbo.application.qosPort= 22222 dubbo.application.qosAcceptForeignIp=false
入口代码
@SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
客户端代码
# Dubbo Config properties ## ApplicationConfig Bean dubbo.application.name= dubbo-demo-client ## RegistryConfig Bean dubbo.registry.id = my-registry dubbo.registry.address = CentOS:2181 dubbo.registry.protocol= zookeeper dubbo.registry.client = curator dubbo.application.qosEnable=false
入口代码
@SpringBootApplication public class DemoApplication { @Reference private HelloService demoService; public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } @PostConstruct public void init() { String sayHello = demoService.sayHello("world"); System.err.println(sayHello); } }
以下:
<!--全局配置--> <dubbo:provider timeout="6000"/> <!--接口配置--> <dubbo:service interface="com.baizhi.service.UserService" ref="userService" timeout="5000" > <!--方法配置--> <dubbo:method name="findName" timeout="4000"/> </dubbo:service>
启动时检查
局部配置
<dubbo:reference interface="xx" check="false" />
全局配置
<dubbo:consumer check="false" />
Dubbo中集群的负载均衡
只须要将服务提供者代码,复制几份,修改一下的端口,所有启动便可(端口号不能冲突):
server1:<dubbo:protocol name="dubbo" port="20881"/> server2:<dubbo:protocol name="dubbo" port="20882"/> server3:<dubbo:protocol name="dubbo" port="20883"/>
启动全部的服务提供者集群
使用服务消费者进行调用
注意:默认dubbo使用负载均衡算法是random 随机的
常见的负载均衡算法
1.在一个截面上碰撞的几率高,但调用量越大分布越均匀,并且按几率使用权重后也比较均匀,有利于动态调整提供者权重 2.RoundRobin LoadBalance 轮循,按公约后的权重设置轮循比率 3.存在慢的提供者累积请求的问题,好比:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,长此以往,全部请求都卡在调到第二台上 4.LeastActive LoadBalance 最少活跃调用数,相同活跃数的随机,活跃数指调用先后计数差。 5.使慢的提供者收到更少请求,由于越慢的提供者的调用先后计数差会越大。 6.ConsistentHash LoadBalance 一致性Hash,相同参数的请求老是发到同一提供者。 若是对应的hash值上没有服务提供者,按照就近原则顺时针找离该hash值最近的服务端。
算法参见:http://en.wikipedia.org/wiki/Consistent_hashing
<!--缺省只对第一个参数 Hash,若是要修改,请配置--> <dubbo:parameterkey="hash.arguments" value="0,1" /> <!--缺省用160份虚拟节点,若是要修改,请配置--> <dubbo:parameterkey="hash.nodes" value="320" /> <!--当某一台提供者挂时,本来发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引发剧烈变更。-->
a.客户端设置集负载均衡策略
基于权重随机
<!--服务端,默认权重是100--> <dubbo:service interface="..." weight="100" /> <!--客户端--> <dubbo:reference interface="..." loadbalance="random" />
基于权重的轮训策略
<dubbo:reference interface="..." loadbalance="roundrobin" />
基于调用次数(致使慢机器接受更少的请求,下降负载)
<dubbo:reference interface="..." loadbalance="leastactive" />
一致性hash(默认使用参数hash)
<dubbo:reference interface="..." loadbalance="consistenthash" />
b . 客户端配置
<dubbo:referenceid="userService" loadbalance="consistenthash"interface="com.baizhi.service.UserService"/>
c. 服务端方法级别
<dubbo:serviceinterface="..."> <dubbo:method name="..." loadbalance="roundrobin"/> </dubbo:service>
配置优先级: 消费者优先级最高,服务端优先级就近原则,通常优先选择在服务提供方配置
Dubbo中集群的容错
在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。
集群容错模式配置:
<dubbo:servicecluster="failsafe"/> 或 <dubbo:referencecluster="failsafe"/
各节点关系:
a. 这里的 Invoker 是 Provider 的一个可调用 Service 的抽象,Invoker 封装了 Provider 地址及 Service 接口信息
Directory 表明多个 Invoker,能够把它当作 List<Invoker> ,但与 List 不一样的是,它的值多是动态变化的,好比注册中心推送变动
b.Cluster 将 Directory 中的多个 Invoker 假装成一个 Invoker,对上层透明,假装过程包含了容错逻辑,调用失败后,重试另外一个
c.Router 负责从多个 Invoker 中按路由规则选出子集,好比读写分离,应用隔离等
d.LoadBalance 负责从多个 Invoker 中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,须要重选
失败自动切换,当出现失败,重试其它服务器 。一般用于读操做,但重试会带来更长延迟。可经过 retries="2" 来设置重试次数(不含第一次)。
重试次数配置以下:
<dubbo:service retries="2" /> 或 <dubbo:referenceretries="2" /> 或 <dubbo:reference> <dubbo:method name="findFoo" retries="2" /> </dubbo:reference>
快速失败,只发起一次调用,失败当即报错。一般用于非幂等性的写操做,好比新增记录。
失败安全,出现异常时,直接忽略。一般用于写入审计日志等操做。
失败自动恢复,后台记录失败请求,定时重发。一般用于消息通知操做。
并行调用多个服务器,只要一个成功即返回。一般用于实时性要求较高的读操做,但须要浪费更多服务资源。可经过 forks="2" 来设置最大并行数。
广播调用全部提供者,逐个调用,任意一台报错则报错 [2]。一般用于通知全部提供者更新缓存或日志等本地资源信息。
Dubbo中线程模型
若是事件处理的逻辑能迅速完成,而且不会发起新的 IO 请求,好比只是在内存中记个标识,则直接在 IO 线程上处理更快,由于减小了线程池调度。
但若是事件处理逻辑较慢,或者须要发起新的 IO 请求,好比须要查询数据库,则必须派发到线程池,不然 IO 线程阻塞,将致使不能接收其它请求。
若是用 IO 线程处理事件,又在事件处理过程当中发起新的 IO 请求,好比在链接事件中发起登陆请求,会报“可能引起死锁”异常,但不会真死锁。
所以,须要经过不一样的派发策略和不一样的线程池配置的组合来应对不一样的场景:
<dubbo:protocol name="dubbo" dispatcher="all" threadpool="fixed" threads="100" />
Dispatcher
all 全部消息都派发到线程池,包括请求,响应,链接事件,断开事件,心跳等。
direct 全部消息都不派发到线程池,所有在 IO 线程上直接执行。
message 只有请求响应消息派发到线程池,其它链接断开事件,心跳等消息,直接在 IO 线程上执行。
execution 只请求消息派发到线程池,不含响应,响应和其它链接断开事件,心跳等消息,直接在 IO 线程上执行。
connection 在 IO 线程上,将链接断开事件放入队列,有序逐个执行,其它消息派发到线程池。
ThreadPool
fixed 固定大小线程池,启动时创建线程,不关闭,一直持有。(缺省)
cached 缓存线程池,空闲一分钟自动删除,须要时重建。
limited 可伸缩线程池,但池中的线程数只会增加不会收缩。只增加不收缩的目的是为了不收缩时忽然来了大流量引发的性能问题。
eager 优先建立Worker线程池。在任务数量大于corePoolSize可是小于maximumPoolSize时,优先建立Worker来处理任务。当任务数量大于maximumPoolSize时,将任务放入阻塞队列中。阻塞队列充满时抛出RejectedExecutionException。(相比于cached:cached在任务数量超过maximumPoolSize时直接抛出异常而不是将任务放入阻塞队列)
线程自动dump
当业务线程池满时,咱们须要知道线程都在等待哪些资源、条件,以找到系统的瓶颈点或异常点。dubbo经过Jstack自动导出线程堆栈来保留现场,方便排查问题
默认策略:
指定导出路径:
# dubbo.properties dubbo.application.dump.directory=/tmp <dubbo:application ...> <dubbo:parameter key="dump.directory" value="/tmp" /> </dubbo:application>
直连提供者
在开发及测试环境下,常常须要绕过注册中心,只测试指定服务提供者,这时候可能须要点对点直连,点对点直连方式,将以服务接口为单位,忽略注册中心的提供者列表,A 接口配置点对点,不影响 B 接口从注册中心获取列表。
若是是线上需求须要点对点,可在 dubbo:reference 中配置 url 指向提供者,将绕过注册中心,多个地址用分号隔开,配置以下 [1]:
<dubbo:reference id="xxxService" interface="com.alibaba.xxx.XxxService" url="dubbo://localhost:20890" />
在 JVM 启动参数中加入-D参数映射服务地址 [2],如:
java -Dcom.alibaba.xxx.XxxService=dubbo://localhost:20890
若是服务比较多,也能够用文件映射,用 -Ddubbo.resolve.file 指定映射文件路径,此配置优先级高于 dubbo:reference 中的配置 [3],如:
java -Ddubbo.resolve.file=xxx.properties
而后在映射文件 xxx.properties 中加入配置,其中 key 为服务名,value 为服务提供者 URL:
com.alibaba.xxx.XxxService=dubbo://localhost:20890
注意 为了不复杂化线上环境,不要在线上使用这个功能,只应在测试阶段使用。
多协议/多版本/多注册中心/服务分组
Dubbo 支持同一服务向多注册中心同时注册,或者不一样服务分别注册到不一样的注册中心上去,甚至能够同时引用注册在不一样注册中心上的同名服务。另外,注册中心是支持自定义扩展的 [1]。也就是注册中心的集群。
好比:中文站有些服务来不及在青岛部署,只在杭州部署,而青岛的其它应用须要引用此服务,就能够将服务同时注册到两个注册中心。
<dubbo:protocol name="dubbo" port="20880"/> <dubbo:protocol name="rmi" port="20881"/> <!--暴露本地的bean服务--> <dubbo:service protocol="dubbo,rmi" interface="" ref="userService" weight="100" /> <!--消费端必须指定一个协议--> <dubbo:reference interface="..." protocol="rmi" /> <!--多版本--> <dubbo:service interface="com.foo.BarService" ref="XxBean_V1" version="1.0.0" /> <dubbo:service interface="com.foo.BarService" ref="XxBean_V2" version="2.0.0" /> <dubbo:reference interface="com.foo.BarService" version="2.0.0" /> <!--不关注写*--> <dubbo:reference interface="com.foo.BarService" version="*" /> <!--服务分组--> <dubbo:service group="g1" interface="com.xxx.IndexService" ref="xxBean1" /> <dubbo:service group="g2" interface="com.xxx.IndexService" ref="xxBean2"/> <dubbo:reference interface="com.xxx.IndexService" group="g1" /> <!--若是不关注组,能够写*--> <dubbo:reference interface="com.xxx.IndexService" group="*" /> <!--多注册中心--> <dubbo:registry id="reg1" address="" /> <dubbo:registry id="reg2" address="" default="false" /> <dubbo:service interface="" ref="xx" registry="reg1,reg2" />
上下文信息|上下文传参
隐式传参
RpcContext.getContext().setAttachment("index", "1"); // 隐式传参,后面的远程调用都会隐式将这些参数发送到服务器端,相似cookie,用于框架集成,不建议常规业务使用 xxxService.xxx(); // 远程调用 --- public class XxxServiceImpl implements XxxService { public void xxx() { // 获取客户端隐式传入的参数,用于框架集成,不建议常规业务使用 String index = RpcContext.getContext().getAttachment("index"); } }
上下文信息
// 远程调用 xxxService.xxx(); // 本端是否为消费端,这里会返回true boolean isConsumerSide = RpcContext.getContext().isConsumerSide(); // 获取最后一次调用的提供方IP地址 String serverIP = RpcContext.getContext().getRemoteHost(); // 获取当前服务配置信息,全部配置信息都将转换为URL的参数 String application = RpcContext.getContext().getUrl().getParameter("application"); // 注意:每发起RPC调用,上下文状态会变化 yyyService.yyy(); --- public class XxxServiceImpl implements XxxService { public void xxx() { // 本端是否为提供端,这里会返回true boolean isProviderSide = RpcContext.getContext().isProviderSide(); // 获取调用方IP地址 String clientIP = RpcContext.getContext().getRemoteHost(); // 获取当前服务配置信息,全部配置信息都将转换为URL的参数 String application = RpcContext.getContext().getUrl().getParameter("application"); // 注意:每发起RPC调用,上下文状态会变化 yyyService.yyy(); // 此时本端变成消费端,这里会返回false boolean isProviderSide = RpcContext.getContext().isProviderSide(); } }
服务治理
|-dubbo | |--com.baizhi.service.IUserService |-consumers |-configurators |-routers |-providers |--com.baizhi.service.IOrderService |-consumers |-configurators |-routers |-providers
dubbo提供一个服务治理的webapp叫作dubboAdmin,因为该应有bug层出不穷,最终被dubbo抛弃。目前使用dubbo公司通常都会选择dubbokeeper第三方提供的服务治理应用管理Dubbo集群。用户可使用DubboKeeper实现配置规则(动态配置服务端、消费端运行参数例如:权重/容错/屏蔽)、路由规则
1)下载Dubbo-keeper安装包
https://github.com/dubboclub/dubbokeeper/archive/dubbokeeper-1.0.1.tar.gz
2)解压该文件
3)在window是执行install-mysql.bat
4)将Dubbo-keeper目录下的target下的mysql-dubbokeeper-ui/dubbokeeper-ui-1.0.1.war拷贝到tomcat的webapps下
5)启动tomcat ,修改上面的war包解压出来后对其中WEB-INF/classes/dubbo.properties文件中的配置项进行调整。
dubbo.application.name=common-monitor dubbo.application.owner=bieber dubbo.registry.address=zookeeper://192.168.128.128:2181 #use netty4 dubbo.reference.client=netty4 #peeper config peeper.zookeepers=192.168.128.128:2181 peeper.zookeeper.session.timeout=60000 #logger monitor.log.home=/monitor-log monitor.collect.interval=6000
6)访问Dubbo-keeper
http://localhost:8080/dubbokeeper-ui-1.0.1
泛化调用
泛化接口调用方式主要用于客户端没有 API 接口及模型类元的状况,参数及返回值中的全部 POJO 均用 Map 表示,一般用于框架集成,好比:实现一个通用的服务测试框架,可经过 GenericService 调用全部服务实现。
经过 Spring 使用泛化调用
在 Spring 配置申明 generic="true":
<dubbo:reference id="barService" interface="com.foo.BarService" generic="true" />
在 Java 代码获取 barService 并开始泛化调用:
GenericService barService = (GenericService) applicationContext.getBean("barService"); Object result = barService.$invoke("sayHello", new String[] { "java.lang.String" }, new Object[] { "World" });
5.分布式架构
当服务集群规模进一步扩大,带动IT治理结构进一步升级,须要实现动态部署,进行流动计算,现有分布式服务架构不会带来阻力。下图是将来可能的一种架构:
节点角色说明
节点 角色说明 Deployer 自动部署服务的本地代理 Repository 仓库用于存储服务应用发布包 Scheduler 调度中心基于访问压力自动增减服务提供者 Admin 统一管理控制台 Registry 服务注册与发现的注册中心 Monitor 统计服务的调用次数和调用时间的监控中心