Java-分布式框架-Dubbo-3

一、分布式項目開發(fā)與聯(lián)調(diào)

接口暴露與引用
image.png

暴露接口的通常做法是 接口與實現(xiàn)分離,服務(wù)端將 接口蠢琳、模型、異常 等統(tǒng)一放置于一個模塊咬最,實現(xiàn)置于另一個模塊圈驼。調(diào)用方通過Maven進行引用玉锌。

注意:在分布式項目中,不會把整個服務(wù)提供方打包成JAR并提供給消費端憔维,而是選擇單獨把接口與實體類打包成JAR提供給消費端章鲤。

自動化構(gòu)建與協(xié)作

當(dāng)項目越來越多,服務(wù)依懶關(guān)系越發(fā)復(fù)雜的時候枯途,為了提高協(xié)作效率灰瞻,必須采用自動化工具 完成 接口從編寫到構(gòu)建成JAR包构回,最后到引用的整個過程。


image.png

流程描述:

  1. 服務(wù)提供者項目發(fā)人員編寫Client 接口
  2. push 至遠程倉庫
  3. jenkins 構(gòu)建指定版本
  4. jenkins Deploye 至私服倉庫 nexus
  5. 服務(wù)消費者項目開發(fā)人員基于maven 從私服務(wù)倉庫下載
接口平滑升級

在項目迭代過程當(dāng)中京腥, 經(jīng)常會有多個項目依懶同一個接口,如下圖 項目B、C都依懶了項目A當(dāng)中的接口1鸿市,此時項目B業(yè)務(wù)需要验游,需要接口1多增加一個參數(shù),升級完成后砸王。項目B能正確構(gòu)建上線,項目C卻不行默穴。


image.png

解決辦法與原則:

  1. 接口要做到向下兼容:接口參數(shù)盡量以對象形式進行封裝膊爪。Model屬性只增不刪叫确,如果需要作廢,可以添加@Deprecated 標識杏慰。
  2. 如果出現(xiàn)了不可兼容的變更凹耙,則必須通知調(diào)用方整改意述,并制定上線計劃瓣戚。
-- 不推薦仓技,兼容性差
public interface UserService {
    User getUser(Integer id, string name);
}
-- 推薦
public interface UserService {
    User getUser(UserParam param);
    public static class UserParam
    {
        Integer id;
        string name;
    }
}

二诉濒、Dubbo控制管理后臺使用

Dubbo 控制后臺的安裝
#從github 中下載dubbo 項目
git clone https://github.com/apache/incubator-dubbo.git
#更新項目
git fetch
#臨時切換至 dubbo-2.5.8 版本
git checkout dubbo-2.5.8
#進入 dubbo-admin 目錄
cd dubbo-admin
#mvn 構(gòu)建admin war 包
mvn clean pakcage -DskipTests
#得到 dubbo-admin-2.5.8.war 即可直接部署至Tomcat
#修改 dubbo.properties 配置文件
dubbo.registry.address=zookeeper://127.0.0.1:2181

三迫卢、Dubbo注冊中心詳解

為了到達服務(wù)集群動態(tài)擴容的目的上荡,注冊中心存儲了服務(wù)的地址信息與可用狀態(tài)信息塞祈,并實時推送給訂閱了相關(guān)服務(wù)的客戶端哼御。


image.png

一個完整的注冊中心需要實現(xiàn)以下功能:

  1. 接收服務(wù)端的注冊與客戶端的引用嗦哆,即將引用與消費建立關(guān)聯(lián),并支持多對多鬓梅。
  2. 當(dāng)服務(wù)非正常關(guān)閉時能即時清除其狀態(tài)
  3. 當(dāng)注冊中心重啟時物遇,能自動恢復(fù)注冊數(shù)據(jù)眶根,以及訂閱請求
  4. 注冊中心本身的集群
Zookeeper 注冊中心

關(guān)于Zookeeper 注冊中心同樣需要了解其存儲結(jié)構(gòu)和更新機制。
Zookeper是一個樹型的目錄服務(wù),本身支持變更推送相比redis的實現(xiàn)Publish/Subscribe功能更穩(wěn)定厚骗。

image.png

注意:其中葉子節(jié)點為臨時節(jié)點刨仑。

源碼解析
image.png

注意:UserService是一個代理對象士八,由ReferenceConfig引用對象生成,并把ClusterInvoker阻课、RegistryDirectory賦予給它。

四、Dubbo調(diào)用模塊

dubbo調(diào)用模塊核心功能是發(fā)起一個遠程方法的調(diào)用并順利拿到返回結(jié)果垒棋,其體系組成如下:

  1. 透明代理:通過動態(tài)代理技術(shù)甜无,屏蔽遠程調(diào)用細節(jié)以提高編程友好性。這里dubbo 使用了 javassist作為代理實現(xiàn)评姨。
  2. 負載均衡:當(dāng)有多個提供者是,如何選擇哪個進行調(diào)用的負載算法阻星。
  3. 容錯機制:當(dāng)服務(wù)調(diào)用失敗時采取的策略
  4. 調(diào)用方式:支持同步調(diào)用、異步調(diào)用
