动态

详情 返回 返回

騰訊 tRPC-Go 教學——(2)trpc HTTP 能力 - 动态 详情

上一篇文章 中我們快速搭建了一個 http API 服務,並且我們可以看到,對外提供了 URL query 和 application/json 兩種服務模式。那麼實際上,我們到底實現了什麼、並且能夠做些什麼?讀者可能還是沒有直觀的感受,因此必要先來簡單 review 一下。就讓我們先放下敲代碼的小手,一起看看剛剛寫出來的都是些什麼玩意兒吧。

系列文章

  • 騰訊 tRPC-Go 教學——(1)搭建服務
  • 騰訊 tRPC-Go 教學——(2)trpc HTTP 能力
  • 騰訊 tRPC-Go 教學——(3)微服務間調用
  • 騰訊 tRPC-Go 教學——(4)tRPC 組件生態和使用
  • 騰訊 tRPC-Go 教學——(5)filter、context 和日誌組件
  • 騰訊 tRPC-Go 教學——(6)服務發現
  • 騰訊 tRPC-Go 教學——(7)服務配置和指標上報
  • 騰訊 tRPC-Go 教學——(8)通過泛 HTTP 能力實現和觀測 MCP 服務

先説説內部版和開源版的 tRPC

首先要説明的是,騰訊內部使用的 tRPC 與開源版的 tRPC,雖然並不是 100% 相同,但大部份的代碼和基本的功能是基本一致。官方對開源版的 PR 比較謹慎,在我提出的 PR 中維護者也提及了這一點,這為的就是儘量保持內部與外部版本的儘量一致性。

筆者雖然是騰訊員工,但並不是 tRPC 團隊的開發者,而只是內部版和開源版雙邊的使用者。撰此係列文章,我的資料主要來源於以下這些:

  1. 對開源版的代碼閲讀
  2. 內部版和開源版邏輯一致的、可脱敏的資料
  3. 內部版本的一些使用經驗和我們團隊的經驗
  4. 個人喜好和觀點(當然我的個人觀點多少也是會影響第 3 條的團隊決策的哈哈)

所以,也還請讀者不要將筆者視作 tRPC 官方,就當作是一名普通程序員就行了~~


從 proto 樁代碼説起

業務代碼與 trpc 服務的綁定

我們看看例子中的 service:

service HelloWorld {
  rpc Hello(HelloRequest) returns (HelloResponse); // @alias=/demo/Hello
}

平平無奇的一個 service,經過 trpc 工具編譯後,生成了 echo.pb.goecho.trpc.go 兩個文件。前者和使用 gRPC 的 proto-gen-go 工具生成的差別不大,我們就不用講了。我們看看後面那個。

echo.trpc.go 文件中,trpc 工具生成了一個服務端接口:

type HelloWorldService interface {
    Hello(ctx context.Context, req *HelloRequest) (*HelloResponse, error) // @alias=/demo/Hello
}

// ......

func RegisterHelloWorldService(s server.Service, svr HelloWorldService) {
    if err := s.Register(&HelloWorldServer_ServiceDesc, svr); err != nil {
        panic(fmt.Sprintf("HelloWorld register error:%v", err))
    }
}

很好理解,RegisterHelloWorldService 函數將 trpc 服務和我們的具體業務實現綁定在了一起,啓動服務就對接上了業務邏輯。這跟 gRPC 的思路是一致的。

alias 關鍵字的作用

繼續往下看,可以留意到下面這段代碼:

var HelloWorldServer_ServiceDesc = server.ServiceDesc{
    ServiceName: "demo.simplest.HelloWorld",
    HandlerType: ((*HelloWorldService)(nil)),
    Methods: []server.Method{
        {
            Name: "/demo/Hello",
            Func: HelloWorldService_Hello_Handler,
        },
        {
            Name: "/demo.simplest.HelloWorld/Hello",
            Func: HelloWorldService_Hello_Handler,
        },
    },
}

這裏其實就是前文 @alias 的作用了。顯然,trpc 默認是使用 package/method 的格式定義一個接口方法的路徑;而 alias 的作用則是在這基礎上再額外註冊了一個路徑。上面的這兩個路徑,都可以直接通過 http 訪問到。


trpc_go.yaml 配置

上文提到,trpc 服務啓動需要搭配一個 yaml 配置文件。tRPC 的文檔會告訴你默認使用與工作目錄同級的 trpc_go.yaml 文件,但實際上考慮到在 Kubernetes 中掛載的需要,我們往往會將配置文件獨立在一個目錄下,而可執行文件在另一個目錄下,再配合日誌(也需要掛載,方便日誌採集),這就構成了這樣的一個結構:

  • 工作目錄

    • bin/ - 可執行文件,包括服務程序和一些啓動腳本
    • conf/ - 服務配置文件,主要就是 trpc_go.yaml
    • log/ - 日誌文件。這個我後文再講

由於配置文件與可執行文件或工作目錄不在同一個路徑下,因此我們啓動服務的時候經常需要 -conf 參數制定配置文件的路徑。

接下來,我們看一下配置文件中的內容:

server:
  service:
    - name: demo.simplest.HelloWorld
      nic: eth0
      # ip: 127.0.0.1
      port: 8000
      network: tcp
      protocol: http
      timeout: 1800

可以看明白的是:我們註冊了一個叫做 demo.simplest.HelloWorld 的服務,監聽端口 8000,服務工作在 tcp 協議上,應用層採用 http 協議,超時時間是 1800 毫秒。這幾個參數我們都是需要好好説道説道的。

