前言
微服務架構在如今的9102年已經不是什么新鮮的話題了,但是怎么做好微服務架構,卻又是一個永恆的話題。比如服務粒度的划分,怎么控制好粗細?服務划分后,對於項目的部署會有什么改變?... 這會是一個很大的話題,以后可以分開篇章探討一翻,但是我們本篇並不打算聊這個,而是討論一下具體的實現技術--dubbo。
dubbo歷史
2011 年末,阿里巴巴在 GitHub 上開源了基於 Java 的分布式服務治理框架 Dubbo,之后它成為了國內該類開源項目的佼佼者,許多開發者對其表示青睞。同時,先后有不少公司在實踐中基於 Dubbo 進行分布式系統架構,目前在 GitHub 上,它的 fork、star 數均已破萬。2014 年 10 月 30 號發布版本 dubbo-2.4.11,修復了一個小 Bug,版本又陷入漫長的停滯到2017年九月份。
在dubbo停滯的期間呢,當當網 Fork 了阿里的一個 Dubbo 版本開始維護,並命名為 dubbox-2.8.0。值得注意的是,當當網擴展 Dubbo 服務框架支持 REST 風格遠程調用,並且跟隨着 ZooKeepe 和 Spring 升級了對應的版本。之后 Dubbox 一直在小版本維護,2015 年 3 月 31 號發布了最后一個版本 dubbox-2.8.4。筆者公司用的也是這個版本,並稍微改造了下源碼,下面會有提及。
其實在當前說到微服務,可能大家第一反應是springcloud,spring全家桶帶來的便捷是顯而易見的,然而為什么我們這里聊的是dubbo呢?原因之一是因為筆者公司只用了dubbo(別扔雞蛋....),其二呢其實rpc框架很多原理是相通的,當我們理解了其中一個,再去看其他的框架,會有一種似曾相識的感覺,最后也沒必要去爭論XX框架的好與壞,選擇最適合自己業務的就是最好的。
先交代下背景,我們這邊是從2016年開始使用dubbo,使用的是dubbox-2.8.4 版本,然后因為一些場景不合適改了下代碼,重新打包成2.8.5提交至公司的私服使用。好了,接下來就開始進入正文,聊聊這幾年在dubbo使用過程中遇到坑,以及需要注意的地方吧。
正文
1、超時重試
這是一個很經典的坑,當時由於剛使用dubbo,很多配置都是基於默認的。剛好此時在項目中,有一個機器人送禮的邏輯比較復雜,當遇到某些特定的條件時,該邏輯的耗時會比正常情況下變長,這時候就出現了一個很神奇的現象,為何我只觸發了一次送禮的請求,而線上卻送了三次?
剛遇到這種情況可我驚呆了,重新審視了代碼,發現並無問題。這就奇怪了,哪里來的3次?后來掉了幾根頭發以后,才在dubbo的文檔中發現了服務這塊有timeout跟retry屬性,默認timeout=1000ms,retry=2。這下就豁然開朗,原來是第一次調用超時,導致又重試了2次,一共就是3次了。
找到問題的原因,我們就有辦法解決了。由於我們這個接口不是冪等性的,而且也不用返回什么信息給調用者,所以我們可以通過一個線程池來執行這段耗時的邏輯,讓rpc調用可以比較快的返回給調用者。這樣就不存在超時的問題了。或者可以配合增加timeout時間跟retry=0也能實現,具體的業務邏輯需要自己找到合適的解決方案。
2、dubbo使用內網ip
正常情況下,我們的服務調用推薦走內網連接的方式,效率是比較高的。但是有些特殊的情況,我們需要dubbo注冊服務的時候使用外網ip,該怎么修改呢?這時候就需要修改我們的服務器上 /etc/hosts 文件了,新增一條 “外網ip 主機名”的記錄,restart我們的服務即可。
3、docker里面注冊宿主機內網ip
說到微服務,當然也少不了docker了,我們當前用的是docker+overlay網絡一個結構,直接把dubbo服務丟進容器里面跑的話,注冊進zk的ip是容器ip。所以我們采取了一種折中的方式。
利用docker的特性,我們在創建容器的時候,把宿主機的ip以及需要暴露的端口寫進容器的環境變量里面。然后就是修改dubbox的源碼了,源碼的com.alibaba.dubbo.registry.integration.RegistryProtocol類的getRegistedProviderUrl
方法,此方法用於返回注冊到注冊中心的URL。
private URL getRegistedProviderUrl(final Invoker<?> originInvoker){ //targetUrl 注冊中心看到的地址 URL targetUrl; URL providerUrl = getProviderUrl(originInvoker); //配置的容器環境變量 String envParameterHost=System.getenv(ENV_HOST_KEY); String envParameterPort=System.getenv(ENV_PORT_KEY); if (StringUtils.isBlank(envParameterHost)||StringUtils.isBlank(envParameterPort)){//非容器環境:執行原來的注冊邏輯 targetUrl=providerUrl.removeParameters(getFilteredKeys(providerUrl)).removeParameter(Constants.MONITOR_KEY); }else {//容器環境,如果環境變量中DOCKER_NAT_HOST和DOCKER_NAT_PORT兩個值都不為空則直接將這兩個值作為url注冊到zk //執行重新拼接url的操作,涉及敏感代碼這里不展示了 targetUrl=dockerRegUrlWithHostAndPort; } return targetUrl; }
4、未注意服務重名
其實這是我們開發人員粗心大意出現的情況,開發的時候注冊了2個相同簽名的服務,但是業務邏輯是完全不同的,這會導致一個之前運行的正常的業務會偶爾調用失敗,原因是因為dubbo的負載均衡策略,把一部分流量轉移到我們新注冊上來的服務上了,但是處理邏輯不同,導致錯誤。
5、版本的一致性
dubbo當前的releases版本已經去到2.7.1了,項目中要注意一下不同項目間版本的一致性,或者是dubbo跟dubbox的一些差別,最好做到統一,不然出現問題解決的成本會比較高。
6、屬性配置的優先級
我們在dubbo的過程中會發現,提供者跟消費者中,很多屬性是一樣的,我們該怎么配呢?在dubbo的文檔當中其實有推薦的用法。
在提供者端盡量多提供消費者端的屬性。
參考文檔,原因如下:
-
作服務的提供方,比服務消費方更清楚服務的性能參數,如調用的超時時間、合理的重試次數等
-
在 Provider 端配置后,Consumer 端不配置則會使用 Provider 端的配置,即 Provider 端的配置可以作為 Consumer 的缺省值 。否則,Consumer 會使用 Consumer 端的全局設置,這對於 Provider 是不可控的,並且往往是不合理的
Provider 端盡量多配置 Consumer 端的屬性,讓 Provider 的實現者一開始就思考 Provider 端的服務特點和服務質量等問題。
結語
其實在dubbo的使用過程中,還有挺多問題這里沒列出來的,但是解決方法都差不多,首先文檔要熟,做到心中有數,比如dubbo功能的成熟度,有些是不推薦在線上使用的,這時你就要謹慎了。然后文檔里面確實是有遺漏的問題,我們有必要可以debug dubbo的源碼,這個過程會比較痛苦,但是對於排查問題跟個人能力的提高是有很有幫助的。
大家在dubbo的使用過程中有什么問題也可以交流一下~
github:https://github.com/apache/incubator-dubbo
中文使用手冊http://dubbo.apache.org/zhcn/docs/user/preface/background.html
喜歡的話,勞煩點個贊,關注一下公眾號<深夜里的程序猿>