負載均衡

Dubbo 目前官方支持以下負載均衡策略:

  1. 隨機(random):按權(quán)重設(shè)置隨機概率印颤。此為默認算法.
  2. 輪循 (roundrobin):按公約后的權(quán)重設(shè)置輪循比率。
  3. 最少活躍調(diào)用數(shù)(leastactive):相同活躍數(shù)的隨機衣迷,活躍數(shù)指調(diào)用前后計數(shù)差陨界。
  4. 一致性Hash(consistenthash ):相同的參數(shù)總是發(fā)到同一臺機器录淡,默認初始化160個虛擬點窍帝,相對hash取余的方式,一致性Hash避免了單一服務(wù)過熱以及節(jié)點數(shù)量變化后全局亂套的缺點突梦。


    image.png
容錯

Dubbo 官方目前支持以下容錯策略:

  1. 失敗自動切換:調(diào)用失敗后基于retries=“2” 屬性重試其它服務(wù)器
  2. 快速失敗:快速失敗礼预,只發(fā)起一次調(diào)用,失敗立即報錯扮休。
  3. 勿略失敗:失敗后勿略凳兵,不拋出異常給客戶端。
  4. 失敗重試:失敗自動恢復(fù),后臺記錄失敗請求,定時重發(fā)丈探。通常用于消息通知操作
  5. 并行調(diào)用: 只要一個成功即返回,并行調(diào)用指定數(shù)量機器,可通過 forks="2" 來設(shè)置最大并行數(shù)侣监。
  6. 廣播調(diào)用:廣播調(diào)用所有提供者遮咖,逐個調(diào)用诀蓉,任意一臺報錯則報錯
異步調(diào)用

異步調(diào)用是指發(fā)起遠程調(diào)用之后獲取結(jié)果的方式鲤孵。

  1. 同步等待結(jié)果返回(默認)
  2. 異步等待結(jié)果返回
  3. 不需要返回結(jié)果
    Dubbo 中關(guān)于異步等待結(jié)果返回的實現(xiàn)流程如下圖:


    image.png
demoService.sayHello1("han");
Future<Object> future1 = RpcContext.getContext().getFuture();  //底層ThreadLocal
demoService.sayHello2("han2");
Future<Object> future2 = RpcContext.getContext().getFuture();
Object r1 = null, r2 = null;
// wait 直到拿到結(jié)果 獲超時
r1 = future1.get();  //同步
// wait 直到拿到結(jié)果 獲超時
r2 = future2.get();  //同步

注意:有返回值的時候,future.get()方法執(zhí)行的是同步,提升效率的關(guān)鍵在于多個future.get()方法同時執(zhí)行時,全部完成的時間是future.get()方法最慢的那一個,而不是多個future.get()方法的總和。
注意:同步調(diào)用底層也使用了Future阱穗,與異步不同的是鲁僚,同步對異步多了一層包裝冰沙,里面使用了future.get()方法科展,一個線程死循環(huán)在訪問結(jié)果是否有值逗抑,一個線程在檢測線程是否超時。

五鳄梅、Dubbo 調(diào)用非典型使用場景

泛化提供

是指不通過接口的方式直接將服務(wù)暴露出去叠国。通常用于Mock框架或服務(wù)降級框架實現(xiàn)。
模擬出來的通用服務(wù)提供方

public class MockService implements GenericService {
    private String target;

    public MockService(String target) {
        this.target = target;
    }

    // 通用方法
    @Override
    public Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException {
        if (target.equals("com.lin.client.UserService") && method.equals("getUser")) {
            HashMap<Object, Object> map = new HashMap<>();
            map.put("id", 1);
            map.put("name", "克里斯");
            return map;
        } 
        return null;
    }
}
public class DubboServer {
    public static void main(String[] args) throws IOException {
        ApplicationConfig applicationConfig = new ApplicationConfig("sample-app");
        ProtocolConfig protocolConfig = new ProtocolConfig();
        protocolConfig.setName("dubbo");
        protocolConfig.setSerialization("fastjson");
        protocolConfig.setPort(-1);//20880
        RegistryConfig registryConfig = new RegistryConfig("zookeeper://192.168.0.147:2181");

        ServiceConfig serviceConfig = new ServiceConfig();
        serviceConfig.setInterface("com.tuling.client.UserService");
        //serviceConfig.setRef(new UserServiceImpl());
        setMock("com.lin.client.UserService");
        serviceConfig.setRegistry(registryConfig);
        serviceConfig.setProtocol(protocolConfig);
        serviceConfig.setApplication(applicationConfig);
        serviceConfig.export();
        System.out.println("服務(wù)已暴露");
        System.in.read();
    }

