由於公司使用基於Java語言的
Dubbo技術棧,而本人對Python技術棧更為熟悉。為了使不懂JAVA代碼的同學也能進行Dubbo接口層的測試,總結一個通過python實現dubbo接口調用的實現方案。
一、實現原理:
根據Dubbo官方文檔中提到的:dubbo可以通過telnet命令進行服務治理,可以通過telnet鏈接dubbo服務,再通過invoke方法調用dubbo接口
詳情見http://dubbo.apache.org/zh-cn/docs/user/references/telnet.html
而在Python中有一個第三方包 telnetlib,所以我們可以通過這個包來執行telnet命令,進而對dubbo接口進行調用
通過上面官方文檔截圖,我們可以看到,當我們拿到dubbo服務的IP和端口號,就能去調用指定的dubbo接口了。下面,讓我們一步步來實現
二、dubbo架構:
調用關系說明 服務容器負責啟動,加載,運行服務提供者。 服務提供者在啟動時,向注冊中心注冊自己提供的服務。
服務消費者在啟動時,向注冊中心訂閱自己所需的服務。
注冊中心返回服務提供者地址列表給消費者,如果有變更,注冊中心將基於長連接推送變更數據給消費者。
服務消費者,從提供者地址列表中,基於軟負載均衡算法,選一台提供者進行調用,如果調用失敗,再選另一台調用。
服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鍾發送一次統計數據到監控中心。 Dubbo
架構具有以下幾個特點,分別是連通性、健壯性、伸縮性、以及向未來架構的升級性。
通過上面架構圖我們可以類似 zookeeper 這樣的服務注冊中心找到對應的服務,所部署的機器和端口
也通過dubbo-monitor上面進行查詢
三、python實現dubbo的調用
通過上述收到查到到要調用的dubbo接口所處的服務器IP和端口,我們就可以通過python實現dubbo的調用了。詳細代碼如下:
import re import telnetlib import time import logging logging.basicConfig(level = logging.INFO,format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) ''' 方法調用案例: conn = InvokeDubboApi('127.0.0.1:88888') data = { 'dubbo_service': 'xxx.xxx.xx.xxxx.xxxx.xxxx.Service', 'dubbo_method': 'xxxxx', 'parameters': ({"age":41,"name":"tom"},"sh",564645,) } invoke = json.loads(conn.invoke_dubbo_api(data)) conn.logout() ''' class TelnetClient(object): """通過telnet連接dubbo服務, 執行shell命令, 可用來調用dubbo接口 """ def __init__(self, server_host, server_port): self.conn = telnetlib.Telnet() self.server_host = server_host self.server_port = server_port # telnet登錄主機 def connect_dubbo(self): try: logging.info("telent連接dubbo服務端: telnet {} {} ……".format(self.server_host, self.server_port)) self.conn.open(self.server_host, port=self.server_port) return True except Exception as e: logging.info('連接失敗, 原因是: {}'.format(str(e))) return False # 執行傳過來的命令,並輸出其執行結果 def execute_command(self, command): # 執行命令 cmd = 'invoke {}\n'.format(command).encode("utf-8") self.conn.write(cmd) # 初始化調用次數 invoke_count = 0 # 若調用無返回時,記錄次數並重試 result = self.conn.read_very_eager().decode(encoding='utf-8').split('\r\n')[0] while result == '': time.sleep(1) result = self.conn.read_very_eager().decode(encoding='utf-8').split('\r\n')[0] invoke_count += 1 if invoke_count>=5: logging.info("調用dubbo接口超過五次,調用失敗") return '調用dubbo接口失敗' return result # 退出telnet def logout_host(self): self.conn.write(b"exit\n") logging.info("登出成功") class InvokeDubboApi(object): def __init__(self, content): #解析dubbo部署的ip和port try: dubboaddrre = re.compile(r"([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+)", re.I) result = dubboaddrre.search(str(content)).group() server_host = result.split(":")[0] server_port = result.split(":")[1] logging.info("獲取到dubbo部署信息" + result) except Exception as e: raise Exception("獲取dubbo部署信息失敗:{}".format(e)) try: self.telnet_client = TelnetClient(server_host, server_port) self.login_flag = self.telnet_client.connect_dubbo() except Exception as e: logging.info("invokedubboapi init error" + e) #調用dubbo接口 def invoke_dubbo_api(self, data): cmd = data.get("dubbo_service") + "." + data.get("dubbo_method") + "{}".format(data.get("parameters")) logging.info("調用命令是:{}".format(cmd)) resp = None try: if self.login_flag: result= self.telnet_client.execute_command(cmd) logging.info("接口響應是,result={}".format(resp)) return result else: logging.info("登陸失敗!") except Exception as e: raise Exception("調用接口異常, 接口響應是result={}, 異常信息為:{}".format(result, e)) self.logout() # 調用多個dubbo接口,注:確保所有接口是同一個ip和port def invoke_dubbo_apis(self,datas): summary = [] if isinstance(datas,list): for i in range(len(datas)): result = self.invoke_dubbo_api(datas[i]) summary.append({"data":datas[i],"result":result}) return summary else: return "請確認入參是list" def logout(self): self.telnet_client.logout_host() if __name__ == '__main__': data = { 'dubbo_service': 'xxx.xxx.xx.xxxx.xxxx.xxxxService', 'dubbo_method': 'xxxxx', 'parameters': ({"id":"123456789","mobile":12456},) } i = InvokeDubboApi('127.0.0.1:110741') i.invoke_dubbo_api(data) i.logout()
請求結果:
四、注意事項
1、請求參數
數據data中的參數字段parameters是一個元組,后面的 ‘,’ 不能少
2、請求參數異常
請求Dubbo接口如果填入的參數有誤,會報 no such method 的錯誤,請檢查一下參數是否正常
3、當要批量請求時
傳入的參數必須是list,且需要同樣的IP和端口。。