Dubbo-服务降级

1.开关

        先讲一下开关的由来,例如京东在6月18日做店庆促销活动,在交易下单环节,可能需要调用A、B、C三个接口来完成,但是其实A和B是必须的,C只是附加的功能(例如在下单的时候做一下推荐),可有可无,在平时系统没有压力,容量充足的情况下,调用下没问题,但是在类似店庆之类的大促环节,系统已经满负荷了,这时候其实完全可以不去调用C接口,怎么实现这个呢?改代码?no,no,no,这样太不敏捷,此时开关诞生了,开发人员只要简单执行一下命令或者点一下页面,就可以关掉对于C接口的调用,在大促过去之后,再把开关恢复回去即可。

2.什么是服务降级

        服务降级,当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。

3.服务降级方式

  • 服务接口拒绝服务:无用户特定信息,页面能访问,但是添加删除提示服务器繁忙。页面内容也可在Varnish或CDN内获取。
  • 页面拒绝服务:页面提示由于服务繁忙此服务暂停。跳转到varnish或nginx的一个静态页面。
  •  延迟持久化:页面访问照常,但是涉及记录变更,会提示稍晚能看到结果,将数据记录到异步队列或log,服务恢复后执行。
  •  随机拒绝服务:服务接口随机拒绝服务,让用户重试,目前较少有人采用。因为用户体验不佳。

4.Dubbo降级服务

        查看dubbo的官方文档,可以发现有个mock的配置,mock只在出现非业务异常(比如超时,网络异常等)时执行。mock的配置支持两种,一种为boolean值,默认的为false。如果配置为true,则缺省使用mock类名,即类名+Mock后缀;另外一种则是配置"return null",可以很简单的忽略掉异常。

4.1 结合dubbo的例子

定义接口:

/**接口定义*/

public interface IUser {

  

    public void addUser(User u);

      

    public User getUserById(int id);

      

}

接口实现类:

/**实现类*/

public class UserImpl implements IUser {

      

    private static List<User> USER_LIST = new ArrayList<User>();

      

    static{

        for(int i=0;i<10;i++){

            User u = new User();

            u.setAddress("address"+i);

            u.setId(i);

            u.setName("name"+i);

              

            USER_LIST.add(u);

        }

    }

      

    public void addUser(User u) {

        USER_LIST.add(u);

        System.out.println("total:"+USER_LIST.size());

    }

  

    public User getUserById(int id) {

        for(int i=0;i<USER_LIST.size();i++){

            if(USER_LIST.get(i).getId() == id){

                return USER_LIST.get(i);

            }

        }

        return null;

    }

}

服务提供者配置:

<?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="hello-world-app" />

 

    <!-- 使用multicast广播注册中心暴露服务地址 -->

    <dubbo:registry address="zookeeper://127.0.0.1:2181" />

 

    <!-- 用dubbo协议在20880端口暴露服务 -->

    <dubbo:protocol name="dubbo" port="20880" />

 

    <!-- 声明需要暴露的服务接口 -->

    <dubbo:service interface="com.zzq.test.iface.IUser" ref="userImpl" timeout="10000" />

 

    <!-- 和本地bean一样实现服务 -->

    <bean id="userImpl" class="com.zzq.test.ifaceimpl.UserImpl" />

 

</beans>

服务调用方配置:

<?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="dubbo-consumer"  />

  

    <dubbo:registry address="zookeeper://127.0.0.1:2181" />

  

    <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->

    <dubbo:reference id="iUser" interface="com.zzq.test.iface.IUser"  timeout="10000" check="false" mock="return null">

    </dubbo:reference>

  

</beans>

调用的测试代码:


public static void main(String[] args) throws Exception{

        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"classpath:dubbo-consumer.xml"});

        context.start();

         

        IUser iUser = (IUser)context.getBean("iUser");

        User u = new User();

        u.setAddress("aaa");

        u.setId(311);

        u.setName("n3");

        iUser.addUser(u);

        System.out.println(iUser.getUserById(1));

    }

通过测试,如果服务启动,则程序按照预期的运行正常;如果服务没启动,则此时运行程序,程序并未报错,打印出null。

4.2 思考

        通过以上的例子可以知道,通过mock的配置,可以很好的实现dubbo服务降级。但是,仔细查看上面的例子会发现,IUser本身定义了两个接口,一个是新增用户,一个是根据id查询用户信息。对于根据id查询用户信息,在调用失败的时候返回null很好理解,可能是由于验证失败或者记录删除了,但是对于新增用户,可能就需要抛出具体的业务信息,否则程序无法处理后续的业务,包括页面弹出”添加成功“或者列表刷新的时候无法查看到最新的记录,这样体验将会非常不好。所以,如果要有较好的区分,可以通过以下的方式,可以更好的实现降级:

(1)将接口进行归类,查询类和变更操作类:对于查询的分为一个接口类,变更的归类为其他的接口类,这样对于查询的可以使用mock="return null"进行降级操作;对于变更类的,可以仍旧使用try……catch进行异常捕获处理;

(2)配置mock="true",同时mock实现接口,接口名要注意命名规范:接口名+Mock后缀。此时如果调用失败会调用Mock实现。mock实现需要保证有无参的构造方法。

        配置mock="true"的情况,对于上面的例子即在IUser的同个路径下,添加类IUserMock,实现如下:

public class IUserMock implements IUser {

 

    @Override

    public void addUser(User u) {

        throw new RuntimeException("add user fail!");

    }

 

    @Override

    public User getUserById(int id) {

        return null;

    }

 

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值