    public static void setMock(ServiceConfig serviceConfig, String server) {
        serviceConfig.setRef(new MockService(server));
    }
}
隱示傳參

是指通過非常方法參數(shù)傳遞參數(shù)戴尸,類似于http 調(diào)用當(dāng)中添加cookie值粟焊。通常用于分布式追蹤框架的實現(xiàn)。使用方式如下 :

//客戶端隱示設(shè)置值
RpcContext.getContext().setAttachment("index", "1"); // 隱式傳參,后面的遠程調(diào)用都會隱
//服務(wù)端隱示獲取值
String index = RpcContext.getContext().getAttachment("index"); 
令牌驗證

通過令牌驗證在注冊中心控制權(quán)限吆玖,以決定要不要下發(fā)令牌給消費者筒溃,可以防止消費者繞過注冊中心訪問提供者,另外通過注冊中心可靈活改變授權(quán)方式沾乘,而不需修改或升級提供者


image.png

使用:

<!--隨機token令牌怜奖,使用UUID生成-->
<dubbo:provider interface="com.foo.BarService" token="true" />
過濾器

類似于 WEB 中的Filter ,Dubbo本身提供了Filter 功能用于攔截遠程方法的調(diào)用翅阵。其支持自定義過濾器與官方的過濾器使用:

<dubbo:provider  filter="accesslog" accesslog="logs/dubbo.log"/>

以上配置 就是 為 服務(wù)提供者 添加 日志記錄過濾器歪玲, 所有訪問日志將會集中打印至 accesslog 當(dāng)中。
自定義過濾器:

  • 編寫過濾器
package com.tuling.dubbo;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
@Activate(group = {CommonConstants.PROVIDER})
public class ProviderHelloFilter implements Filter {
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        System.out.println("log========>");
        return invoker.invoke(invocation);
    }
}
  • 添加擴展點
# 文件路徑
META-INF/dubbo/org.apache.dubbo.rpc.Filter
#內(nèi)容:
helloFilter=com.tuling.dubbo.ProviderHelloFilter

六掷匠、調(diào)用內(nèi)部實現(xiàn)源碼分析

分析代理類

在調(diào)用服務(wù)端時滥崩,是接口的形式進行調(diào)用,該接口是Duboo 動態(tài)代理之后的實現(xiàn)讹语,通過反編譯工具可以查看到其具體實現(xiàn):
因為類是代理生成钙皮,所以采用arthas工具來反編譯,具體操作如下:

#運行 arthas
java -jar arthas-boot.jar
#掃描類
sc *.proxy0
#反編譯代理類
jad com.alibaba.dubbo.common.bytecode.proxy0

反編譯的代碼如下:

package org.apache.dubbo.common.bytecode;

import com.alibaba.dubbo.rpc.service.EchoService;
import com.tuling.client.User;
import com.tuling.client.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;
import org.apache.dubbo.common.bytecode.ClassGenerator;

public class proxy0 implements ClassGenerator.DC, EchoService, UserService {
    public static Method[] methods;
    private InvocationHandler handler;
    public List findUser(String string, String string2) {
        Object[] arrobject = new Object[]{string, string2};
        Object object = this.handler.invoke(this, methods[0], arrobject);
        return (List)object;
    }
    public User getUser(Integer n) {
        Object[] arrobject = new Object[]{n};
        Object object = this.handler.invoke(this, methods[1], arrobject);
        return (User)object;
    }
    @Override
    public Object $echo(Object object) {
        Object[] arrobject = new Object[]{object};
        Object object2 = this.handler.invoke(this, methods[2], arrobject);
        return object2;
    }
    public proxy0() {
    }
    public proxy0(InvocationHandler invocationHandler) {
        this.handler = invocationHandler;
    }
}

可看出其代理實現(xiàn)了 UserService 接口顽决。并且基于InvocationHandler 進行代理短条。實際類是 InvokerInvocationHandler 并且其中之屬性為Invoker.。也就是說最終會調(diào)用Invoker進行遠程調(diào)用才菠。

