前言
在看下面時,我們先來 分析下入口方法,trpc.NewServer都做了拿一些事情
1.讀取配置文件,這里會讀取用戶設(shè)置的配置文件路徑在(./trpc_go.yaml)和設(shè)置默認配置(網(wǎng)絡(luò)類型tcp,協(xié)議類型trpc),然后設(shè)置到Config對象中
2.把配置通過localstore的方式設(shè)置到全局變量中
3.開始設(shè)置用戶自定義插件,亮點功能
4.初始化服務(wù)和rpc連接的最大并發(fā)數(shù)
5.關(guān)閉插件方法,前提是插件必須實現(xiàn)了closes接口
- 根據(jù)以上,我們對trpc框架進行三個方面的講解
1.trpc框架配置文件加載方式
trpc配置文件采用yaml格式,文件默認目錄在 ./trpc_go.yaml下,所有自定義配置都需要寫在這個yaml文件中,所有支持用戶自定義的配置都可以參考結(jié)構(gòu)體config
// Config is the configuration for trpc, which can be divided into 4 parts:
// 1. Global config.
// 2. Server config.
// 3. Client config.
// 4. Plugins config.
type Config struct {
Global struct {
Namespace string `yaml:"namespace"` // Namespace for the configuration.
EnvName string `yaml:"env_name"` // Environment name.
ContainerName string `yaml:"container_name"` // Container name.
LocalIP string `yaml:"local_ip"` // Local IP address.
EnableSet string `yaml:"enable_set"` // Y/N. Whether to enable Set. Default is N.
// Full set name with the format: [set name].[set region].[set group name].
FullSetName string `yaml:"full_set_name"`
// Size of the read buffer in bytes. <=0 means read buffer disabled. Default value will be used if not set.
ReadBufferSize *int `yaml:"read_buffer_size,omitempty"`
}
Server struct {
App string `yaml:"app"` // Application name.
Server string `yaml:"server"` // Server name.
BinPath string `yaml:"bin_path"` // Binary file path.
DataPath string `yaml:"data_path"` // Data file path.
ConfPath string `yaml:"conf_path"` // Configuration file path.
Admin struct {
IP string `yaml:"ip"` // NIC IP to bind, e.g., 127.0.0.1.
Nic string `yaml:"nic"` // NIC to bind.
Port uint16 `yaml:"port"` // Port to bind, e.g., 80. Default is 9028.
ReadTimeout int `yaml:"read_timeout"` // Read timeout in milliseconds for admin HTTP server.
WriteTimeout int `yaml:"write_timeout"` // Write timeout in milliseconds for admin HTTP server.
EnableTLS bool `yaml:"enable_tls"` // Whether to enable TLS.
RPCZ *RPCZConfig `yaml:"rpcz"` // RPCZ configuration.
}
Transport string `yaml:"transport"` // Transport type.
Network string `yaml:"network"` // Network type for all services. Default is tcp.
Protocol string `yaml:"protocol"` // Protocol type for all services. Default is trpc.
Filter []string `yaml:"filter"` // Filters for all services.
StreamFilter []string `yaml:"stream_filter"` // Stream filters for all services.
Service []*ServiceConfig `yaml:"service"` // Configuration for each individual service.
// Minimum waiting time in milliseconds when closing the server to wait for deregister finish.
CloseWaitTime int `yaml:"close_wait_time"`
// Maximum waiting time in milliseconds when closing the server to wait for requests to finish.
MaxCloseWaitTime int `yaml:"max_close_wait_time"`
Timeout int `yaml:"timeout"` // Timeout in milliseconds.
}
Client ClientConfig `yaml:"client"` // Client configuration.
Plugins plugin.Config `yaml:"plugins"` // Plugins configuration.
}
1.1 trpc配置文件加載流程和干了哪些事情
簡單來說干了以下幾件事情
-
1.獲取配置文件的默認目錄,并且解析目錄到 ServerConfigPath 中
-
2.讀取yaml文件,把用戶自定義配置讀取到config結(jié)構(gòu)體對象中,用于后續(xù)的使用和初始化
-
3.初始化用戶配置
1.讀取配置文件中的ip地址,如果是網(wǎng)卡名稱,則獲取網(wǎng)卡對應(yīng)的ip地址,然后設(shè)置成服務(wù)的ip地址
2.讀取用戶自定義配置 設(shè)置網(wǎng)絡(luò)協(xié)議類型和ip:prot地址,設(shè)置連接最大存活時間和請求超時時間
3.讀取自定義客戶端網(wǎng)絡(luò)配置,并且設(shè)置到config對象中
- 4.采用atomic,Value的local store 寫時復制技術(shù)(線程安全)把config配置文件加載到全局對象 globalConfig 之中
2.插件化實現(xiàn)原理和簡單demo
trpc-go實現(xiàn)插件化簡單來說就分為三步
1.加載插件,設(shè)置一個插件隊列,按照用戶自定義的順序去加載讀取插件,并且把插件默認狀態(tài)變成不可用,使用方法 loadPlugins()
2.從插件隊列中取出插件,進行啟動,使用方法setupPlugins()
2.1.首先判斷插件的依賴關(guān)系,把先依賴的插件先啟動
2.2.一個一個的去啟動插件并且執(zhí)行,然后把插件狀態(tài)變成可用
3.執(zhí)行插件執(zhí)行結(jié)束關(guān)閉方法,onFinish() 如果你不想插件結(jié)束可以不用實現(xiàn)這個方法,該方法不是必選
2.1 加載插件方法分析 loadPlugins
1.首先設(shè)置默認支持最大插件個數(shù)1000、初始化channel插件隊列,設(shè)置插件默認狀態(tài)為false
2.讀取用戶配置文件,獲取到用戶自定義插件配置
3.通過用戶配置的yaml文件,在factory中獲取到具體初始化完成的插件
4.把初始化完成的插件狀態(tài)變成 false
注意:根據(jù)代碼可以分析出來 可以注冊多個類型的插件,每個類型又可以綁定多個插件,最小唯獨是插件類型+插件名稱,也就是一個具體的插件
2.2 插件啟動和插件啟動校驗 setupPlugins()
1.按照順序讀取插件隊列中的插件,插件隊列是利用channel實現(xiàn)的,先進先出隊列
2.檢查當前插件依賴的插件是否已經(jīng)初始化完成,如果沒有完成,則會把當前插件取出來放在channel隊尾
如果想自定義依賴實現(xiàn)接口 Depender 即可,注意這里分為強依賴和弱依賴
強依賴:如果插件a強依賴插件b,那么插件[]b們必須存在,插件a會在插件b初始化完成后再初始化
弱依賴:如果插件a弱依賴插件[]b們,只需要有一個插件存在就可以
3.如果插件檢查完畢通過后,trpc會調(diào)用 插件實現(xiàn)的 setup()方法去運行插件,而且把用戶自定義的配置信息傳遞過去
4.最后檢驗插件是否初始化和執(zhí)行完畢
如果沒有完畢,會返回一個錯誤 cycle depends, not plugin is setup,服務(wù)將不會啟動
如果完畢,則會返回插件隊列和插件關(guān)閉函數(shù),前提是插件實現(xiàn)了插件關(guān)閉函數(shù)
2.3. 如果插件執(zhí)行完畢,實現(xiàn) OnFinish()方法,關(guān)閉插件;當然這個方法可以不用實現(xiàn)
2.4 流程總結(jié)
2.1 首先實現(xiàn) trpc提供的 Factory接口,完成自定義插件設(shè)計
2.2 插件中init() 方法里面調(diào)用 trpc提供的 plugins,Register() 方法 把插件注冊到 局部變量 var plugins = make(map[string]map[string]Factory)中
2.3 trpc框架在程序啟動的時候會調(diào)用 loadPlugins()方法和plugins中的 get()方法,把插件從factory中獲取出來
- 1.首先實現(xiàn) trpc提供的 Factory接口,完成自定義插件設(shè)計
Factory接口有兩個方法 Type()和SetUp()
Type()的作用是設(shè)置插件的類型,也就是插件的名稱,要保持唯一
SetUp()的作用是實現(xiàn)插件具體的業(yè)務(wù)邏輯,trpc框架會進行調(diào)用
- 2.在插件的init()方法中調(diào)用 trpc提供的 plugins,Register() 方法 把插件注冊到 局部變量 var plugins = make(map[string]map[string]Factory)中
- 3.trpc框架在程序啟動的時候會調(diào)用 loadPlugins()方法和plugins中的 get()方法,把插件從factory中獲取出來,放入到channel隊列中依次執(zhí)行
2.5 實現(xiàn)插件demo,這里拿日志上報插件舉例子
插件demo
package main
import (
"context"
"trpc.group/trpc-go/trpc-go"
"trpc.group/trpc-go/trpc-go/filter"
"trpc.group/trpc-go/trpc-go/plugin"
)
const (
pluginName = "metric_log"
pluginType = "tracing"
)
// Config 插件配置
type MetricLogConfig struct {
ServiceName string `yaml:"service_name"`
}
var metricLogConfigPluginCfg = MetricLogConfig{}
func init() {
plugin.Register(pluginName, &MetricLogPlugin{})
}
// EduTracingPlugin
type MetricLogPlugin struct {
}
// PluginType 返回插件類型
func (e *MetricLogPlugin) Type() string {
return pluginType
}
// Setup 裝載tracer實現(xiàn)
func (e *MetricLogPlugin) Setup(name string, decoder plugin.Decoder) error {
cfg := MetricLogConfig{}
//1。解析在trpc.yaml中自定義的插件配置文件
if err := decoder.Decode(&cfg); err != nil {
return err
}
//2.設(shè)置插件相關(guān)的配置參數(shù)和網(wǎng)絡(luò)類型
if cfg.ServiceName == "" {
cfg.ServiceName = pluginName
}
metricLogConfigPluginCfg = cfg
//3.創(chuàng)建攔截器ServerFilterRaw 和ClientFilterRaw,攔截rpc請求,然后注冊到trpc攔截器中
filter.Register(name, ServerFilterRaw(e), ClientFilterRaw(e))
return nil
}
func ServerFilterRaw(e *MetricLogPlugin) filter.ServerFilter {
return func(ctx context.Context, req interface{}, handler filter.ServerHandleFunc) (rsp interface{}, err error) {
// 設(shè)置日志中的上下文處理.
msg := trpc.Message(ctx)
// 業(yè)務(wù)處理.
rsp, err = handler(ctx, req)
// 日志上報
UploadLog(ctx, msg)
return rsp, err
}
}
func ClientFilterRaw(e *MetricLogPlugin) filter.Filter {
return func(ctx context.Context, req, rsp interface{}, handler filter.HandleFunc) (err error) {
msg := trpc.Message(ctx)
// 業(yè)務(wù)處理.
err = handler(ctx, req, rsp)
// 日志上報
UploadLog(ctx, msg)
return err
}
}
使用方式在自己服務(wù)的main,go中引入即可
3.trpc-go rpc注冊、調(diào)用流程
trpc-go rpc 分成了三個部分
1.在 trpc.NewServer()中調(diào)用 NewServerWithConfig()方法,把用戶自定義服務(wù)端配置加載到service對象中
2.調(diào)用trpc工具生成的pb文件 如 pb.RegisterGoTrpcTestService(s.Service("trpc.test.svr"), service)把服務(wù)名稱和服務(wù)隊員的rpc方法注冊到server中
3.調(diào)用s.serve()方法,創(chuàng)建tpc連接進行 rpc方法調(diào)用
3.1 初始化sevice NewServerWithConfig()
1.讀取用戶自定義攔截器,進行攔截器去重
攔截器支持普通攔截器Filter和流式攔截器StreamFilter
2. 把網(wǎng)絡(luò)相關(guān)、攔截器相關(guān)等參數(shù)設(shè)置到service中
3.初始化Transport和ServeOptions對象
1.這里特別注意下 Transport對象,
該對象如果用戶在配置文件中沒有自定義,那么trpc框架會給他一個默認值,后續(xù)會使用該默認值進行rpc調(diào)用
2.這里特別注意下 s.opts.ServeOptions
把serve中的handler地址給了s.opts.ServeOptions,當在進行rpc方法注冊時,也會在s.opts.ServeOptions中注冊一份rpc方法表
3.2 注冊服務(wù)名稱和服務(wù)對應(yīng)的rpc方法到server中 service.Register()方法
Register(serviceDesc interface{}, serviceImpl interface{})
-
1.register方法有兩個入?yún)?shù),一個是serviceDesc代表注冊的服務(wù)名稱,一個是serviceImpl代表服務(wù)的rpc方法列表
-
2.register方法邏輯,主要就是初始化攔截器和注冊rpc方法到handler中
-
2.1 首先初始化流式攔截器
-
2.2 然后把serviceImpl中的方法依次注冊到server的Handlers中,注意這一步也會把對象注冊到service的opts.ServeOptions中
-
2.3 初始化普通攔截器
3.3 建立rpc連接,進行rpc方法調(diào)用 s.serve()
建立rpc連接,也是按照傳統(tǒng)的net包進行的,分別分為
1.創(chuàng)建監(jiān)聽器
2.等待客戶端連接
3.調(diào)用rpc方法處理數(shù)據(jù),然后把數(shù)據(jù)寫入到連接中
4.關(guān)閉連接
- 1.獲取初始化時的sevice對象,建立tcp連接,一個對象單獨建立一個連接
-
- 調(diào)用第一步初始化 Transport對象的ListenAndServe()方法,如果沒有設(shè)置默認的listener,就會根據(jù)用戶設(shè)置的networker類型來創(chuàng)建監(jiān)聽器
-
- 創(chuàng)建一個tcp的監(jiān)聽器,內(nèi)部用的就是net包的listne方法
- 創(chuàng)建一個tcp的監(jiān)聽器,內(nèi)部用的就是net包的listne方法
-
- 監(jiān)聽客戶端連接,如果沒有客戶端連接,Accept方法會阻塞
- 監(jiān)聽客戶端連接,如果沒有客戶端連接,Accept方法會阻塞
-
5.把rpc方法列表從opts.ServeOptions設(shè)置到conn.handler中去
文章來源:http://www.zghlxwxcb.cn/news/detail-771135.html
-
6.調(diào)用rpc方法處理請求,然后再把數(shù)據(jù)通過conn連接寫會到客戶端,寫完后關(guān)閉連接,整個流程結(jié)束
文章來源地址http://www.zghlxwxcb.cn/news/detail-771135.html
到了這里,關(guān)于go語言:騰訊終于開源trpc框架——對trpc-go源碼分析的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!