分佈式專題-分佈式服務治理01-揭開Dubbo的神祕面紗

前言

在傳統的遠程調用,比如RMI、HTTP協議、WebService等確實能夠滿足遠程調用關係,但是隨着用戶量的倍增以及系統的複雜性增加,傳統的遠程調用卻滿足不了服務治理的需求:

  • 地址維護
  • 負載均衡
  • 限流/容錯/降級
  • 監控

所以在這一部分,我們引入Dubbo來實現服務治理,我們從三方面講Dubbo這個RPC遠程調用框架。

  • 解開Dubbo的神祕面紗
  • 分佈式服務治理Dubbo常用配置
  • 分佈式服務治理Dubbo源碼分析

本節我們講第一個部分:解開Dubbo的神祕面紗

架構的發展

傳統互聯網架構
還記得阿里最初的項目是什麼樣的結構麼?就是LAMP(即,Linux+Apache+MySQL+PHP),Tomcat作爲web容器,放置的單體項目包括整套業務的所有邏輯,用戶服務,訂單服務,支付服務等~
在這裏插入圖片描述
分佈式架構的演進
如今互聯網比較完善的體系就是將業務分離,服務分離,數據庫主從設計,讀寫分離。
在這裏插入圖片描述

帶來哪些問題

(1)當服務越來越多時,服務 URL 配置管理變得非常困難,F5 硬件負載均衡器的單點壓力也越來越大。

此時需要一個服務註冊中心,動態的註冊和發現服務,使服務的位置透明。

並通過在消費方獲取服務提供方地址列表,實現軟負載均衡和 Failover,降低對 F5 硬件負載均衡器的依賴,也能減少部分成本。

(2)當進一步發展,服務間依賴關係變得錯蹤複雜,甚至分不清哪個應用要在哪個應用之前啓動,架構師都不能完整的描述應用的架構關係。

這時,需要自動畫出應用間的依賴關係圖,以幫助架構師理清理關係。

(3)服務的調用量越來越大,服務的容量問題就暴露出來,這個服務需要多少機器支撐?什麼時候該加機器?

爲了解決這些問題,第一步,要將服務現在每天的調用量,響應時間,都統計出來,作爲容量規劃的參考指標。
其次,要可以動態調整權重,在線上,將某臺機器的權重一直加大,並在加大的過程中記錄響應時間的變化,直到響應時間到達閥值,記錄此時的訪問量,再以此訪問量乘以機器數反推總容量。

Dubbo的架構

dubbo架構圖
老生常談,隨手掏來一個經典的dubbo架構圖,圖示上已經寫的很清楚了,一共有0~5六個部分:

  1. start

provider服務提供者:服務啓動

  1. register

provider服務提供者然後註冊register服務

  1. subscribe

Consumer服務消費者:消息訂閱subscribe、

  1. notify

註冊中心會將這些服務通過notify到消費者

  1. invoke

invoke這條實線按照圖上的說明當然同步的意思了
服務消費者隨機調用一個服務地址,失敗重試另一個地址

  1. count

這是一個監控,圖中虛線表明Consumer 和Provider通過異步的方式發送消息至Monitor。Monitor在整個架構中是可選的,Monitor功能需要單獨配置,不配置或者配置以後,Monitor掛掉並不會影響服務的調用。

Dubbo 案例演示

接下來,我們簡單的使用一下dubbo~

  • 服務端

我這裏使用server-api和server-provider作爲服務端兩個模塊
在這裏插入圖片描述

  • server-api:服務調用接口
  • server-provider:服務提供者

在server-api中,就是空實現,用來調用server-provider:
接口1:

public interface IGpHello {

    String sayHello(String msg);
}

接口2:

public interface IDemoService {

    String protocolDemo(String msg);
}

真正的實現就是在server-provider中實現:
接口1實現:

public class GpHelloImpl implements IGpHello{

    @Override
    public String sayHello(String msg) {
        return "Hello:"+msg;
    }
}

接口2實現:

public class GpHelloImpl2 implements IGpHello{

    @Override
    public String sayHello(String msg) {
        return "Hello,i'm server 2:"+msg;
    }
}

在dubbo-server的pom文件引用dubbo的依賴,zookeeper等依賴,然後創建配置文件dubbo-server.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="dubbo-server" owner="mic"/>

    <!--註冊中心-->
    <dubbo:registry id="zk1" address="zookeeper://192.168.200.111:2181"/>

    <dubbo:registry id="zk2" address="zookeeper://192.168.200.112:2181"/>

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

    <dubbo:protocol port="8080" name="hessian"/>

    <dubbo:service interface="com.test.dubbo.IGpHello"
                   ref="gpHelloService" protocol="dubbo,hessian" registry="zk1"/>

    <dubbo:service interface="com.test.dubbo.IDemoService"
                   ref="demoService" protocol="hessian"/>

    <bean id="gpHelloService" class="com.test.dubbo.GpHelloImpl"/>

    <bean id="demoService" class="com.test.dubbo.DemoService"/>
</beans>

