1. 注解配置
dubbo可以使用注解在生产者端暴露服务接口和在消费端引用接口,只需要在生产者和消费者的配置文件里面配置扫描包路径即可,而不用在xml里面配置需要暴露和引用的接口
扫描包路径的配置
<!-- 扫描注解包路径,多个包用逗号分隔,不填pacakge表示扫描当前ApplicationContext中所有的类 -->
<dubbo:annotation package="com.study.service" />
1.1 在生产者dubbo-provider-web和消费者dubbo-consumer-web新建一个dubbo注解测试的接口
package com.study.service;
/**
*
* @Description: dubbo注解测试的接口
* @author leeSmall
* @date 2018年10月23日
*
*/
public interface AnnotationDubboTest {
public String eat(String param);
}
1.2 在生产者dubbo-provider-web新建一个dubbo注解测试的接口的实现类
package com.study.service;
import com.alibaba.dubbo.config.annotation.Service;
/**
*
* @Description: dubbo注解测试的接口的实现类
* @author leeSmall
* @date 2018年10月23日
*
*/
@Service(timeout = 1000000, version = "1.2.3")
public class AnnotationDubboTestImpl implements AnnotationDubboTest {
public String eat(String param) {
System.out.println("-----------AnnotationDubboTestImpl service test------------"
+ param);
return "-----------AnnotationDubboTestImpl service test------------";
}
}
1.3 在消费端dubbo-consumer-web新建一个测试的control
/**
*
* @Description: dubbo消费端测试control
* @author leeSmall
* @date 2018年10月23日
*
*/
@Controller
@RequestMapping("/common")
public class CommonController implements ApplicationContextAware {
private static Logger logger = Logger.getLogger(CommonController.class);
@Reference(check = false, timeout = 100000, version = "1.2.3")
AnnotationDubboTest annotationdubbo;
@RequestMapping("/annotationdubbo")
public @ResponseBody String annotationdubbo() {
annotationdubbo.eat("我是dubbo的注解测试control");
return "annotationdubbo";
}
}
1.4 在tomcat8080和tomcat8081分别启动生产者和消费者,在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/annotationdubbo访问查看效果
生产者端:
浏览器:
2. 启动时检查
Dubbo缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止Spring初始化完成,以便上线时,能及早发现问题,默认 check="true" 。
可以通过 check="false" 关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动。
另外,如果你的Spring容器是懒加载的,或者通过API编程延迟引用服务,请关闭 check,否则服务临时不可用时,会抛出异常,拿到null引用,如果 check="false" ,总是会返回引用,当服务恢复时,能自动连上。
关闭某个服务的启动时检查 (没有提供者时报错):
<dubbo:reference interface="com.foo.BarService" check="false" />
关闭所有服务的启动时检查 (没有提供者时报错):
<dubbo:consumer check="false" />
3. 集群容错
3.1 Failover Cluster
失败自动切换,缺省值,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。
配置如下:
生产者:
<dubbo:service retries="2" />
消费者:
<dubbo:reference retries="2" />
消费者具体到调用生产者的哪个方法:
<dubbo:reference>
<dubbo:method name="findFoo" retries="2" />
</dubbo:reference>
3.2 Failfast Cluster
快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
配置如下:
生产者:
<dubbo:service cluster="failfast" />
消费者:
<dubbo:reference cluster="failfast" />
3.3 Failsafe Cluster
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
配置如下:
生产者:
<dubbo:service cluster="failsafe" />
消费者:
<dubbo:reference cluster="failsafe" />
3.4 Failback Cluster
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
配置如下:
生产者:
<dubbo:service cluster="failback" />
消费者:
<dubbo:reference cluster="failback" />
3.5 Forking Cluster
并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。
配置如下:
生产者:
<dubbo:service cluster=“forking" />
消费者:
<dubbo:reference cluster=“forking" />
3.6 Broadcast Cluster
广播调用所有提供者,逐个调用,任意一台报错则报错 。通常用于通知所有提供者更新缓存或日志等本地资源信息。
配置如下:
生产者:
<dubbo:service cluster="broadcast" />
消费者:
<dubbo:reference cluster="broadcast" />
4. 负载均衡
4.1 Random LoadBalance
随机,按权重设置随机概率。
在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
配置如下:
<dubbo:service interface="..." loadbalance="random" />
或:
<dubbo:reference interface="..." loadbalance="random" />
或:
<dubbo:service interface="...">
<dubbo:method name="..." loadbalance="random"/>
</dubbo:service>
或:
<dubbo:reference interface="...">
<dubbo:method name="..." loadbalance="random"/>
</dubbo:reference>
4.2 RoundRobin LoadBalance
轮循,按公约后的权重设置轮循比率。
存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
配置如下:
<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>
4.3 ConsistentHash LoadBalance
一致性 Hash,相同参数的请求总是发到同一提供者。
当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
缺省只对第一个参数 Hash,如果要修改,请配置
<dubbo:parameter key="hash.arguments"value="0,1" />
缺省用160份虚拟节点,如果要修改,
请配置 <dubbo:parameter key="hash.nodes" value="320" />
说明:
hash.arguments:当进行调用时候根据调用方法的哪几个参数生成key,并根据key来通过一致性hash算法来选择调用结点。例如调用方法invoke(String s1,String s2); 若hash.arguments为1(默认值),则仅取invoke的参数1(s1)来生成hashCode。
hash.nodes:为结点的副本数
<dubbo:service interface="..." loadbalance="consistenthash" />
或:
<dubbo:reference interface="..." loadbalance="consistenthash" />
或:
<dubbo:service interface="...">
<dubbo:method name="..." loadbalance="consistenthash"/>
</dubbo:service>
或:
<dubbo:reference interface="...">
<dubbo:method name="..." loadbalance="consistenthash"/>
</dubbo:reference>
4.4 LeastActive LoadBalance
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
<dubbo:service interface="..." loadbalance="leastactive" />
或:
<dubbo:reference interface="..." loadbalance="leastactive" />
或:
<dubbo:service interface="...">
<dubbo:method name="..." loadbalance="leastactive"/>
</dubbo:service>
或:
<dubbo:reference interface="...">
<dubbo:method name="..." loadbalance="leastactive"/>
</dubbo:reference>
5. 服务分组
当一个接口有多个实现时,可以用group区分要调用的服务。
5.1 服务分组配置方式实现:
在生产者dubbo-provider-web和消费者dubbo-consumer-web分别新建一个接口
package com.study.test.service;
public interface DubboTestService {
public String eat(String param);
}
在生产者dubbo-provider-web新建两个DubboTestService接口的实现类
实现类1:
package com.study.test.service;
public class DubboTestServiceImpl implements DubboTestService {
public String eat(String param) {
System.out.println("-----------dubbo service test DubboTestServiceImpl ------------" + param);
return "-----------dubbo service test DubboTestServiceImpl ------------";
}
}
实现类2:
package com.study.test.service;
public class DubboTestService1Impl implements DubboTestService {
public String eat(String param) {
System.out.println("-----------dubbo service test DubboTestService1Impl------------" + param);
return "-----------dubbo service test DubboTestService1Impl------------";
}
}
在生产者dubbo-provider-web的applicationProvider.xml配置分组
<!-- 服务分组 -->
<bean id="dubboTestServiceImpl1" class="com.study.test.service.DubboTestServiceImpl"/>
<bean id="dubboTestServiceImpl2" class="com.study.test.service.DubboTestService1Impl"/>
<dubbo:service interface="com.study.test.service.DubboTestService" ref="dubboTestServiceImpl1" group="dubboTestServiceImpl1"/>
<dubbo:service interface="com.study.test.service.DubboTestService" ref="dubboTestServiceImpl2" group="dubboTestServiceImpl2"/>
在消费者dubbo-consumer-web的applicationConsumer.xml配置调用的分组
<!--服务分组 -->
<dubbo:reference id="dubboTestServiceImpl1" interface="com.study.test.service.DubboTestService" check="false" retries="4" cluster="failover" group="dubboTestServiceImpl1"/>
<dubbo:reference id="dubboTestServiceImpl2" interface="com.study.test.service.DubboTestService" check="false" retries="4" cluster="failover" group="dubboTestServiceImpl2"/>
在消费者dubbo-consumer-web的CommonController.java里面创建服务分组测试代码
//服务分组示例begin
@Autowired
@Qualifier("dubboTestServiceImpl1")
DubboTestService dubboService1;
@Autowired
@Qualifier("dubboTestServiceImpl2")
DubboTestService dubboService2;
@RequestMapping("/dubboTest")
public @ResponseBody String dubboTest() {
dubboService1.eat("服务分组示例!");
dubboService2.eat("服务分组示例!");
return "dubboTest";
}
//服务分组示例end
在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/dubboTest访问查看效果
生产者:
浏览器:
5.2 服务分组注解方式实现:
在生产者dubbo-provider-web和消费者dubbo-consumer-web分别新建一个接口
package com.study.service;
public interface UserService {
public String login(String param);
}
在生产者dubbo-provider-web新建两个UserService接口的实现类
实现类1:
package com.study.service;
import org.apache.log4j.Logger;
import com.alibaba.dubbo.config.annotation.Service;
@Service(version = "1.0.2", group = "user2")
public class UserService2Impl implements UserService {
private static Logger logger = Logger.getLogger(UserService2Impl.class);
public String login(String param) {
logger.info("UserService2Impl.login begin!22222");
return "用户已经登录成功!·~~~~~~~~~~~~~~~~~~";
}
}
实现类2:
package com.study.service;
import org.apache.log4j.Logger;
import com.alibaba.dubbo.config.annotation.Service;
@Service(version = "1.0.2", group = "user1")
public class UserServiceImpl implements UserService {
private static Logger logger = Logger.getLogger(UserServiceImpl.class);
public String login(String param) {
logger.info("UserServiceImpl.login begin!");
return "用户已经登录成功!·~~~~~~~~~~~~~~~~~~";
}
}
在消费者dubbo-consumer-web的CommonController.java里面创建服务分组测试代码
//服务分组注解实现示例begin
@Reference(version = "1.0.2", check = false, group = "user1")
UserService usrService;
@Reference(version = "1.0.2", check = false, group = "user2")
UserService usr2Service;
@RequestMapping("/grouplogin1")
public @ResponseBody String login() {
logger.info(usrService.login("服务分组注解实现"));
logger.info(usr2Service.login("服务分组注解实现"));
return "成功";
}
//服务分组注解实现示例end
在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/grouplogin1访问查看效果
生产者:
消费者:
6. 多版本
当一个接口实现出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。
可以按照以下的步骤进行版本迁移:
1)在低压力时间段,先升级一半提供者为新版本
2)再将所有消费者升级为新版本
3)然后将剩下的一半提供者升级为新版本
老版本服务提供者配置:
<dubbo:service interface="com.foo.BarService" version="1.0.0" />
新版本服务提供者配置:
<dubbo:service interface="com.foo.BarService" version="2.0.0" />
老版本服务消费者配置:
<dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" />
新版本服务消费者配置:
<dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" />
如果不需要区分版本,可以按照以下的方式配置 :
<dubbo:reference id="barService" interface="com.foo.BarService" version="*" />
7. 参数验证
在生产者dubbo-provider-web和消费者dubbo-consumer-web的pom.xml里面分别引入如下依赖:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.2.0.Final</version>
</dependency>
在生产者dubbo-provider-web和消费者dubbo-consumer-web分别新建一个参数验证接口和一个参数验证实体
参数验证接口:
package com.study.service;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
/**
*
* @Description: 参数验证接口
* 缺省可按服务接口区分验证场景,如:@NotNull(groups = ValidationService.class)
* @author leeSamll
* @date 2018年10月23日
*
*/
public interface ValidationService {
void save(ValidationParameter parameter);
void update(ValidationParameter parameter);
void delete(
@Min(1) long id,
@NotNull @Size(min = 2, max = 16) @Pattern(regexp = "^[a-zA-Z]+$") String operator);
// 与方法同名接口,首字母大写,用于区分验证场景,如:@NotNull(groups = ValidationService.Save.class),可选
@interface Save {
}
// 与方法同名接口,首字母大写,用于区分验证场景,如:@NotNull(groups = ValidationService.Update.class),可选
@interface Update {
}
}
参数验证实体:
package com.study.service;
import java.io.Serializable;
import java.util.Date;
import javax.validation.constraints.Future;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
/**
*
* @Description: 参数验证实体
* @author leeSamll
* @date 2018年10月23日
*
*/
public class ValidationParameter implements Serializable {
private static final long serialVersionUID = 7158911668568000392L;
// 不允许为空
@NotNull
// 长度或大小范围
@Size(min = 2, max = 20)
private String name;
@NotNull(groups = ValidationService.Save.class)
// 保存时不允许为空,更新时允许为空 ,表示不更新该字段
@Pattern(regexp = "^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$")
private String email;
// 最小值
@Min(18)
// 最大值
@Max(100)
private int age;
// 必须为一个过去的时间
@Past
private Date loginDate;
// 必须为一个未来的时间
@Future
private Date expiryDate;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getLoginDate() {
return loginDate;
}
public void setLoginDate(Date loginDate) {
this.loginDate = loginDate;
}
public Date getExpiryDate() {
return expiryDate;
}
public void setExpiryDate(Date expiryDate) {
this.expiryDate = expiryDate;
}
}
在生产者dubbo-provider-web新建ValidationService接口的实现类
package com.study.service;
/**
*
* @Description: 参数验证接口实现类
* @author leeSamll
* @date 2018年10月23日
*
*/
public class ValidationServiceImpl implements ValidationService {
public void save(ValidationParameter parameter) {
System.out.println("save");
}
public void update(ValidationParameter parameter) {
}
public void delete(long id, String operator) {
System.out.println("delete");
}
}
在生产者dubbo-provider-web的applicationProvider.xml配置参数验证接口:
<!--参数验证begin -->
<bean id="validationService" class="com.study.service.ValidationServiceImpl"/>
<dubbo:service interface="com.study.service.ValidationService" ref="validationService"
validation="true"/>
<!--参数验证end -->
在消费者dubbo-consumer-web的applicationConsumer.xml配置调用的参数验证接口
<!--参数验证begin -->
<dubbo:reference id="validationService" interface="com.study.service.ValidationService"
validation="true"/>
<!--参数验证end -->
在消费者dubbo-consumer-web的CommonController.java里面创建参数验证测试代码
//参数验证begin
@Autowired
ValidationService validationService;
@RequestMapping("/validation")
public @ResponseBody String validation() {
// Save OK
ValidationParameter parameter = new ValidationParameter();
parameter.setName("leeSmall");
parameter.setEmail("[email protected]");
parameter.setAge(50);
parameter.setLoginDate(new Date(System.currentTimeMillis() - 1000000));
parameter.setExpiryDate(new Date(System.currentTimeMillis() + 1000000));
validationService.save(parameter);
System.out.println("Validation Save OK");
// Save Error
try {
parameter = new ValidationParameter();
validationService.save(parameter);
System.err.println("Validation Save ERROR");
}
catch (RpcException e) {
ConstraintViolationException ve = (ConstraintViolationException)e.getCause();
Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();
System.out.println(violations);
}
// Delete OK
validationService.delete(2, "abc");
System.out.println("Validation Delete OK");
// Delete Error
try {
validationService.delete(0, "abc");
System.err.println("Validation Delete ERROR");
}
catch (RpcException e) {
ConstraintViolationException ve = (ConstraintViolationException)e.getCause();
Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();
System.out.println(violations);
}
return "OK";
}
//参数验证end
在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/validation访问查看效果
生产者:
消费者: