【Python使用】嘿馬頭條項目從到完整開發(fā)教程第11篇:RPC,編寫客戶端【附代碼文檔】

??????教程全知識點簡介:1.APScheduler任務調度涵蓋安裝配置、使用方式、調度器Scheduler、執(zhí)行器executors、觸發(fā)器Trigger等核心組件。2. RPC遠程過程調用包括RPC概念、背景用途、優(yōu)缺點分析。3. Protocol Buffers數(shù)據(jù)序列化涉及文檔結構、注釋語法、數(shù)據(jù)類型、枚舉類型、消息類型(字段編號、字段規(guī)則、嵌套類型、保留字段、默認值)。4. 客戶端開發(fā)包含頭條首頁新聞推薦接口編寫。5. 即時通訊技術涵蓋需求場景、傳統(tǒng)推送實現(xiàn)、Socket.IO(Python服務器端開發(fā)、事件處理)。6. Elasticsearch搜索引擎包括簡介原理、倒排索引、分析器、相關性排序、集群概念、IK中文分析器、索引類型、文檔操作(索引文檔、獲取文檔、判斷存在、更新刪除)、Logstash數(shù)據(jù)導入、查詢(基本查詢、高級查詢)、全文檢索實現(xiàn)、Python客戶端使用、聯(lián)想提示(拼寫糾錯、自動補全)。7. 單元測試涵蓋測試分類、基本寫法、測試必要性。8. 服務器部署包括Gunicorn、Supervisor配置管理。9. 項目開發(fā)流程涉及產(chǎn)品介紹、原型圖UI圖、技術架構、開發(fā)環(huán)境(ToutiaoWeb虛擬機、Pycharm遠程開發(fā))。10. 數(shù)據(jù)庫技術包含ORM理解、SQLAlchemy映射構建、數(shù)據(jù)庫連接設置、模型類字段選項。11. 分布式系統(tǒng)涵蓋分布式ID方案選擇、Twitter Snowflake算法(64位ID劃分、最大取值計算、移位偏移計算、序號循環(huán)掩碼、時間戳處理)。12. Redis數(shù)據(jù)庫包括Redis持久化機制。13. Git工作流涵蓋Gitflow工作流(工作方式、歷史分支、功能分支、發(fā)布分支、維護分支)、調試方法。14. 身份認證技術包含JWT、JWS、JWE概念、Python庫使用、項目封裝實施方案。15. 對象存儲涉及OSS對象存儲、七牛云存儲服務。16. 緩存系統(tǒng)包括緩存架構、緩存數(shù)據(jù)保存方式、緩存有效期TTL、緩存淘汰策略、緩存問題(緩存穿透、緩存雪崩)、頭條項目緩存設計(User Cache、Article Cache、Announcement Cache)、持久存儲設計(閱讀歷史、搜索歷史、統(tǒng)計數(shù)據(jù))。


????倉庫code.zip ??直接-->:???https://gitee.com/yinuo112/Backend/blob/master/Python/嘿馬頭條項目從到完整開發(fā)教程/note.md ???????

? 本教程項目亮點

?? 知識體系完整:覆蓋從基礎原理、核心方法到高階應用的全流程內容
?? 全技術鏈覆蓋:完整前后端技術棧,涵蓋開發(fā)必備技能
?? 從零到實戰(zhàn):適合 0 基礎入門到提升,循序漸進掌握核心能力
?? 豐富文檔與代碼示例:涵蓋多種場景,可運行、可復用
?? 工作與學習雙參考:不僅適合系統(tǒng)化學習,更可作為日常開發(fā)中的查閱手冊
?? 模塊化知識結構:按知識點分章節(jié),便于快速定位和復習
?? 長期可用的技術積累:不止一次學習,而是能伴隨工作與項目長期參考


??????全教程總章節(jié)


??????本篇主要內容

RPC

編寫客戶端

在toutiao-backend/common/rpc目錄下新建client.py

import grpc
import reco_pb2
import reco_pb2_grpc
import time


def feed_articles(stub):
    # 構建rpc調用的調用參數(shù)
    user_request = reco_pb2.UserRequest()
    user_request.user_id = '1'
    user_request.channel_id = 1
    user_request.article_num = 10
    user_request.time_stamp = round(time.time()*1000)

    # 通過stub進行方法調用,并接收調用返回值
    ret = stub.user_recommend(user_request)
    print('ret={}'.format(ret))

def run():
    """
    rpc客戶端調用的方法
    """
    # 使用with語句連接rpc服務器
    with grpc.insecure_channel('127.0.0.1:8888') as channel:
        # 創(chuàng)建調用rpc遠端服務的輔助對象stub
        stub = reco_pb2_grpc.UserRecommendStub(channel)
        # 通過stub進行rpc調用
        feed_articles(stub)

if __name__ == '__main__':
    run()

頭條首頁新聞推薦接口編寫

在toutiao-backend/toutiao/resources/news/article.py中編寫

from rpc import reco_pb2, reco_pb2_grpc

class ArticleListResource(Resource):
    """
    獲取推薦文章列表數(shù)據(jù)
    """
    def _feed_articles(self, channel_id, timestamp, feed_count):
        """
        獲取推薦文章
        :param channel_id: 頻道id
        :param feed_count: 推薦數(shù)量
        :param timestamp: 時間戳
        :return: [{article_id, trace_params}, ...], timestamp
        """
        user_request = reco_pb2.UserRequest()
        user_request.user_id = g.user_id or 'annoy'
        user_request.channel_id = channel_id
        user_request.article_num = feed_count
        user_request.time_stamp = round(time.time() * 1000)

        stub = reco_pb2_grpc.UserRecommendStub(current_app.rpc_reco)
        ret = stub.user_recommend(user_request)
        return ret.recommends, ret.time_stamp

    def get(self):
        """
        獲取文章列表
        """
        qs_parser = RequestParser()
        qs_parser.add_argument('channel_id', type=parser.channel_id, required=True, location='args')
        qs_parser.add_argument('timestamp', type=inputs.positive, required=True, location='args')
        args = qs_parser.parse_args()
        channel_id = args.channel_id
        timestamp = args.timestamp
        per_page = constants.DEFAULT_ARTICLE_PER_PAGE_MIN
        try:
            feed_time = time.strftime('%Y-%m-%dT%H:%M:%S', time.localtime(time.time()))
        except Exception:
            return {'message': 'timestamp param error'}, 400

        results = []

        # 獲取推薦文章列表
        feeds, pre_timestamp = self._feed_articles(channel_id, timestamp, per_page)

        # 查詢文章
        for feed in feeds:
            article = cache_article.ArticleInfoCache(feed.article_id).get()
            if article:
                article['pubdate'] = feed_time
                article['trace'] = {
                    'click': feed.track.click,
                    'collect': feed.track.collect,
                    'share': feed.track.share,
                    'read': feed.track.read
                }
                results.append(article)

        return {'pre_timestamp': pre_timestamp, 'results': results}

RPC

Python 官方文檔

即時通訊簡介

即時通訊(Instant Messaging)是一種基于互聯(lián)網(wǎng)的即時交流消息的業(yè)務。

類型:

  • 在線push

    • 適用:web頁面 和 App

    • 自己構建IM服務器

      • 使用WebSocket
      • 采用成熟的框架方案Socket.IO
      • 對于App還可自己封裝socket
    • 使用第三方IM服務商提供的服務

  • 離線push

    • 適用:App
    • 對于iOS,使用APNs
    • 對于andorid,使用FCM(國外)或第三方IM服務商提供的服務

提供第三方IM服務的服務商有:

  • 網(wǎng)易云信
  • 融云
  • 環(huán)信
  • LeanCloud

下面只針對 「在線推送」 的自建方案來展開講解。

需求場景

服務端需要主動推送消息給客戶端,例如

  • 用戶下了訂order單,需要在運營管理后臺向運營人員推送新訂order單通知
  • 用戶A關注了用戶B,系統(tǒng)需要向用戶B推送提示消息
  • 即時聊天

傳統(tǒng)的推送實現(xiàn)

HTTP/1.x 不支持服務器主動推送,只能在客戶端發(fā)起請求后做出回應。 (HTTP/2支持服務器主動推送,但HTTP/2 還未全面實施)

  • 輪詢

輪詢是在特定的的時間間隔(如每1秒),由客戶端對服務器發(fā)出HTTP請求,了解服務器有沒有新的信息,然后由服務器告知有無新數(shù)據(jù)或返回最新的數(shù)據(jù)給客戶端。

缺點:

  • 效率低下,浪費資源

