摘要:請(qǐng)注意閉包的返回值必需是。開(kāi)發(fā)者可以利用相關(guān)方法來(lái)自定義其內(nèi)容,會(huì)將閉包的返回值作為最終結(jié)果與其他后端服務(wù)的響應(yīng)合并,然后返回給訪問(wèn)層。注意不要忘記本身是一個(gè)閉包否則,無(wú)法模擬過(guò)期后重新生成另一個(gè)的情況。
dgate:an API Gateway based on Vert.x
dgate是基于Vertx的API Gateway。運(yùn)行dgate的命令如下:
java -jar dgate-version-fat.jar -Dconf=conf
其中的conf屬性用來(lái)指定運(yùn)行所需的配置文件。
conf的文件格式在說(shuō)明格式之前,先看一個(gè)例子:
apiGateway1 { port = 7000 login = "/login" urls { "/login" { required = ["sub", "password"] methods = [HttpMethod.GET, HttpMethod.POST] upstreamURLs = [ [ host: "localhost", port: 8080, url: "/login", after: { simpleResponse -> Map payload = [ sub: simpleResponse.payload.getString("sub"), name: simpleResponse.payload.getString("name"), role: simpleResponse.payload.getString("role") ] simpleResponse.payload.put("token", delegate.tokenGenerator.token(payload, 5)) simpleResponse } ] ] } "/summary" { expected { statusCode = 200 payload { eqLocations = [] opRateInLast30Days = [] myOrgs = [ ["name": "org1", "admin": false] ] } } } "/forward" { required = ["param1", "param2"] methods = ["GET", "POST"] upstreamURLs = [ [host: "localhost", port: 8080, url: "/test"] ] } "/composite" { required = ["param1", "param2"] methods = ["GET", "POST"] upstreamURLs = [ [host: "localhost", port: 8080, url: "/test1"], [host: "localhost", port: 8080, url: "/test2"] ] } } } apiGateway2 { port = 7001 host = "localhost" urls { "/mock" { expected { statusCode = 200 payload = [test: true] } } } }
dgate采用ConfigSlurper解析conf文件,因此其文件的語(yǔ)法實(shí)際上是Groovy語(yǔ)法。
conf文件由多個(gè)Api Gateway的定義組成,對(duì)于每個(gè)Gateway定義如下:
apiGatewayName { port //端口 host //綁定的ip或主機(jī)名,默認(rèn)0.0.0.0 login //后端login服務(wù)的配置 urls { URL配置(UrlConfig)列表 } //dgate暴露的url列表 }
對(duì)于每個(gè)URL配置,其結(jié)構(gòu)如下:
url { required //必需參數(shù)列表 methods //支持的HTTP Method upstreamURLs { 上游URL列表(UpStreamURL) } expected //期望返回值 }
其中:
required支持兩種格式:List和Map。前者適用于不區(qū)分HttpMethod時(shí)的參數(shù)驗(yàn)證,后者則可以針對(duì)不同的HttpMethod分別進(jìn)行設(shè)置。
List,如:required = ["sub", "password"]
Map,如:required = [get: ["param1"], post: ["param2"], delete: ["param3"]]
expected和upStreamURLs兩個(gè)屬性不能并存
expected主要用于mock模式,其目的是為了便于依賴dgate的訪問(wèn)層可以自行mock所需的響應(yīng),使得它們的開(kāi)發(fā)進(jìn)度受dgate開(kāi)發(fā)進(jìn)度的影響最小化。
對(duì)于expected的內(nèi)容:
statusCode和payload至少有一個(gè)
或針對(duì)具體的HTTP METHOD的返回值
對(duì)于每個(gè)上游URL,有3個(gè)屬性:host、port和url,其中url必需以"/"開(kāi)始
不同類型的API Gateway請(qǐng)求API Gateway的目的是作為后端Service的一個(gè)統(tǒng)一集中點(diǎn),簡(jiǎn)化訪問(wèn)層與后端Service的交互。說(shuō)得更簡(jiǎn)單點(diǎn),其作用非常類似Facade。
因此,API Gateway的主要作用就是:
接受訪問(wèn)層的請(qǐng)求
將請(qǐng)求轉(zhuǎn)發(fā)到對(duì)應(yīng)的后端服務(wù)
收集后端響應(yīng),統(tǒng)一組裝成response,然后返回給訪問(wèn)層
從大的方面講,發(fā)給API Gateway的請(qǐng)求分成兩類:
轉(zhuǎn)發(fā)給一個(gè)上游URL,即上面的forward
轉(zhuǎn)發(fā)給多個(gè)上游URL,即上面的Composite
這便是為何upStreamURLs是一個(gè)列表的主要原因。對(duì)于每種請(qǐng)求:
forward
訪問(wèn)層的request parameter直接轉(zhuǎn)發(fā)給后端service
后端service的response直接轉(zhuǎn)發(fā)給訪問(wèn)層
composite
訪問(wèn)層的request parameter會(huì)直接(并發(fā))轉(zhuǎn)發(fā)給每個(gè)后端服務(wù)
后端服務(wù)的response會(huì)merge在一起后再發(fā)給訪問(wèn)層
對(duì)于每個(gè)發(fā)往后端的請(qǐng)求,dgate會(huì)采用Circuit Breaker來(lái)防護(hù),防止出現(xiàn)由于某個(gè)后端service的失效導(dǎo)致整個(gè)系統(tǒng)雪崩。同時(shí),為了簡(jiǎn)單起見(jiàn),dgate和后端service之間只通過(guò)JSON進(jìn)行交互:
對(duì)于發(fā)往dgate的請(qǐng)求,dgate會(huì)將:request parameters、form變量、request body合并為一體,統(tǒng)一作為request body發(fā)往后端
對(duì)于后端響應(yīng),dgate只接受json格式
UpStreamURL的擴(kuò)展點(diǎn)如上節(jié)所說(shuō),API Gateway具備兩個(gè)職責(zé):轉(zhuǎn)發(fā)請(qǐng)求和轉(zhuǎn)發(fā)響應(yīng)。由于每個(gè)后端服務(wù)所需的request參數(shù)和產(chǎn)生的response不同,在這兩個(gè)階段,都需要對(duì)訪問(wèn)層傳來(lái)的request和后端服務(wù)傳回的response進(jìn)行定制:
需要將訪問(wèn)層的request適配成后端service所需的request,這就面臨著一些格式轉(zhuǎn)換,如參數(shù)改名、增減對(duì)應(yīng)的參數(shù)等。
需要對(duì)后端服務(wù)的response適配成訪問(wèn)層所需的response,采用類似的格式轉(zhuǎn)換的動(dòng)作。
針對(duì)這個(gè)需求,每個(gè)UpStreamURL都有兩個(gè)可選屬性可以利用。
before閉包upstreamURLs = [ [ host: "localhost", port: 8080, url: "/test", before: { params -> ... } ] ]
before閉包的參數(shù)為訪問(wèn)層所發(fā)來(lái)的request parameters,其類型是一個(gè)JsonObject。開(kāi)發(fā)者可以通過(guò)調(diào)用相關(guān)的方法來(lái)對(duì)其內(nèi)容進(jìn)行增減,dgate會(huì)將before閉包的返回值作為參數(shù)發(fā)給對(duì)應(yīng)的上游URL。故,在此處非常適合將多余的request params過(guò)濾掉。
請(qǐng)注意:before閉包的返回值必需是JsonObject。最簡(jiǎn)單的before閉包如下:
before: { params -> params }after閉包
upstreamURLs = [ [ host: "localhost", port: 8080, url: "/test", after: { simpleResponse -> ... } ] ]
after閉包的參數(shù)為后端服務(wù)所發(fā)來(lái)的response,其類型是SimpleResponse。開(kāi)發(fā)者可以利用相關(guān)方法來(lái)自定義其內(nèi)容,dgate會(huì)將after閉包的返回值作為最終結(jié)果與其他后端服務(wù)的響應(yīng)合并,然后返回給訪問(wèn)層。故,此處適合對(duì)響應(yīng)進(jìn)行自定義。
請(qǐng)注意:after閉包的返回值必需是SimpleResponse。最簡(jiǎn)單的after閉包如下:
after: { simpleResponse -> simpleResponse }給before/after閉包傳入工具類
由于Groovy閉包的特性,我們可以給before/after傳入工具類實(shí)例,供開(kāi)發(fā)者使用。典型的例子如下:
"/login" { required = ["sub", "password"] methods = [HttpMethod.GET, HttpMethod.POST] upstreamURLs = [ [ host: "localhost", port: 8080, url: "/login", after: { simpleResponse -> simpleResponse.put(tokenGenerator.token(["sub": "13572209183", "name": "foxgem", "role": "normal"], 2)) simpleResponse } ] ] }
其中的tokenGenerator就是利用這個(gè)特性完成的,具體實(shí)現(xiàn)可以參見(jiàn)LoginHandler。注意,上述代碼中的2表示token的超時(shí)時(shí)間,單位為秒。
url path parametersUpStreamURL除了支持一般的url格式,還支持url path parameters,格式如下:
"/:x"
"/y/:x?"
"/:x?/test/:y?"
所有的參數(shù)以":"開(kāi)始,以"?"結(jié)束的為可選參數(shù),規(guī)定如下:
當(dāng)url中有且僅有一個(gè)可選參數(shù)時(shí),它必需為最后一個(gè)參數(shù)
當(dāng)url中有多個(gè)可選參數(shù)時(shí),則只能從后向前不出現(xiàn)在url中,即:對(duì)于/:x?/:y?/:z?
/x/y/z,合法
/x/y,合法
/x,合法
/x/z,非法,它將解析對(duì)應(yīng)成:/x/y
Mock請(qǐng)求最簡(jiǎn)單的mock請(qǐng)求的書(shū)寫(xiě)如下:
"/mock" { expected { statusCode = 200 payload = [test: true] } }
若statusCode或payload本身是一個(gè)閉包,則dgate會(huì)將它們的返回值作為結(jié)果返回,這在excepted為一個(gè)動(dòng)態(tài)值時(shí)非常有用,參見(jiàn)下面【Mock JWT Token】的例子。
上面的形式對(duì)于任何Http Method不加區(qū)分地返回同一個(gè)內(nèi)容。但在Restful URL中,往往會(huì)有一個(gè)URL要為不同的Http Method服務(wù),執(zhí)行不同動(dòng)作(如CRUD),返回不同響應(yīng)體的情形。在dgate中可以以如下形式書(shū)寫(xiě):
"/mock" { expected { get { statusCode = 200 payload = [method: "get"] } post { statusCode = 200 payload = [method: "post"] } delete { statusCode = 200 payload = [method: "delete"] } } }
即對(duì)于每一種HTTP method,都有各自響應(yīng)體,每個(gè)響應(yīng)體的格式都是由statusCode和payload組成。
安全dgate支持JWT Token來(lái)認(rèn)證每個(gè)訪問(wèn)層請(qǐng)求,當(dāng)配置中出現(xiàn)login時(shí),安全機(jī)制即被觸發(fā)。
Login的配置若conf中沒(méi)有l(wèi)ogin配置,則所有url都是公共可訪問(wèn)的。
若要對(duì)所有url都要求先登錄,則配置如下:
login = "/login"
若要忽略某些url,則配置如下:
login { url = "/login" ignore = [ 被忽略的url ] }
若只有部分url是需要訪問(wèn)控制的,則配置如下:
login { url = "/login" only = [ 被控制的url ] }
注意:ignore和only不能同時(shí)存在。
JWT Tokendgate使用JWT Token來(lái)認(rèn)證每個(gè)訪問(wèn)層請(qǐng)求,故每個(gè)請(qǐng)求必需先請(qǐng)求獲得jwt token,然后將每個(gè)token放入后續(xù)每個(gè)request的"Authorization"頭,這樣每個(gè)后續(xù)的request才會(huì)被認(rèn)為是有效請(qǐng)求,否則將返回401。
如何產(chǎn)生jwt token的職責(zé)由開(kāi)發(fā)者來(lái)完成,它必需放入login配置部分,上面的例子給出了一個(gè)參考實(shí)現(xiàn)。
對(duì)于發(fā)往后端服務(wù)的每個(gè)請(qǐng)求,dgate會(huì)將前端請(qǐng)求Authorization頭中的jwt token附到請(qǐng)求的參數(shù)內(nèi),可以通過(guò)token來(lái)獲取,此時(shí)它已經(jīng)被解碼成一個(gè)JSON對(duì)象。
產(chǎn)生JWT Token的密鑰由下面的三個(gè)環(huán)境變量決定,故一旦配置中包含login,則需要在啟動(dòng)dgate之前先設(shè)置這3個(gè)環(huán)境變量:
dgate_key_store,keystore文件路徑
dgate_key_type,keystore文件類型
dgate_key_password,keystore的密鑰,至少6位
下面是一個(gè)配置的例子
export dgate_key_store=./test1.jceks export dgate_key_type=jceks export dgate_key_password=test123
使用keytool來(lái)產(chǎn)生文件,例子如下:
keytool -genseckey -keystore test1.jceks -storetype jceks -storepass test123 -keyalg HMacSHA256 -keysize 2048 -alias HS256 -keypass test123
由于dgate利用Vert.x的JWTAuthProvider產(chǎn)生JWT Token,因此其密鑰文件為符合其要求的文件格式。關(guān)于密鑰和如何產(chǎn)生密鑰文件的命令,可以參見(jiàn)其文檔。
注意:
在產(chǎn)品環(huán)境中請(qǐng)安全地保管好這個(gè)密鑰文件!
在開(kāi)發(fā)環(huán)境可以使用dgate自帶的測(cè)試密鑰文件:dgate,密碼為“dcloud”。請(qǐng)注意不要將其用于產(chǎn)品環(huán)境,否則為偽造證書(shū)提供了便利。
Mock JWT Token對(duì)于使用Mock功能來(lái)進(jìn)行開(kāi)發(fā)的訪問(wèn)層,有時(shí)會(huì)需要mock login來(lái)獲得mock的jwt token以便開(kāi)發(fā)相應(yīng)的功能。可以參考下面的代碼:
import io.vertx.core.http.HttpMethod import io.vertx.core.Vertx import io.vertx.ext.auth.jwt.JWTAuth import top.dteam.dgate.utils.* apiGateway { port = 7000 login = "/login" urls { "/login" { required = ["sub", "password"] methods = [HttpMethod.GET, HttpMethod.POST] expected { statusCode = 200 payload = { JWTAuth jwtAuth = Utils.createAuthProvider(Vertx.vertx()) JWTTokenGenerator tokenGenerator = new JWTTokenGenerator(jwtAuth) [token: tokenGenerator.token(["sub": "13572209183", "name": "foxgem", "role": "normal"], 200)] } } } …… }
既然login指向的是dgate暴露的url,那么當(dāng)然也就是可以直接mock啦。
注意:不要忘記payload本身是一個(gè)閉包!否則,無(wú)法模擬JWT Token過(guò)期后重新生成另一個(gè)Token的情況。
刷新JWT Tokendgate支持JWT Token的刷新,刷新用的url為:/token-refresh。它會(huì)從Request Header中取出JWT,解碼,然后重新生成新的JWT。即,刷新JWT Token的前提是必須先獲得dgate的JWT Token。
刷新JWT Token的配置由login配置塊決定。當(dāng)login是一個(gè)url時(shí),則使用缺省的刷新屬性。刷新屬性(單位:秒)包括:
refreshLimit,刷新時(shí)限,即對(duì)于一個(gè)超時(shí)的JWT Token,若距離當(dāng)前時(shí)間小于這個(gè)值,則允許刷新。否則返回401。
refreshExpire,新生成的JWT Token的時(shí)限,一旦超過(guò),則對(duì)應(yīng)的JWT Token失效。
若要自定義這兩個(gè)值,則可按照如下定義:
login { …… refreshLimit = 30 * 60 refreshExpire = 30 * 60 …… }
若不指定,則使用缺省值。這兩個(gè)值的缺省值都為:30分鐘,即:30 * 60(秒)。
CORS支持對(duì)于某些情況下,需要打開(kāi)CORS支持才能讓訪問(wèn)層正常使用。在dgate中需要在apiGateway中添加如下的配置:
apiGateway { …… cors { allowedOriginPattern = "*" allowedMethods = [HttpMethod.GET, HttpMethod.POST, HttpMethod.DELETE] allowedHeaders = ["content-type"] allowCredentials = true } …… }
cors配置并非必需的,若沒(méi)有,dgate不會(huì)支持CORS。CORS的配置項(xiàng)目如下:
allowedOriginPattern,字符串,如 “http://localhost:7000”。
allowedHeaders,字符串集合,支持的HTTP請(qǐng)求HEADER集合
allowedMethods,Vertx的HttpMethod集合,支持的HTTP方法集合
exposedHeaders,字符串集合,允許暴露的響應(yīng)頭集合
maxAgeSeconds,整型,可用于緩存preflight的時(shí)間,單位(秒)
allowCredentials,是否允許Cookie
其中僅allowedOriginPattern是必填。但,對(duì)于提交格式為JSON的ajax請(qǐng)求,需要允許:"content-type"。
此外,在調(diào)試過(guò)程中,也可以注意瀏覽器給出的提示,根據(jù)相應(yīng)提示進(jìn)行配置。
警告:在產(chǎn)品環(huán)境中不要使用“*”作為allowedOriginPattern的值。
每個(gè)請(qǐng)求的附加參數(shù)dgate會(huì)給每個(gè)發(fā)往后端服務(wù)的請(qǐng)求參數(shù)中添加若干參數(shù),通過(guò)下面的key可以獲得:
nameOfApiGateway,Api Gateway的名字,字符串。
token,已解碼的jwt token,其類型是一個(gè)Map,內(nèi)容依賴于在產(chǎn)生token時(shí)設(shè)置的值。如:在產(chǎn)生時(shí)包含[sub, name, role]這幾個(gè)鍵值,則此處就獲得這3個(gè)鍵值。若在產(chǎn)生時(shí)為[sub, name, role, other],則此處就可以會(huì)有這4個(gè)鍵值。
注意:除了必需的幾個(gè)屬性,JWT Token中token本身是可以附加其他屬性進(jìn)來(lái)的。相當(dāng)于將token本身作為信息的載體。
斷路器設(shè)置dgate缺省會(huì)為每個(gè)上游服務(wù)(注:對(duì)于Mock服務(wù),斷路器設(shè)置無(wú)效)設(shè)置一個(gè)斷路器,缺省的配置如下:
最大失敗次數(shù)(maxFailures),3次
請(qǐng)求超時(shí)(timeout),5秒
斷路器重置時(shí)間(resetTimeout),10秒
若缺省的設(shè)置不合適,dgate支持兩個(gè)層次的設(shè)置:gateway級(jí)別和上游服務(wù)級(jí)別。并且,在兩者都存在時(shí),上游服務(wù)的斷路器設(shè)置會(huì)覆蓋gateway級(jí)別的設(shè)置。
gateway級(jí)別的設(shè)置這個(gè)設(shè)置將對(duì)gateway內(nèi)所有的上游服務(wù)生效,例子如下:
apiGateway { …… circuitBreaker { maxFailures = 5 timeout = 10000 resetTimeout = 30000 } …… }上游服務(wù)級(jí)別的設(shè)置
這個(gè)設(shè)置僅對(duì)當(dāng)前上游服務(wù)生效,例子如下:
…… "/url" { upstreamURLs = [ [host: "localhost", port: 8080, url: "/test1", circuitBreaker: [maxFailures: 2, timeout: 3000, resetTimeout: 3000]] ] } ……
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/66564.html
摘要:最近,因?yàn)轫?xiàng)目的需要,順手給它增加了一個(gè)新的特性。其中,對(duì)應(yīng)后端主動(dòng)發(fā)起的推送,對(duì)于每一個(gè)推送地址,必填,單位為毫秒。,對(duì)應(yīng)后端接收前端消息的消費(fèi)者。當(dāng)為閉包時(shí),其返回值為結(jié)果。相關(guān)鏈接基于的輕量級(jí)上的的使用指南 由于簡(jiǎn)單同時(shí)又強(qiáng)大的Mock特性,dgate在我的項(xiàng)目中除了作為簡(jiǎn)單的API網(wǎng)關(guān),它也承擔(dān)著面向前端的Mock Server作用,保證前后端開(kāi)發(fā)同步進(jìn)行。最近,因?yàn)轫?xiàng)目的需要...
摘要:輕量級(jí),部署簡(jiǎn)單。此外,本文也不是入門文檔,而是為了預(yù)防陷坑而給出的指導(dǎo)意見(jiàn),故在閱讀本文之前還請(qǐng)先仔細(xì)閱讀的文檔。可視作的一個(gè)最小部署和運(yùn)行單元,簡(jiǎn)單的說(shuō),可類比為。,主,負(fù)責(zé)部署程序中其他的。嚴(yán)格來(lái)講,之后,上述第一點(diǎn)并不完全正確。 一直以來(lái)早有將這些年用Vert.x的經(jīng)驗(yàn)整理一下的想法,奈何天生不是勤快人,直到最近扶墻老師問(wèn)起,遂成此文。 選擇理由 現(xiàn)在想想,我們應(yīng)該算是國(guó)內(nèi)用V...
摘要:對(duì)于集成測(cè)試,直接模擬實(shí)際的環(huán)境,再加上合適的,目前看來(lái)也還不錯(cuò)。這里給出兩個(gè)例子集成測(cè)試單元測(cè)試都是基于寫(xiě)的,各位可以體驗(yàn)其酸爽度。好啦,本期內(nèi)容就此結(jié)束,請(qǐng)保持關(guān)注,期待下期繼續(xù)本系列其他文章入坑須知入坑須知 隨著Vert.x進(jìn)化到3.5.0,本系列也迎來(lái)了新篇章。 CORS的新變化 對(duì)于CORS,搞Web開(kāi)發(fā)(不論你是前端,還是后端)的同志應(yīng)該不陌生,尤其是如今微服務(wù)盛行的時(shí)代,...
摘要:這一點(diǎn)其實(shí)是非常不妥的,有潛在的安全問(wèn)題。這次,在項(xiàng)目中終于采用了以它為基礎(chǔ)的集群方案。相反,使用一個(gè)周期,但針對(duì)每個(gè)生成一個(gè)一次性的,模擬隨機(jī)發(fā)送。同時(shí),要記得用完之后立即釋放。 當(dāng)初創(chuàng)建簡(jiǎn)書(shū)賬號(hào)的時(shí)候曾立下宏愿,希望保持周更,無(wú)奈現(xiàn)實(shí)殘酷,整個(gè)5月都處于忙忙碌碌的狀態(tài),居然令這個(gè)本來(lái)并不算太宏偉的目標(biāo)難以為繼,最終導(dǎo)致5月份交了白卷!【好吧,我承認(rèn),是我意志不夠堅(jiān)定,太懶了,;)】...
摘要:本教程是藍(lán)圖系列的第三篇教程,對(duì)應(yīng)的版本為。提供了一個(gè)服務(wù)發(fā)現(xiàn)模塊用于發(fā)布和獲取服務(wù)記錄。前端此微服務(wù)的前端部分,目前已整合至組件中。監(jiān)視儀表板用于監(jiān)視微服務(wù)系統(tǒng)的狀態(tài)以及日志統(tǒng)計(jì)數(shù)據(jù)的查看。而服務(wù)則負(fù)責(zé)發(fā)布其它服務(wù)如服務(wù)或消息源并且部署。 本文章是 Vert.x 藍(lán)圖系列 的第三篇教程。全系列: Vert.x Blueprint 系列教程(一) | 待辦事項(xiàng)服務(wù)開(kāi)發(fā)教程 Vert....
閱讀 2128·2021-09-27 14:04
閱讀 1873·2019-08-30 15:55
閱讀 1698·2019-08-30 13:13
閱讀 1065·2019-08-30 13:07
閱讀 2742·2019-08-29 15:20
閱讀 3240·2019-08-29 12:42
閱讀 3324·2019-08-28 17:58
閱讀 3593·2019-08-28 17:56