通過(guò)rabbitMQ實(shí)現(xiàn)一個(gè)Python的簡(jiǎn)單rpc框架

rpc中文即遠(yuǎn)程過(guò)程調(diào)用的意思

微服務(wù)架構(gòu)大行其道的形勢(shì)下冗栗, 作為開(kāi)發(fā)人員或多或少都會(huì)接觸到相關(guān)技術(shù)

大廠google的gRPC秋茫、Facebook的thrift迎瞧、阿里的dubble算是rpc框架中比較優(yōu)秀的代表啦

其中不少框架都是支持跨語(yǔ)言調(diào)用肉迫,有python調(diào)用接口妓忍,但是因?yàn)橛闷渌Z(yǔ)言編寫,使用起來(lái)略顯復(fù)雜尝苇,且不太pythonic 手動(dòng)滑稽

這里不妨借助rabbitMQ 實(shí)現(xiàn)一個(gè)自己的簡(jiǎn)單rpc框架

小伙伴們首先得熟悉一下rabbitMQ的api, 這里貼出一個(gè)非常友好的教程文檔铛只,文檔不大僅有幾篇文章,快一點(diǎn)大概半天即可了解消息隊(duì)列的機(jī)制茎匠,rabbitmq作為生產(chǎn)中經(jīng)常用到的工具格仲,需要好生了解哦~
python版教程最后一篇押袍,就是一個(gè)簡(jiǎn)單的rpc demo
該實(shí)現(xiàn)僅能作單個(gè)函數(shù)的遠(yuǎn)程調(diào)用诵冒,我在此之上重新封裝實(shí)現(xiàn)了一個(gè)支持多種函數(shù)調(diào)用的rpc實(shí)現(xiàn)
服務(wù)架設(shè)配置:

系統(tǒng) 版本
ubuntu 16.04
RabbitMQ 3.5.7
python 3.6.2
pika 1.0.0
客戶端/調(diào)用端部分:

在初始化對(duì)象方法中加入了兩個(gè)數(shù)據(jù)結(jié)構(gòu):

    # 請(qǐng)求id結(jié)果映射表
    self._req_id_result_map = {}
    # 請(qǐng)求id列表
    self.req_id_list = []

每一次請(qǐng)求調(diào)用都需要使用一個(gè)唯一標(biāo)識(shí)id
這兩個(gè)數(shù)據(jù)結(jié)構(gòu),一個(gè)用來(lái)保存調(diào)用結(jié)果谊惭,一個(gè)用來(lái)保存調(diào)用者id列表

    rst = self._req_id_result_map.get(req_id, None)
    del self._req_id_result_map[req_id]

從保存的結(jié)果中拿出數(shù)據(jù)汽馋,記得要清空數(shù)據(jù),不然請(qǐng)求數(shù)據(jù)量大的話會(huì)出現(xiàn)數(shù)據(jù)積壓到內(nèi)存從而導(dǎo)致內(nèi)存溢出的情況

    def call(self, req_id, fn, param):
        """
        :param req_id:str 遠(yuǎn)程調(diào)用請(qǐng)求id  要求唯一
        :param fn:str  遠(yuǎn)程調(diào)用函數(shù)名 要求遠(yuǎn)端必須有同名函數(shù)
        :param param:list 遠(yuǎn)程調(diào)用參數(shù) 限制列表長(zhǎng)度為2  第一個(gè)元素代表位置參數(shù)的列表 
                          無(wú)參數(shù)傳空列表 第二個(gè)元素為具名參數(shù)的字典
                      沒(méi)有則傳空字典   遠(yuǎn)端調(diào)用類似  fn(*param[0], **param[1])
        :return:
        """
        self.req_id_list.append(req_id)
        self.channel.basic_publish(exchange='', routing_key='rpc_queue',
                                   properties=pika.BasicProperties(reply_to=self.callback_queue,
                                                                   correlation_id=req_id),
                                   body=json.dumps([fn, param]))
        while not self._req_id_result_map.get(req_id, None):
            self.connection.process_data_events()
        return self.get_request_result(req_id)

這里是調(diào)用端的核心邏輯:
call方法參數(shù)為 請(qǐng)求id圈盔, 調(diào)用函數(shù)名豹芯, 調(diào)用函數(shù)參數(shù)
其中參數(shù)有特別要求,必須是長(zhǎng)度為二的可遍歷容器(列表驱敲、元祖铁蹈、set...)
容器的第一個(gè)元素代表位置參數(shù),第二個(gè)元素是命名參數(shù)的字典

call方法首先向默認(rèn)交換機(jī)發(fā)送數(shù)據(jù)众眨,注意這里通過(guò)路由鍵把消息指定到名為rpc_queue的隊(duì)列中, 然后阻塞一直等到遠(yuǎn)端響應(yīng)結(jié)果

