開(kāi)啟Client監(jiān)聽(tīng)
Client監(jiān)聽(tīng)通常監(jiān)聽(tīng)在2379端口上,用于對(duì)外提供http+grpc服務(wù),如etcdctl客戶端等
http監(jiān)聽(tīng)
普通http監(jiān)聽(tīng)會(huì)注冊(cè)一些rest api
/debug/vars
/version
/metrics
/health
grpc監(jiān)聽(tīng)
grpc基于http2協(xié)議
grpc層也實(shí)現(xiàn)了一些keepalive策略
定義 服務(wù)器端主動(dòng)發(fā)送 ping 的時(shí)間間隔,用來(lái)確認(rèn)客戶端還在;Timeout: ping 發(fā)出后多長(zhǎng)時(shí)間沒(méi)收到 ACK 就斷開(kāi)連接
grpc.KeepaliveParams(keepalive.ServerParameters{
Time: e.cfg.GRPCKeepAliveInterval, // 2h
Timeout: e.cfg.GRPCKeepAliveTimeout, // 20s
}
限制客戶端的 ping 頻率,防止 DoS 攻擊或?yàn)E用資源(否則斷開(kāi)連接)
grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{
MinTime: e.cfg.GRPCKeepAliveMinTime, // 5s
PermitWithoutStream: false,
}
grpc中的注冊(cè)方法分為兩類:
Unary:類似HTTP rest請(qǐng)求,一次請(qǐng)求,一次響應(yīng);但仍然是基于http2的,可以在保持連接的時(shí)候做到tcp連接復(fù)用,在同一個(gè)tcp連接上復(fù)用http多路傳輸
Stream:流式請(qǐng)求,連接不斷開(kāi),采用推送的方式,類似SSE
在注冊(cè)方法的時(shí)候可以選擇注冊(cè)為Unary方法或者stream方法,在調(diào)用的時(shí)候通過(guò)不同的攔截器進(jìn)行處理
srv, knownService := s.services[service]
if knownService {
if md, ok := srv.methods[method]; ok {
s.processUnaryRPC(ctx, stream, srv, md, ti)
return
}
if sd, ok := srv.streams[method]; ok {
s.processStreamingRPC(ctx, stream, srv, sd, ti)
return
}
}
grpc監(jiān)聽(tīng)和http監(jiān)聽(tīng)是監(jiān)聽(tīng)在同一個(gè)端口上的,通過(guò) cmux 多路復(fù)用器同時(shí)運(yùn)行 gRPC 和 HTTP 服務(wù)
m := cmux.New(sctx.l)
server = m.Serve
err = server()
在處理請(qǐng)求時(shí),對(duì)于http2請(qǐng)求并且請(qǐng)求header中包含Content-Type=application/grpc的將其轉(zhuǎn)發(fā)到grpc服務(wù)、否則將其轉(zhuǎn)發(fā)到http服務(wù)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
grpcServer.ServeHTTP(w, r)
} else {
otherHandler.ServeHTTP(w, r)
}
})
grpc注冊(cè)的服務(wù)端點(diǎn):
-
KVServer:服務(wù)名稱:/etcdserverpb.KV/ ;注冊(cè)的Unary方法包括:
- Range
- Put
- DeleteRange
- Txn
- Compact
-
WatchServer:服務(wù)名稱:/etcdserverpb.Watch/ ;注冊(cè)的Stream方法包括:
- Watch
-
LeaseServer:服務(wù)名稱:/etcdserverpb.Lease/ ;注冊(cè)的Unary方法包括:
- LeaseGrant
- LeaseRevoke
- LeaseTimeToLive
- LeaseLeases
注冊(cè)的Stream方法包括: - LeaseKeepAlive
-
ClusterServer:服務(wù)名稱:/etcdserverpb.Cluster/ ;注冊(cè)的Unary方法包括:
- MemberAdd
- MemberRemove
- MemberUpdate
- MemberList
- MemberPromote
AuthServer:服務(wù)名稱:/etcdserverpb.Auth/ ;注冊(cè)的Unary方法包括:AuthEnable、AuthDisable、UserAdd、RoleAdd等
-
HealthServer:服務(wù)名稱:/grpc.health.v1.Health/ ;注冊(cè)的Unary方法包括:
- Check
- List
注冊(cè)的Stream方法包括: - Watch
-
MaintenanceServer:服務(wù)名稱:/etcdserverpb.Maintenance/ ;注冊(cè)的Unary方法包括:
- Alarm、Status、Defragment、Hash、HashKV、MoveLeader、Downgrade
注冊(cè)的Stream方法包括: - Snapshot
- Alarm、Status、Defragment、Hash、HashKV、MoveLeader、Downgrade
-
ElectionServer:服務(wù)名稱:/v3electionpb.Election/ ;注冊(cè)的Unary方法包括:
- Campaign、Proclaim、Leader、Resign
注冊(cè)的Stream方法包括: - Observe
- Campaign、Proclaim、Leader、Resign
-
LockServer:服務(wù)名稱:/v3lockpb.Lock/ ;注冊(cè)的Unary方法包括:
- Lock、Unlock
grpc-gateway
grpc-gateway用于將grpc上注冊(cè)的方法同時(shí)注冊(cè)為http rest端點(diǎn),能夠使用http rest 方式來(lái)請(qǐng)求grpc服務(wù)
將自身做為grpc客戶端,dial client監(jiān)聽(tīng)的端口,用于接收http請(qǐng)求,并轉(zhuǎn)換為grpc請(qǐng)求轉(zhuǎn)發(fā)到 client監(jiān)聽(tīng)的端口
conn, err := dial(ctx)
構(gòu)建grpc gateway,定義接收到的http請(qǐng)求參數(shù)反序列話方式,UseProtoNames直接使用protobuf定義的字段名稱而不是駝峰命名等
gwmux := gw.NewServeMux(
gw.WithMarshalerOption(gw.MIMEWildcard,
&gw.HTTPBodyMarshaler{
Marshaler: &gw.JSONPb{
MarshalOptions: protojson.MarshalOptions{
UseProtoNames: true,
EmitUnpopulated: false,
},
UnmarshalOptions: protojson.UnmarshalOptions{
DiscardUnknown: true,
},
},
},
),
)
將grpc服務(wù)注冊(cè)為http rest 端點(diǎn),對(duì)應(yīng)的就是上述grpc服務(wù)上注冊(cè)的那些服務(wù)方法
handlers := []registerHandlerFunc{
etcdservergw.RegisterKVHandler,
etcdservergw.RegisterWatchHandler,
etcdservergw.RegisterLeaseHandler,
etcdservergw.RegisterClusterHandler,
etcdservergw.RegisterMaintenanceHandler,
etcdservergw.RegisterAuthHandler,
v3lockgw.RegisterLockHandler,
v3electiongw.RegisterElectionHandler,
}
注冊(cè)格式:
- 全部使用POST方式注冊(cè)
mux.Handle(http.MethodPost, pattern_KV_Range_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
//
})
- 映射路徑解析:[]int{2, 0, 2, 1, 2, 2} 是 opcode(操作碼),2 對(duì)應(yīng)的是入棧操作,操作的是[]string{"v3", "kv", "range"}這個(gè)數(shù)組元素,
2,0相當(dāng)于把0號(hào)元素v3入棧,2,1相當(dāng)于把1號(hào)元素kv入棧,2,2相當(dāng)于把2號(hào)元素range入棧,最終構(gòu)成的請(qǐng)求路徑就是:/v3/kv/range
pattern_KV_Range_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "kv", "range"}, ""))
因此可以通過(guò)POST http://127.0.0.1:2379/v3/kv/range來(lái)請(qǐng)求grpc方法:/etcdserverpb.KV/Range
還可以通過(guò)websocket來(lái)請(qǐng)求grpc方法,這里自動(dòng)將”/v3“開(kāi)頭的 WebSocket 請(qǐng)求轉(zhuǎn)成 HTTP POST請(qǐng)求,進(jìn)而在轉(zhuǎn)換為grpc請(qǐng)求
if gwmux != nil {
httpmux.Handle(
"/v3/",
wsproxy.WebsocketProxy(
gwmux,
wsproxy.WithRequestMutator(
// Default to the POST method for streams
func(_ *http.Request, outgoing *http.Request) *http.Request {
outgoing.Method = "POST"
return outgoing
},
),
wsproxy.WithMaxRespBodyBufferSize(0x7fffffff),
wsproxy.WithLogger(wsProxyZapLogger{sctx.lg}),
),
)
}
總結(jié)
整個(gè)Client的監(jiān)聽(tīng)中,可以歸納為這類型的請(qǐng)求:
普通http rest請(qǐng)求,如/version、/metrics等,直接交給 http 服務(wù)器處理
grpc請(qǐng)求,這部分請(qǐng)求是http2的,并且header種攜帶
Content-Type=application/grpc,直接交給grpc 服務(wù)器處理http rest請(qǐng)求,請(qǐng)求類路是/v3開(kāi)頭的,請(qǐng)求方法是POST,這部分請(qǐng)求交給grpc-gateway處理,grpc-gateway會(huì)將自身作為grpc client,進(jìn)而將http請(qǐng)求轉(zhuǎn)發(fā)給grpc服務(wù)處理
websocket 請(qǐng)求,請(qǐng)求路徑是/v3開(kāi)頭的,這部分請(qǐng)求也交給grpc-gateway處理,grpc-gateway會(huì)自動(dòng)把websocket請(qǐng)求轉(zhuǎn)換為 http post請(qǐng)求,進(jìn)而交給grpc服務(wù)處理