必須不停連接,或者連接始終打開,但傳輸HTTP請求,然而HTTP請求可能包含較長的頭部,其中真正有效的數(shù)據(jù)可能只是很小的一部分,顯然這樣會浪費很多的帶寬等資源。

  • Comet (基于長連接)

    • 長輪詢 長輪詢是在打開一條連接以后保持,等待服務器推送來數(shù)據(jù)再關閉的方式。
    • iframe流 iframe流方式是在頁面中插入一個隱藏的iframe,利用其src屬性在服務器和客戶端之間創(chuàng)建一條長鏈接,服務器向iframe傳輸數(shù)據(jù)(通常是HTML,內有負責插入信息的javascript),來實時更新頁面。

    缺點:

    依然需要反復發(fā)出請求,而且長連接也會消耗服務器資源。

WebSocket

HTML5定義了WebSocket協(xié)議,能更好的節(jié)省服務器資源和帶寬,并且能夠更實時地進行通訊。

在2008年誕生,2011年成為國際標準。

現(xiàn)在基本所有瀏覽器都已經(jīng)支持了。

WebSocket是一種在單個TCP連接上進行全雙工通信的協(xié)議。在WebSocket API中,瀏覽器和服務器只需要完成一次握手(不是指建立TCP連接的那個三次握手,是指在建立TCP連接后傳輸一次握手數(shù)據(jù)),兩者之間就直接可以創(chuàng)建持久性的連接,并進行雙向數(shù)據(jù)傳輸。

Websocket使用ws或wss的統(tǒng)一資源標志符,類似于HTTPS,其中wss表示在TLS之上的Websocket。如:

ws://example.com/wsapi
wss://secure.example.com/

Websocket使用和 HTTP 相同的 TCP 端口,可以繞過大多數(shù)防火墻的限制。默認情況下,Websocket協(xié)議使用80端口;運行在TLS之上時,默認使用443端口。

握手協(xié)議

WebSocket 是獨立的、創(chuàng)建在 TCP 上的協(xié)議。 報文

Websocket 通過 HTTP/1.1 協(xié)議的101狀態(tài)碼進行握手。

為了創(chuàng)建Websocket連接,需要通過瀏覽器發(fā)出請求,之后服務器進行回應,這個過程通常稱為“握手”(handshaking)。

一個典型的Websocket握手請求如下:

客戶端請求

GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13

服務器回應

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Location: ws://example.com/

reportlab 文檔

  • Connection必須設置Upgrade,表示客戶端希望連接升級。
  • Upgrade字段必須設置Websocket,表示希望升級到Websocket協(xié)議。
  • Sec-WebSocket-Key是隨機的字符串,服務器端會用這些數(shù)據(jù)來構造出一個SHA-1的信息摘要。把“Sec-WebSocket-Key”加上一個特殊字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,然后計算SHA-1摘要,之后進行BASE-64編碼,將結果做為“Sec-WebSocket-Accept”頭的值,返回給客戶端。如此操作,可以盡量避免普通HTTP請求被誤認為Websocket協(xié)議。
  • Sec-WebSocket-Version 表示支持的Websocket版本。RFC6455要求使用的版本是13,之前草案的版本均應當棄用。
  • Origin字段是可選的,通常用來表示在瀏覽器中發(fā)起此Websocket連接所在的頁面,類似于Referer。但是,與Referer不同的是,Origin只包含了協(xié)議和主機名稱。
  • 其他一些定義在HTTP協(xié)議中的字段,如Cookie等,也可以在Websocket中使用。

優(yōu)點

  • 較少的控制開銷。在連接創(chuàng)建后,服務器和客戶端之間交換數(shù)據(jù)時,用于協(xié)議控制的數(shù)據(jù)包頭部相對較小。在不包含擴展的情況下,對于服務器到客戶端的內容,此頭部大小只有2至10字節(jié)(和數(shù)據(jù)包長度有關);對于客戶端到服務器的內容,此頭部還需要加上額外的4字節(jié)的掩碼。相對于HTTP請求每次都要攜帶完整的頭部,此項開銷顯著減少了。
  • 更強的實時性。由于協(xié)議是全雙工的,所以服務器可以隨時主動給客戶端下發(fā)數(shù)據(jù)。相對于HTTP請求需要等待客戶端發(fā)起請求服務端才能響應,延遲明顯更少;即使是和Comet等類似的長輪詢比較,其也能在短時間內更多次地傳遞數(shù)據(jù)。
  • 保持連接狀態(tài)。與HTTP不同的是,Websocket需要先創(chuàng)建連接,這就使得其成為一種有狀態(tài)的協(xié)議,之后通
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容