文章目录
- 一、在 dubbo 管理控制台配置服务降级
- 1.1、屏蔽的方式:
- 1.2、容错的方式:
- 二、 也可以通过代码,进行服务降级:
- 2.1、向注册中心写入动态配置覆盖规则:(dubbo文档中给出了代码片段)
- 2.2、返回null 或者 简单的值
- 2.3、采用自定义提示
- 2.4、整合 hystrix
- 2.4.1、Provider端:
- 2.4.2、Consumer端
- 三、分析 consumer 端静态配置 mock 服务调用
一、在 dubbo 管理控制台配置服务降级
1.1、屏蔽的方式:
1.2、容错的方式:
上图的配置含义是:consumer 调用 com.zhang.HelloService
的方法时,直接返回 null,不发起远程调用。
实际操作是:在 zk 的 /dubbo/com.zhang.HelloService/configurators
节点中添加了 override。
override://0.0.0.0/com.zhang.HelloService?category=configurators&dynamic=false&group=a&mock=force:return+null
二、 也可以通过代码,进行服务降级:
2.1、向注册中心写入动态配置覆盖规则:(dubbo文档中给出了代码片段)
可以通过服务降级功能,临时屏蔽某个出错的非关键服务,并定义降级后的返回策略。
RegistryFactory registryFactory =
ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181"));
registry.register(URL.valueOf("override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&application=foo&mock=force:return+null"));
- mock=force:return+null
表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。 - mock=fail:return+null
表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
dubbo 服务降级的真实含义:并不是对 provider 进行操作,而是告诉 consumer,调用服务时要做哪些动作。
2.2、返回null 或者 简单的值
配置 mock 参数:
<dubbo:reference id="xxService" check="false" interface="com.xx.XxService" timeout="3000" mock="return null" />
<dubbo:reference id="xxService2" check="false" interface="com.xx.XxService2" timeout="3000" mock="return 1234" />
timeout:超过该时间返回null 或者1234。
配置后启动即可,我们在服务提供者例 如 UserServiceImpl 中的 getUser 方法打个断点来模拟请求超时。
然后浏览器访问,断点不过,一致等待,当时间超过3秒,直接返回了空;ok! 这样我们就已经实现了一个简单的服务降级了 。
2.3、采用自定义提示
consumer.xml 修改如下:
<dubbo:reference id="xxService" check="false" interface="com.xx.XxService" timeout="3000" mock="true"/>
package com.cwh.service;
import java.util.ArrayList;
import java.util.List;
import com.cwh.model.Article;
import com.cwh.model.User;
public class UserServiceMock implements UserService{
public List<Article>getUserArticles(int uid){
return null;
}
public List<User>getUser(String name){
//throw new RuntimeException("服务降级-----");
User user = new User();
user.setUserName("服务降级啦");
user.setUserAge("500");
List<User> list = new ArrayList<User>();
list.add(user);
return list;
}
}
重启服务,然后浏览器访问,断点不过,一致等待,当时间超过3秒,返回如下我们自定义好的提示:
2.4、整合 hystrix
2.4.1、Provider端:
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
Main方法:
@EnableHystrix
@SpringBootApplication
public class BootOrderServiceConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(BootOrderServiceConsumerApplication.class, args);
}
}
增加 @HystrixCommand
注解
@Service //dubbo 服务注解,用于 暴露服务
@Component
public class UserServiceImpl implements UserService {
@HystrixCommand
@Override
public List<UserAddress> getUserAddressList(String userId) {
// TODO Auto-generated method stub
System.out.println("UserServiceImpl..3.....");
UserAddress address1 = new UserAddress(1, "北京市XX", "1", "ZZ", "010-111", "Y");
UserAddress address2 = new UserAddress(2, "深圳市X", "1", "WWW", "010-222", "N");
return Arrays.asList(address1,address2);
}
}
2.4.2、Consumer端
依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
Main方法:
@EnableDubbo
@EnableHystrix
@SpringBootApplication
public class BootOrderServiceConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(BootOrderServiceConsumerApplication.class, args);
}
}
增加 @HystrixCommand(fallbackMethod="hello")
,并实现 hello
方法。
@Service
public class OrderServiceImpl implements OrderService {
@Reference(loadbalance="random",timeout=1000) //dubbo直连
UserService userService;
@HystrixCommand(fallbackMethod="hello")
@Override
public List<UserAddress> initOrder(String userId) {
System.out.println("用户id:"+userId);
//1、查询用户的收货地址
List<UserAddress> addressList = userService.getUserAddressList(userId);
return addressList;
}
public List<UserAddress> hello(String userId) {
return Arrays.asList(new UserAddress(10, "测试地址", "1", "测试", "测试", "Y"));
}
}
三、分析 consumer 端静态配置 mock 服务调用
用户可以在 <dubbo:reference>
中配置 mock 属性。如何配置自定义的mock,还没有搞懂。以 mock="force:return+null"
为例,我们先分析 dubbo 默认的 MockInvoker 。
MockClusterInvoker 逻辑:
// MockClusterInvoker
public Result invoke(Invocation invocation) throws RpcException {
Result result = null;
// 获取<dubbo:reference>的mock属性。
String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
if (value.length() == 0 || value.equalsIgnoreCase("false")){
//mock="" 或者 mock="false"
result = this.invoker.invoke(invocation);
} else if (value.startsWith("force")) {
//强制使用mock,如mock="force:return+null"
if (logger.isWarnEnabled()) {
logger.info("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + directory.getUrl());
}
result = doMockInvoke(invocation, null);
} else {
//mock="fail:return+null" 或 mock="MyMock"
try {
result = this.invoker.invoke(invocation);
}catch (RpcException e) {
if (e.isBiz()) {
throw e;
} else {
if (logger.isWarnEnabled()) {
logger.info("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);
}
//出现超时异常
result = doMockInvoke(invocation, e);
}
}
}
return result;
}
private Result doMockInvoke(Invocation invocation, RpcException e) {
Result result = null;
Invoker<T> minvoker;
List<Invoker<T>> mockInvokers = selectMockInvoker(invocation);
if (mockInvokers == null || mockInvokers.size() == 0){
//如果没有配置自定义Mock,则使用默认MockInvoker
minvoker = (Invoker<T>) new MockInvoker(directory.getUrl());
} else {
minvoker = mockInvokers.get(0);
}
try {
//调用
result = minvoker.invoke(invocation);
} catch (RpcException me) {
if (me.isBiz()) {
result = new RpcResult(me.getCause());
} else {
throw new RpcException(me.getCode(), getMockExceptionMessage(e, me), me.getCause());
}
} catch (Throwable me) {
throw new RpcException(getMockExceptionMessage(e, me), me.getCause());
}
return result;
}
默认的MockInvoker:
// MockInvoker
public Result invoke(Invocation invocation) throws RpcException {
//获取url中的sayHello.mock参数值
String mock = getUrl().getParameter(invocation.getMethodName() + "." + Constants.MOCK_KEY);
if (invocation instanceof RpcInvocation) {
((RpcInvocation) invocation).setInvoker(this);
}
if (StringUtils.isBlank(mock)){
//获取mock属性值
mock = getUrl().getParameter(Constants.MOCK_KEY);
}
if (StringUtils.isBlank(mock)){
throw new RpcException(new IllegalAccessException("mock can not be null. url :" + url));
}
//假定mock="force:return+null",处理后mock="return+null"
mock = normallizeMock(URL.decode(mock));
if (Constants.RETURN_PREFIX.trim().equalsIgnoreCase(mock.trim())){
RpcResult result = new RpcResult();
result.setValue(null);
return result;
} else if (mock.startsWith(Constants.RETURN_PREFIX)) {
//构造返回值
mock = mock.substring(Constants.RETURN_PREFIX.length()).trim();
mock = mock.replace('`', '"');
try {
Type[] returnTypes = RpcUtils.getReturnTypes(invocation);
Object value = parseMockValue(mock, returnTypes);
return new RpcResult(value);
} catch (Exception ew) {
throw new RpcException("mock return invoke error. method:" + invocation.getMethodName() + ", mock:" + mock + ", url: "+ url , ew);
}
} else if (mock.startsWith(Constants.THROW_PREFIX)) {
mock = mock.substring(Constants.THROW_PREFIX.length()).trim();
mock = mock.replace('`', '"');
if (StringUtils.isBlank(mock)){
throw new RpcException(" mocked exception for Service degradation. ");
} else { //用户自定义类
Throwable t = getThrowable(mock);
throw new RpcException(RpcException.BIZ_EXCEPTION, t);
}
} else { //impl mock
try {
Invoker<T> invoker = getInvoker(mock);
return invoker.invoke(invocation);
} catch (Throwable t) {
throw new RpcException("Failed to create mock implemention class " + mock , t);
}
}
}
//mock=fail:throw
//mock=fail:return
//mock=xx.Service
private String normallizeMock(String mock) {
if (mock == null || mock.trim().length() ==0){
return mock;
} else if (ConfigUtils.isDefault(mock) || "fail".equalsIgnoreCase(mock.trim()) || "force".equalsIgnoreCase(mock.trim())){
mock = url.getServiceInterface()+"Mock";
}
if (mock.startsWith(Constants.FAIL_PREFIX)) {
mock = mock.substring(Constants.FAIL_PREFIX.length()).trim();
} else if (mock.startsWith(Constants.FORCE_PREFIX)) {
mock = mock.substring(Constants.FORCE_PREFIX.length()).trim();
}
return mock;
}