服務(wù)端/被調(diào)端部分:

服務(wù)端要準(zhǔn)備好遠(yuǎn)程調(diào)用的函數(shù)握牧,建議模塊化到單獨(dú)的地方然后引入到服務(wù)端主邏輯模塊
聲明connection、rpc_queue隊(duì)列娩梨、綁定回調(diào)函數(shù)并開(kāi)啟消費(fèi)(consume)

回調(diào)函數(shù)的主要邏輯:

def on_request(ch, method, props, body):
    fn, param = json.loads(body.decode())
    print(" [.] fib(%s)" % (param,))
    if not (isinstance(param, list) and len(param) == 2 and 
            isinstance(param[0], list) and isinstance(param[1], dict)):
        response = "error params please use like this fn(*param[0], **param[1])"
    else:
        try:
            fn = eval(fn)
            response = fn(*param[0], **param[1])
        except:
            response = "remote has not function like {}".format(fn)

    ch.basic_publish(exchange='', routing_key=props.reply_to,
                     properties=pika.BasicProperties(correlation_id=props.correlation_id),
                     body=str(response))
    ch.basic_ack(delivery_tag=method.delivery_tag)

這里要判斷調(diào)用端的參數(shù)格式正確與否沿腰,以及調(diào)用的函數(shù)是否存在
取出調(diào)用端的reply_to 作為路由鍵(也是調(diào)用端聲明的隊(duì)列,注意有兩個(gè)隊(duì)列狈定,一個(gè)存放遠(yuǎn)程調(diào)用信息颂龙,一個(gè)存放遠(yuǎn)程調(diào)用結(jié)果, 分別在服務(wù)端/客戶端聲明)
同時(shí)取出correlation_id這個(gè)即是調(diào)用端的請(qǐng)求id 把這個(gè)id作為屬性發(fā)送到調(diào)用端纽什,調(diào)用端通過(guò)這個(gè)id(可以看成一個(gè)token)取出調(diào)用結(jié)果

數(shù)據(jù)傳輸默認(rèn)使用二進(jìn)制措嵌, 參數(shù)部分先json序列化,然后轉(zhuǎn)化為二進(jìn)制

最后把調(diào)用端封裝了一下方法:
call_single芦缰、call_many
使用這個(gè)兩個(gè)函數(shù)即可單次企巢、多次進(jìn)行rpc
調(diào)用端(客戶端)和遠(yuǎn)程端(服務(wù)端/被調(diào)端)分別部署在不同的機(jī)器上,注意配置統(tǒng)一的rabbitMQ主機(jī)地址饺藤,至于消息隊(duì)列可以放在服務(wù)端也可以放在客戶端甚至可以部署在一臺(tái)獨(dú)立的服務(wù)器上

最后總結(jié):
源代碼地址在此哦 >~<
這里用最簡(jiǎn)潔直接的方式實(shí)現(xiàn)了一個(gè)rpc框架包斑,目前僅支持函數(shù)的遠(yuǎn)程調(diào)用流礁,后續(xù)可以加入方法的遠(yuǎn)程調(diào)用,原理也和函數(shù)調(diào)用類似罗丰,感興趣的小伙伴可以自己動(dòng)手實(shí)現(xiàn)一下

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末神帅,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子萌抵,更是在濱河造成了極大的恐慌找御,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绍填,死亡現(xiàn)場(chǎng)離奇詭異霎桅,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)讨永,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門滔驶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人卿闹,你說(shuō)我怎么就攤上這事揭糕。” “怎么了锻霎?”我有些...
    開(kāi)封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵著角,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我旋恼,道長(zhǎng)吏口,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任冰更,我火速辦了婚禮产徊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘冬殃。我一直安慰自己囚痴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布审葬。 她就那樣靜靜地躺著深滚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪涣觉。 梳的紋絲不亂的頭發(fā)上痴荐,一...
    開(kāi)封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音官册,去河邊找鬼生兆。 笑死,一個(gè)胖子當(dāng)著我的面吹牛膝宁,可吹牛的內(nèi)容都是我干的鸦难。 我是一名探鬼主播根吁,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼合蔽!你這毒婦竟也來(lái)了击敌?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤拴事,失蹤者是張志新(化名)和其女友劉穎沃斤,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體刃宵,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡衡瓶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了牲证。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哮针。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖从隆,靈堂內(nèi)的尸體忽然破棺而出诚撵,到底是詐尸還是另有隱情,我是刑警寧澤键闺,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站澈驼,受9級(jí)特大地震影響辛燥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缝其,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一挎塌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧内边,春花似錦榴都、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至和屎,卻和暖如春拴驮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背柴信。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工套啤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人随常。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓潜沦,卻偏偏與公主長(zhǎng)得像萄涯,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子唆鸡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

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