name

trpc 的文檔中,並沒有詳細説明這個 name 應該取什麼值。一般而言,這個 name 值等於你 proto 中定義的 package 名 + 服務名。

不過你不用記這個規則。實際上我們直接以生成的 trpc.go 文件為準。我們打開之前我們生成的 echo.trpc.go,搜索 Register,很快可以找打下面的代碼段:

// RegisterHelloWorldService registers service.
func RegisterHelloWorldService(s server.Service, svr HelloWorldService) {
    if err := s.Register(&HelloWorldServer_ServiceDesc, svr); err != nil {
        panic(fmt.Sprintf("HelloWorld register error:%v", err))
    }
}

找到 s.Register 的第一個參數 HelloWorldServer_ServiceDesc 的定義,對,就是前面我們講 alias 關鍵字的時候看到的那個結構體中的 ServiceName 字段,字段的值就是我們應該填在配置的 name 字段中的值。

此外, trpc 的官方文檔會告訴你,如果當前進程僅定義了一個 service(這是絕大部分微服務的情況),那麼實際上這個 service name 字段定義成什麼都沒關係,因為 trpc 會自動尋找這唯一的 service 配置,並且自動適配它。儘管如此,筆者依然不建議你因為有了這一條 feature,就隨便寫,咱們還是按規範的好。後面這個規範在作為客户端的時候也需要遵從,等用到了筆者再提吧。

nic、ip 和 port

ip 就是表示服務應該監聽在哪一個 ip 上;而 nic 是 network interface card 的縮寫,表示監聽哪一個網卡。在生產環境中,應該是 nic 參數用得比較多,而在開發 / 測試的時候,當服務並不是部署在 Kubernetes 上的時候,ip 則提供了更大的自由度。

port 很好理解,表示監聽的端口

network 和 protocol

網絡類型參數就是 tcpudp 兩類。兩者能夠支持的應用層協議不同。除非是極為極致的性能和高併發需求,否則我們一般還是固定使用 tcp。至於 protocol 參數,一般就是在 httptrpc 這兩者之間選。當然也支持 grpc,讀者可以試一下,筆者沒有實際用過。

在前面的例子中,我們部署了一個 HTTP 服務,因此這裏我們填寫的是 http。如果填寫 trpcgrpc,那麼無需修改任何業務邏輯, 框架會自動字改為配置所指定的服務協議。

timeout

毫秒級的超時時間。這個參數會影響在 context 中的時間,如果配置了超時時間,trpc 框架在調用業務邏輯的時候,會給 context 加上這個 timeout。


tRPC 的 HTTP 服務模式

上一篇文章我們分別是用了兩種模式來調用 Hello 方法,通過這個例子我們可以知道,trpc 服務框架會自動適配前端不同的調用方式、解析數據並調用業務邏輯。這一點對我們來説是非常舒服的,這讓開發者們不用去關心業務無關的東西,一切都交給協議和框架解決。

我們對外提供的 HTTP 服務格式,常見的是以下三種:

  1. application/json: 前端可以使用 GET 或 POST 方式,在 body 中放置 JSON 數據作為入參
  2. application/proto: 與前面一樣,不同的是 body 中是 protobuf 編碼後的字節流
  3. 如果沒有指定 body 格式,那麼 trpc 也會嘗試從 url query 參數中尋找協議所需的參數

總而言之,如果是定在最前面的 HTTP API,最好跟前端同學約定,一般情況下就使用 POST application/json 把,畢竟 URL query 遇到複雜數據類型比較麻煩,而 proto 前端不太處理了。


下一步

本文,我們簡單介紹了上一篇文章中提到的 hello world 服務的各種參數。至此,我們開啓了一個最簡單的服務,對前端提供了最簡單的響應。

然而,當我引入文中的這些概念之後,聰明的讀者們肯定有更多的問題。而這些功能,也只不過是 tRPC-Go 眾多功能的冰山一角上的一粒冰片。

接下來,我會帶領大家開始拉起一個真正的微服務,構建一個完整的系統。我也會列出各種 trpc 服務倉庫的目錄組織形式,這個目錄組織形式也方便對微服務進行單體化構建。與 trpc 官方給出的建議、和 trpc 工具自動生成的不同,這也就是為什麼我不使用 trpc 工具的默認行為。

同時,trpc 的周邊服務生態也是必不可少的一環,必然需要一併講述。

此外,我也會詳細説明我是如何在 tRPC-Go 中踐行 微服務+單體 架構的,比起之前我文章中乾巴巴的描述,上代碼會來得更清楚。


本文章採用 知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議 進行許可。

原作者: amc,原文發佈於騰訊雲開發者社區,也是本人的博客。歡迎轉載,但請註明出處。

原文標題:《手把手 tRPC-Go 教學——(2)trpc HTTP 能力》

發佈日期:2024-01-16

原文鏈接:https://cloud.tencent.com/developer/article/2379587。

CC BY-NC-SA 4.0 DEED.png

user avatar soroqer 头像 momodel 头像 daqianduan 头像 daoqiangburudelianou 头像 shouke 头像 wuliaodeliema 头像 aigoto 头像 anonymous_5f6b14f11289a 头像 caigaobadoudetangyuan 头像 tanking 头像 aphysia 头像 mangrandedanche 头像
点赞 18 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.