服務端調用就加載這個配置文件:

    public static void main(String[] args) throws IOException {
        ClassPathXmlApplicationContext context=
                new ClassPathXmlApplicationContext
                        ("META-INF/spring/dubbo-server.xml");
        context.start();

        System.in.read(); //阻塞當前進程
    }
  • 客戶端

配置文件:

<?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-client" owner="mic"/>

    <!--註冊中心-->
    <dubbo:registry address="zookeeper://192.168.200.111:2181?register=false" check="false" file="d:/dubbo-server"/>


    <dubbo:reference id="gpHelloService"
                     interface="com.test.dubbo.IGpHello"
                     protocol="dubbo"/>


</beans>

啓動:

    public static void main( String[] args ) throws IOException, InterruptedException {
        ClassPathXmlApplicationContext context=new
                ClassPathXmlApplicationContext
                ("dubbo-client.xml");

      /*  Protocol protocol=ExtensionLoader.getExtensionLoader(Protocol.class).
                getExtension("defineProtocol");
        System.out.println(protocol.getDefaultPort());*/

           //得到IGpHello的遠程代理對象
            IGpHello iGpHello = (IGpHello) context.getBean("gpHelloService");

            System.out.println(iGpHello.sayHello("Mic"));
            Thread.sleep(4000);

        System.in.read();
    }

在這裏插入圖片描述
dubbo節點分析
上面的demo中,使用dubbo通信成功,我們注意到,此時,dubbo在註冊中心創建了“dubbo”的節點
在這裏插入圖片描述
在這裏插入圖片描述
關於dubbo的緩存
注意到:在客戶端其實是有緩存的概念的,這樣不一定就讓client每次都請求到zookeeper
在這裏插入圖片描述
運行後,我們在本地磁盤找到了的緩存文件
在這裏插入圖片描述
實際上,在dubbo的源碼裏,通過定時任務去跑數據,更新緩存,這點我們在後面的dubbo源碼分析章節會講到

服務的啓動過程
我們也可以通過dubbo提供main方法啓動容器

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

        //默認情況下會使用spring容器來啓動服務
        com.alibaba.dubbo.container.Main.main(
                new String[]{"spring","log4j"});
    }

看看源碼:

    public static void main(String[] args) {
        try {
            if (args == null || args.length == 0) {
                String config = ConfigUtils.getProperty("dubbo.container", loader.getDefaultExtensionName());
                args = Constants.COMMA_SPLIT_PATTERN.split(config);
            }

            final List<Container> containers = new ArrayList();

            for(int i = 0; i < args.length; ++i) {
                containers.add(loader.getExtension(args[i]));
            }

            logger.info("Use container type(" + Arrays.toString(args) + ") to run dubbo serivce.");
            if ("true".equals(System.getProperty("dubbo.shutdown.hook"))) {
                Runtime.getRuntime().addShutdownHook(new Thread() {
                    public void run() {
                        Iterator i$ = containers.iterator();

                        while(i$.hasNext()) {
                            Container container = (Container)i$.next();

                            try {
                                container.stop();
                                Main.logger.info("Dubbo " + container.getClass().getSimpleName() + " stopped!");
                            } catch (Throwable var6) {
                                Main.logger.error(var6.getMessage(), var6);
                            }

                            Class var3 = Main.class;
                            synchronized(Main.class) {
                                Main.running = false;
                                Main.class.notify();
                            }
                        }

                    }
                });
            }

            Iterator i$ = containers.iterator();

            while(i$.hasNext()) {
                Container container = (Container)i$.next();
                container.start();
                logger.info("Dubbo " + container.getClass().getSimpleName() + " started!");
            }

            System.out.println((new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]")).format(new Date()) + " Dubbo service server started!");
        } catch (RuntimeException var7) {
            var7.printStackTrace();
            logger.error(var7.getMessage(), var7);
            System.exit(1);
        }

        Class var9 = Main.class;
        synchronized(Main.class) {
            while(running) {
                try {
                    Main.class.wait();
                } catch (Throwable var5) {
                    ;
                }
            }

        }
    }

說明dubbo支持多容器啓動

dubbo支持多協議

  • RMI
  • hessian
  • webservice
  • http
  • thirft
  • Dubbo(默認)

優勢:
1、不需要修改原本的服務的情況下,方便協議的遷移
2、通過增加相應協議的jar包,快速發佈

dubbo支持多註冊中心

<?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-server" owner="mic"/>

    <!--註冊中心-->
    <dubbo:registry id="zk1" address="zookeeper://192.168.200.111:2181"/>

    <dubbo:registry id="zk2" address="zookeeper://192.168.200.112:2181"/>

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

    <dubbo:protocol port="8080" name="hessian"/>

    <dubbo:service interface="com.test.dubbo.IGpHello"
                   ref="gpHelloService" protocol="dubbo,hessian" registry="zk1"/>

    <dubbo:service interface="com.test.dubbo.IDemoService"
                   ref="demoService" protocol="hessian"/>

    <bean id="gpHelloService" class="com.test.dubbo.GpHelloImpl"/>

    <bean id="demoService" class="com.test.dubbo.DemoService"/>
</beans>

dubbo基於集羣訪問
對dubbo-server做負載均衡
在這裏插入圖片描述

後記

本節具體代碼詳見:
dubbo-demo

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