工程造價(jià)定額在哪查網(wǎng)站推廣優(yōu)化設(shè)計(jì)方案
概述
從0研究一下Golang已經(jīng)Golang的微服務(wù)生態(tài)體系,Golang的微服務(wù)首先要從Rpc開(kāi)始,在升級(jí)到Grpc,詳細(xì)介紹這些技術(shù)點(diǎn)都在解決什么技術(shù)問(wèn)題。
Rpc
- Rpc (Remote Procedure Call) 遠(yuǎn)程過(guò)程調(diào)用,簡(jiǎn)單的理解是一個(gè)節(jié)點(diǎn)請(qǐng)求另一個(gè)節(jié)點(diǎn)提供的服務(wù)。
- 對(duì)應(yīng)Rpc的是本地過(guò)程調(diào)用,函數(shù)調(diào)用時(shí)最常見(jiàn)的本地過(guò)程調(diào)用。
- 將本地過(guò)程調(diào)用變成遠(yuǎn)程過(guò)程調(diào)用會(huì)面臨各種問(wèn)題。
遠(yuǎn)程調(diào)用過(guò)程面臨的問(wèn)題?
1.Call ID映射。
我們?cè)趺锤嬖V遠(yuǎn)程機(jī)器我們要調(diào)用的函數(shù)ID呢?再本地調(diào)用中,函數(shù)體是直接通過(guò)指針來(lái)指定的,我們調(diào)用function,編譯器就自動(dòng)幫我們調(diào)用它相應(yīng)的函數(shù)指針。
但是在遠(yuǎn)程調(diào)用中,函數(shù)指針是不行的,因?yàn)閮蓚€(gè)進(jìn)程的地址空間是完全不一樣的。所以,再RPC中,所有的函數(shù)都必須有自己的一個(gè)ID。這個(gè)ID在所有的進(jìn)程中都是唯一確定的??蛻舳嗽谧鲞h(yuǎn)程調(diào)用時(shí),必須附上這個(gè)ID。然后還需要再客戶端和服務(wù)端分別維護(hù)一個(gè){函數(shù) <–> Call ID}的對(duì)應(yīng)表。兩者的表不一定需要完全相同,但相同函數(shù)對(duì)應(yīng)的Call ID必須相同。
當(dāng)客戶端需要進(jìn)行遠(yuǎn)程調(diào)用時(shí),它就查一下這個(gè)表,找出相應(yīng)的Call ID,然后把它傳給服務(wù)端,服務(wù)端也通過(guò)查表,來(lái)確定客戶端需要調(diào)用的函數(shù),然后執(zhí)行相應(yīng)的函數(shù)的代碼。
2.序列化和反序列化
客戶端怎么把參數(shù)值傳給遠(yuǎn)程函數(shù)呢?在本地調(diào)用中,我們只需要把參數(shù)壓到棧里,然后讓函數(shù)自己去棧里讀就行。但是在遠(yuǎn)程過(guò)程調(diào)用時(shí),客戶端跟服務(wù)端是不同的進(jìn)程,不能通過(guò)內(nèi)存來(lái)傳遞參數(shù)。甚至有時(shí)候客戶端和服務(wù)端使用的不是同一種編程語(yǔ)言,這時(shí)候就需要客戶端和服務(wù)端把參數(shù)先轉(zhuǎn)成一個(gè)字節(jié)流,傳給服務(wù)端后再把字節(jié)流轉(zhuǎn)成自己能讀懂的格式,這個(gè)過(guò)程叫做序列化和反序列化。
3.網(wǎng)絡(luò)傳輸
遠(yuǎn)程調(diào)用往往用在網(wǎng)絡(luò)上,客戶端和服務(wù)端是通過(guò)網(wǎng)絡(luò)連接的。所有的數(shù)據(jù)都需要通過(guò)網(wǎng)絡(luò)傳輸,因此就需要一個(gè)網(wǎng)絡(luò)傳輸層。網(wǎng)絡(luò)傳輸層需要把Call ID和序列化后的參數(shù)字節(jié)流傳給服務(wù)端,然后在把序列化后的調(diào)用結(jié)果返回客戶端,只要能完成這兩者的,都可以作為傳輸層使用。
因此,它所使用的協(xié)議其實(shí)是不限的,能完成傳輸就行。盡管大部分Rpc框架都使用Tcp協(xié)議,但其實(shí)Udp也可以,gRPC干脆使用了Http2。
Rpc框架需要解決哪些問(wèn)題
Client端要解決的問(wèn)題:
1.將這個(gè)調(diào)用映射為Call ID,這里假設(shè)用最簡(jiǎn)單的字符串當(dāng)Call ID的方法
2.將Call ID a和b序列化,可以直接將他們的值以二進(jìn)制形式打包
3.把2中得到的數(shù)據(jù)包發(fā)送給ServerAddr,這需要網(wǎng)絡(luò)傳輸層
4.等待服務(wù)器返回結(jié)果
5.如果服務(wù)器調(diào)用成功,那么被結(jié)果反序列化,并獻(xiàn)給total
Server端解決的問(wèn)題:
1.在本地維護(hù)一個(gè)Call ID到函數(shù)指針的映射call_id_map,可以用dict完成
2.等待請(qǐng)求,包括多線程的并發(fā)處理能力
3.得到一個(gè)請(qǐng)求后,將其數(shù)據(jù)包反序列化,得到Call ID
4.通過(guò)在call_id_map中查找,得到相應(yīng)的函數(shù)指針
5.將aherb反序列化后,本地調(diào)用add函數(shù),得到結(jié)果
6.將結(jié)果序列化后通過(guò)網(wǎng)絡(luò)返回給Client
在上面的整個(gè)流程中,估計(jì)有部分同學(xué)看到了熟悉的計(jì)算機(jī)網(wǎng)絡(luò)的流程和web服務(wù)器的定義,所以要實(shí)現(xiàn)一個(gè)Rpc框架,其實(shí)只需要按以上流程實(shí)現(xiàn)就基本完成了。
其中:
- Call ID映射可以直接使用函數(shù)字符串,也可以使用整數(shù)ID,映射表一般就是一個(gè)哈希表。
- 序列化和反序列化可以自己寫,也可以使用Protobuf或者FlatBuffers之類的。
- 網(wǎng)絡(luò)傳輸庫(kù)可以自己寫socket,或者用asio,ZeroMQ,Netty之類。