TinyRMI—RMI的封裝、擴充套件及踩到的坑的解決
在Tiny的平行計算中,引用了遠端方法呼叫工程,就是這裡說的TinyRMI,當時在寫測試用例的時候,只是在單機進行了測試,一切安好,但是Dawn在使用時,在多機進行試用,結果就出現了問題,最後花了不下一人周,才解決了Dawn發現的問題,最終解決了問題,也發現了RMI中的一些坑。可能有的人已經走過了,有的人如果沒有碰到,也可能會掉同樣的坑,因此把它成文,以饗讀者,避免上同樣的當。
此文的形成離不開Dawn的深入測試與分析,在此表示深深感謝!
功能需求
期望對Jdk中的Rmi進行一定的封裝提供以下特性:
- 支援本地物件註冊與登出
- 支援遠端物件註冊與登出
- 支援斷線重連
由於網路故障,導致連線斷開,在網路故障恢復之後,可以繼續正常訪問
由於RmiServerLocal停止,重新啟動後,客戶端註冊的物件要繼續可以正常訪問
由於RmiServerRemote停止 ,重新啟動後,客戶端註冊的物件需要重新註冊 - 支援物件校驗
如果某些物件已經失效,伺服器端可以把它從登錄檔去除,以避免別人拿到失效的物件
介面設計
可以看到RmiServer是繼承了Serializable和Remote介面的一個介面,它提供了註冊物件及取消註冊物件的多個方法。當然也提供了一些輔助方法,看起來還是非常簡單的。
另外還有一個輔助介面Verifiable,對於加入的遠端物件,如果實現了此介面,則可以對其有效性進行驗證,如果已經失效,將被自動從註冊無中去除。
1
2
3
4
5
6
7
8
9
10
11
|
/** * 是否可驗證,實現了此介面的類,可以進行校驗
*/
public interface Verifiable {
/**
* 校驗,如果校驗時不出現異常,就表示是OK的
*
* @throws RemoteException
*/
void verify() throws RemoteException;
} |
當然,要加入到RmiServer中,也要有一定的約束,因此設定了介面RemoteObject
1
2
|
public interface RemoteObject extends Serializable, Remote {
} |
好的,至此為止,介面就算設計完了。
程式碼實現
第一版程式碼實現,偶預想的非常簡單,如何獲取Registry作為一個抽象方法由子類實現,其它都是針對Registry進行的操作,就放在抽象類中實現,分分鐘寫好,然後本地測試通過,洋洋自得中,卻濛濛然不知犯下了嚴重的錯誤…..
在單機環境下,測試都是好的,不管是RMI自己的測試用例還是複用它的平行計算工程。
但是Dawn在使用的時候,採用了Linux機器兩個物理機進行測試,問題出現了,錯誤資訊如下:
1
|
java.rmi.AccessException: Registry.Registry.bind disallowed; origin / 192.168 .xxx.xxx is non-local host
|
我們用物理的兩臺計算機進行測試,也有同樣的問題。
於是Baidu、谷歌都用上了,查詢了終於找到原因:
Registry只有本地的才可以對登錄檔進行修改,遠端的只能用來檢視。
於是把RmiServerLocal作為一個遠端物件提供出來,讓RmiServerRemote來呼叫,心想這樣總可以了吧??但是還是不行,還是同樣的問題。
仔細閱讀JavaDoc文件和找到的一些材料,才理解了,這個bind過程只能在RmiServerLocal所在的機器中執行,即使是通過遠端服務呼叫,它還是認為是在RmiServerRemote中呼叫的……這尼瑪的坑爹了,這個RMI這麼難用,設計者知道麼?
這次改進,遠端機註冊的時候,只是新增到RmiServerLocal中的一個Queue中,然後在RmiServerLocal所在機器開一個掃描執行緒,來進行bind,unbind操作,這樣總保證是在Local中執行了。
測試一下,確實OK了……還沒有高興半下,Dawn報過來,又出問題了。
Windows作Server,Linux做客戶端的時候是OK的,但是Linux只要做伺服器端就還是不行。
繼續查詢原因,看到資料說是因為Linux查詢到IP與外部訪問的IP不對應導致,需要修改/etc/hosts檔案中的127.0.0.1到外部方面的IP地址。
修改之後,問題得到解決。
測試過程
初步跑通之後,接下來就是各種各樣的測試及各種各樣的問題。
由於採用了非同步註冊的關係,導致註冊時間過後一小段時間才可以訪問註冊到的物件。
有物件訪問衝突問題,等等等等,總之就是測了改,改了測,最後終於修正完畢,終於有一個穩定的可用的版本出現了。
最後,由於原來的設想的程式碼共用基本上沒有複用的價值了,因此RmiServerLocal和RmiServerRemote都是各自的實現,不再有共同的基類。
經驗總結
- 測試用例的編寫一定要充分覆蓋。
- 涉及到網路方面的測試,不能僅在本地測試通過就可以,一定要用真實的環境進行測試。
- 不同的作業系統處理還是有一些不同,不要太迷信Java的一次編寫到處使用,事實是到處可以跑,在大多數情況下也是可以正確的跑的,但是有些外部條件不同,可能會導致故障的出現。
關於RMI:
- 如果是客戶端僅呼叫伺服器端提供的物件,那麼是非常簡單的。
- 如果是客戶端也要向伺服器註冊遠端物件,那麼就需要採用非同步的方式,搞一下注冊或登出佇列。
程式碼本身不復雜,需要的同學,請自行檢視原始碼。
https://git.oschina.net/tinyframework/tiny/tree/master/framework/org.tinygroup.rmi
相關文章
- [rabbitmq]安裝ampq的擴充套件的踩坑總結MQ套件
- WPF如何封裝一個可擴充套件的Window封裝套件
- vs 擴充套件無法安裝的解決辦法套件
- php安裝擴充套件模組後,重啟不生效的原因及解決辦法PHP套件
- Dapper的封裝、二次封裝、官方擴充套件包封裝,以及ADO.NET原生封裝APP封裝套件
- 使用composer安裝擴充套件包的坑點之一套件
- 安裝PHPscws分詞擴充套件時候遇到的坑PHP分詞套件
- 安裝PHP及Memcache擴充套件PHP套件
- PHPWAMP安裝Redis擴充套件的方式與相關擴充套件的下載PHPRedis套件
- 安裝PHP的memcache擴充套件PHP套件
- Swift 核心動畫 面向協議 擴充套件封裝Swift動畫協議套件封裝
- 有未分配的磁碟卻不能擴充套件卷怎麼解決 有未分配的磁碟卻不能擴充套件卷解決方法套件
- CentOS安裝RabbitMQ及PHP擴充套件CentOSMQPHP套件
- centos安裝php的oracle擴充套件CentOSPHPOracle套件
- Solon詳解(六)- Solon的校驗擴充套件框架使用與擴充套件套件框架
- Hash分割槽表的使用及擴充套件套件
- 我的第一個擴充套件包釋出啦-封裝明道雲API套件封裝API
- centos安裝php缺失fileinfo.so擴充套件解決CentOSPHP套件
- 【PHP】Mcrypt 擴充套件模組安裝及使用PHP套件
- php安裝redis擴充套件及操作redisPHPRedis套件
- amqp及zmq擴充套件安裝錯誤MQ套件
- redis的PHP擴充套件包安裝方法RedisPHP套件
- 解決debian7下安裝GD擴充套件失敗的問題套件
- RxRetrofit - 終極封裝 - 深入淺出 & 擴充套件 String封裝套件
- 正則的擴充套件套件
- SRAM的容量擴充套件套件
- ?用Chrome擴充套件管理器, 管理你的擴充套件Chrome套件
- mac安裝PHP擴充套件之pcntl遇坑筆記MacPHP套件筆記
- php7安裝redis擴充套件和memcache擴充套件PHPRedis套件
- YAML 擴充套件安裝YAML套件
- Yac 擴充套件安裝套件
- PHP 擴充套件安裝PHP套件
- mongodb擴充套件安裝MongoDB套件
- sphinxphp擴充套件安裝PHP套件
- PHP 擴充套件安裝。解決 Homestead6.1.0 裡沒有 phpize 的問題PHP套件
- composer 安裝擴充套件出現 "PHP Fatal error: Allowed memory ..."的解決辦法套件PHPError
- CalltoundefinedfunctionImageCreate()錯誤解決擴充套件gd庫UndefinedFunction套件
- ThreadLocal可能踩到的坑thread