Dubbo調(diào)用流程
image.png
//------7協(xié)議 調(diào)用
doInvoke:77, DubboInvoker {org.apache.dubbo.rpc.protocol.dubbo}
invoke:155, AbstractInvoker {org.apache.dubbo.rpc.protocol}
//------6異步轉(zhuǎn)同步
invoke:52, AsyncToSyncInvoker {org.apache.dubbo.rpc.protocol} // 異步轉(zhuǎn)同步 ,返回結(jié)果之前進行阻塞調(diào)用線程
//----- 5過濾器鏈
invoke:92, MonitorFilter {org.apache.dubbo.monitor.support}  // 過濾鏈-> 監(jiān)控器
invoke:54, FutureFilter {org.apache.dubbo.rpc.protocol.dubbo.filter}    //過濾鏈-> 回調(diào)參數(shù)
invoke:14, ProviderHelloFilter {com.tuling.dubbo}  // 過濾鏈-> 自定義過濾器
invoke:60, ConsumerContextFilter {org.apache.dubbo.rpc.filter} // 過濾鏈-> 消費者環(huán)境初始化
//------4集群處理
doInvoke:82, FailoverClusterInvoker {org.apache.dubbo.rpc.cluster.support} // 集服-失敗重試
invoke:248, AbstractClusterInvoker {org.apache.dubbo.rpc.cluster.support} //
//----- 3Mock服務(wù)
invoke:78, MockClusterInvoker {org.apache.dubbo.rpc.cluster.support.wrapper} // mock 服務(wù)
//----- 2動態(tài)代理 --透明化
invoke:55, InvokerInvocationHandler {org.apache.dubbo.rpc.proxy}// 代理的中間接口
getUser:-1, proxy0 {org.apache.dubbo.common.bytecode} // 代理對象
//----- 1調(diào)用客戶端
main:53, DubboClient {com.tuling.dubbo}  // 客戶端

協(xié)議-->注冊協(xié)議--->MockClusterInvoker--->ClusterInvoker--->RegistryDirectory--->DubboProtcol->FilterChain-->DubboInvoker
image.png

注意:核心在于運用了責(zé)任鏈模式與spi擴展點的技術(shù)茸时。

七、RPC 協(xié)議

在一個典型RPC的使用場景中赋访,包含了服務(wù)發(fā)現(xiàn)可都、負載、容錯蚓耽、網(wǎng)絡(luò)傳輸渠牲、序列化等組件,其中RPC協(xié)議就指明了程序如何進行網(wǎng)絡(luò)傳輸和序列化 田晚。也就是說一個RPC協(xié)議的實現(xiàn)就等于一個非透明的遠程調(diào)用實現(xiàn)嘱兼,如何做到的的呢?


image.png
dubbo 支持的RPC協(xié)議列表
image.png
dubbo協(xié)議結(jié)構(gòu)
image.png
  • magic:類似java字節(jié)碼文件里的魔數(shù)贤徒,用來判斷是不是dubbo協(xié)議的數(shù)據(jù)包芹壕。魔數(shù)是常量0xdabb,用于判斷報文的開始。
  • flag:標志位, 一共8個地址位接奈。低四位用來表示消息體數(shù)據(jù)用的序列化工具的類型(默認hessian)踢涌,高四位中,第一位為1表示是request請求序宦,第二位為1表示雙向傳輸(即有返回response)睁壁,第三位為1表示是心跳ping事件。
  • status:狀態(tài)位, 設(shè)置請求響應(yīng)狀態(tài),dubbo定義了一些響應(yīng)的類型潘明。具體類型見 com.alibaba.dubbo.remoting.exchange.Response
  • invoke id:消息id, long 類型行剂。每一個請求的唯一識別id(由于采用異步通訊的方式,用來把請求request和返回的response對應(yīng)上)
  • body length:消息體 body 長度, int 類型钳降,即記錄Body Content有多少個字節(jié)厚宰。


    image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市遂填,隨后出現(xiàn)的幾起案子铲觉,更是在濱河造成了極大的恐慌,老刑警劉巖吓坚,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撵幽,死亡現(xiàn)場離奇詭異,居然都是意外死亡礁击,警方通過查閱死者的電腦和手機盐杂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來客税,“玉大人况褪,你說我怎么就攤上這事「埽” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵捏膨,是天一觀的道長秧均。 經(jīng)常有香客問我,道長号涯,這世上最難降的妖魔是什么目胡? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮链快,結(jié)果婚禮上誉己,老公的妹妹穿的比我還像新娘。我一直安慰自己域蜗,他們只是感情好巨双,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著霉祸,像睡著了一般筑累。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上丝蹭,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天慢宗,我揣著相機與錄音,去河邊找鬼。 笑死镜沽,一個胖子當(dāng)著我的面吹牛敏晤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播缅茉,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼嘴脾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了宾舅?” 一聲冷哼從身側(cè)響起统阿,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎筹我,沒想到半個月后扶平,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡蔬蕊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年结澄,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片岸夯。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡麻献,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出猜扮,到底是詐尸還是另有隱情勉吻,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布旅赢,位于F島的核電站齿桃,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏煮盼。R本人自食惡果不足惜短纵,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望僵控。 院中可真熱鬧香到,春花似錦、人聲如沸报破。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽泛烙。三九已至理卑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蔽氨,已是汗流浹背藐唠。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工帆疟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人宇立。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓踪宠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親妈嘹。 傳聞我的和親對象是個殘疾皇子柳琢,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容