基于 Spring Boot Starter 开发微服务应用
https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/quick-start/spring-boot/
Dubbo Demo
https://github.com/apache/dubbo-samples
高级特性和用法
框架与服务
1 版本与分组
Dubbo服务中,接口并不能唯一确定一个服务,只有 接口+分组+版本号
的三元组才能唯一确定一个服务。
- 当同一个接口针对不同的业务场景、不同的使用需求或者不同的功能模块等场景,可使用服务分组来区分不同的实现方式。同时,这些不同实现所提供的服务是可并存的,也支持互相调用。
- 当接口实现需要升级又要保留原有实现的情况下,即出现不兼容升级时,我们可以使用不同版本号进行区分。
使用方式
使用 @DubboService 注解,配置 group
参数和 version
参数:
生产者
//接口定义
public interface DevelopService {String invoke(String param);
}//接口实现1 都是同一个接口不同是的实现类
@DubboService(group = "group1", version = "1.0")
public class DevelopProviderServiceV1 implements DevelopService{@Overridepublic String invoke(String param) {StringBuilder s = new StringBuilder();s.append("ServiceV1 param:").append(param);return s.toString();}
}//接口实现2
@DubboService(group = "group2", version = "2.0")
public class DevelopProviderServiceV2 implements DevelopService{@Overridepublic String invoke(String param) {StringBuilder s = new StringBuilder();s.append("ServiceV2 param:").append(param);return s.toString();}
}
消费者
@DubboReference(group = "group1", version = "1.0")
private DevelopService developService;@DubboReference(group = "group2", version = "2.0")
private DevelopService developServiceV2;//group值为*,标识匹配任意服务分组
@DubboReference(group = "*")
private DevelopService developServiceAny;@Override
public void run(String... args) throws Exception {//调用DevelopService的group1分组实现System.out.println("Dubbo Remote Return ======> " + developService.invoke("1"));//调用DevelopService的另一个实现System.out.println("Dubbo Remote Return ======> " + developServiceV2.invoke("2"));
}
2 参数校验
特性说明
参数验证功能是基于 JSR303 实现的,用户只需标识 JSR303 标准的验证 annotation,并通过声明 filter 来实现验证。
Maven 依赖
<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>
使用场景
服务端在向外提供接口服务时,解决各种接口参数校验问题。
参考用例 https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-validation
使用方式
参数标注示例
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;public class ValidationParameter implements Serializable {private static final long serialVersionUID = 7158911668568000392L;@NotNull // 不允许为空@Size(min = 1, 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;}
}
分组验证示例
public interface ValidationService { // 缺省可按服务接口区分验证场景,如:@NotNull(groups = ValidationService.class) @interface Save{} // 与方法同名接口,首字母大写,用于区分验证场景,如:@NotNull(groups = ValidationService.Save.class),可选void save(ValidationParameter parameter);void update(ValidationParameter parameter);
}
方法中参数验证示例
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;public interface ValidationService {void save(@NotNull ValidationParameter parameter); // 验证参数不为空void delete(@Min(1) int id); // 直接对基本类型参数验证
}
3 集群容错
在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。
各节点关系:
- 这里的
Invoker
是Provider
的一个可调用Service
的抽象,Invoker
封装了Provider
地址及Service
接口信息 Directory
代表多个Invoker
,可以把它看成List<Invoker>
,但与List
不同的是,它的值可能是动态变化的,比如注册中心推送变更Cluster
将Directory
中的多个Invoker
伪装成一个Invoker
,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个Router
负责从多个Invoker
中按路由规则选出子集,比如读写分离,应用隔离等LoadBalance
负责从多个Invoker
中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选
Failover Cluster
失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries="2"
来设置重试次数(不含第一次)。
重试次数配置如下:
<dubbo:service retries="2" />或<dubbo:reference retries="2" />或<dubbo:reference><dubbo:method name="findFoo" retries="2" />
</dubbo:reference>
Failsafe Cluster 失败安全
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
Failback Cluster 失败自动恢复
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
Forking Cluster 并行调用多个服务器
并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2"
来设置最大并行数。
Broadcast Cluster 广播调用
广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。
现在广播调用中,可以通过 broadcast.fail.percent 配置节点调用失败的比例,当达到这个比例后,BroadcastClusterInvoker 将不再调用其他节点,直接抛出异常。 broadcast.fail.percent 取值在 0~100 范围内。默认情况下当全部调用失败后,才会抛出异常。 broadcast.fail.percent 只是控制的当失败后是否继续调用其他节点,并不改变结果(任意一台报错则报错)。broadcast.fail.percent 参数 在 dubbo2.7.10 及以上版本生效。
Broadcast Cluster 配置 broadcast.fail.percent。
broadcast.fail.percent=20 代表了当 20% 的节点调用失败就抛出异常,不再调用其他节点。
@reference(cluster = "broadcast", parameters = {"broadcast.fail.percent", "20"})
集群模式配置
按照以下示例在服务提供方和消费方配置集群模式
<dubbo:service cluster="failsafe" />
或
<dubbo:reference cluster="failsafe" />
4 服务降级
Dubbo服务降级通常是指在服务不可用或负载过高时,为某些服务调用提供备选方案,以防止系统崩溃或性能下降。在Dubbo中,可以使用Mock
机制来实现服务降级。
Mock伪造返回数据,可用作服务降级
- 强制返回null
<dubbo:reference id="demoService" interface="com.example.DemoService" mock="return null"/>
- 这种方式强制执行本地伪装逻辑,即使远程调用正常也会执行降级逻辑。
- 一般用这种,直接降级
<dubbo:reference id="demoService" interface="com.example.DemoService" mock="force:true"/>
或者
@DubboService(version = "1.0.0",timeout = 3000, mock = "force:true")
- 1 mock = force:return+null:强制服务返回null,不会进行RPC调用
- 2 mock = fail:return+null:调用服务失败后返回null,会进行RPC调用。
- 3 mock = throw:直接跑RpcException,不会RPC调用
以下是使用Dubbo Mock机制的步骤:
-
实现
Mock
接口。 -
配置
Mock
规则。
1 定义一个服务接口:public interface MyService {String sayHello(String name);
}
2 实现Mock接口:public class MyServiceMock implements MyService {@Overridepublic String sayHello(String name) {return "Mocked: Hello, " + name;}
}
3 在provider的配置文件中添加Mock规则:<dubbo:service interface="MyService" ref="myService" mock="MyServiceMock"/>
或者在注解配置中使用:@Service(version = "1.0.0", mock = "MyServiceMock")
public class MyServiceImpl implements MyService {// ...
}
当MyService
不可用时,Dubbo会自动使用MyServiceMock
的实现。
注意:
-
确保
Mock
类和服务接口在同一个ClassLoader
下。 -
如果使用注解配置,请确保
Mock
类在服务提供者的类路径下。 -
如果服务提供者不可用,Dubbo也会使用
Mock
。
以上是Dubbo服务降级的一种简单实现方式,具体的降级策略可能需要根据实际情况进行定制。