Dubbo學習:服務降級

一、dubbo降級服務    


    dubbo開發中,可能由於服務沒有啓動或者網絡不通,調用中會出現RpcException,也就是遠程調用失敗。如果是服務啓動順序的問題,可能加工check="false"的配置可以得到很好的解決。但是,如果是服務宕掉或者併發數太高導致的RpcException該如何處理?


    經過過12306搶票的人應該經常會遇到這個問題:在搶票高峯的時候,明明票還有,但是查詢出來的列表卻是爲空的(如果沒票列表也應該會呈現);等高峯過後再查詢,列表又恢復正常。個人猜測應該是查詢過程中出現了問題,要麼超時,要麼網絡問題導致查詢失敗採用的服務降級處理。所以,最終呈現給用戶的並不是內部系統出錯之類的提示,而是一個空的列表。好了,言歸正傳,在dubbo中想實現服務降級,需要怎麼樣做可以實現?


    查看dubbo的官方文檔,可以發現有個mock的配置,mock只在出現非業務異常(比如超時,網絡異常等)時執行。mock的配置支持兩種,一種爲boolean值,默認的爲false。如果配置爲true,則缺省使用mock類名,即類名+Mock後綴;另外一種則是配置"return null",可以很簡單的忽略掉異常。

--------------------- 

二、結合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;
        
            }
        
        }

dubbo-provider.xml配置:

<?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。


三、思考


    通過以上的例子可以知道,通過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;
    
        }

    }
 --------------------- 
    作者:傑布斯 
    來源:CSDN 
    原文:https://blog.csdn.net/ZuoAnYinXiang/article/details/51251836 
    版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章