â说å¨åé¢ï¼
ç°å¨æ¿å°offerè¶ çº§é¾ï¼çè³è¿é¢è¯çµè¯ï¼ä¸ä¸ªé½æä¸å°ã
å°¼æ©çææ¯ç¤¾åºä¸ï¼50+ï¼ï¼å¾å¤å°ä¼ä¼´åå âå·¦æäºåç+å³æ大æ°æ®âçç»æ´»ï¼æ¿å°äºofferï¼å¹¶ä¸æ¯é常ä¼è´¨çofferï¼æ®è¯´å¹´ç»å¥é½è¶³è¶³18个æã
第äºä¸ªæ¡ä¾å°±æ¯ï¼å段æ¶é´ï¼ä¸ä¸ª2å¹´å°ä¼ä¼´å¸æ涨èªå°18Kï¼ å°¼æ©æGOè¯è¨ç项ç®æ¶æï¼ç»ä»åå ¥äºç®åï¼å¯¼è´ä»çç®åéå éªéªï¼è±èæ¢éª¨ï¼å®å ¨å¯ä»¥å»æ¿å¤´æ¡ãè ¾è®¯ç30Kçofferï¼ å¹´èªå¯ä»¥ç´æ¥å¤ 20Wã
第äºä¸ªæ¡ä¾å°±æ¯ï¼ä¸ä¸ª6å¹´å°ä¼ä¼´ååJava+goåè¯è¨äºåçæ¶æï¼å¹´èª60Wã
ä»Javaé«èªå²ä½åå°±ä¸å²ä½æ¥çï¼äºåçãK8SãGO ç°å¨å¯¹äº é«çº§å·¥ç¨å¸/æ¶æå¸æ¥è¯´ï¼è¶æ¥è¶éè¦ã
æ以ï¼å°¼æ©ä»æ¶æå¸è§è§åºåï¼åºäºå°¼æ© 3é«æ¶æç¥è¯å®å®ï¼åä¸æ¬ãGOå¦ä¹ å£ç»ã
ãGOå¦ä¹ å£ç»ãå·²ç»å®æçå 容æï¼
Goå¦ä¹ å£ç»ï¼0åºç¡ç²¾éGOå¼åä¸é«å¹¶åæ¶æ
Goå¦ä¹ å£ç»ï¼éååå³°+æ¹éåå ¥ è¶ é«å¹¶ååçåå®æ
ãGOå¦ä¹ å£ç»ãPDFçæç»ç®æ
å±ä»¬çç®æ ï¼ä¸ä» ä» å¨äº GO åºç¨ç¼ç¨èªç±ï¼æ´å¨äº GO æ¶æèªç±ã
å¦å¤ï¼åé¢å°¼æ©çäºåçæ¯æ²¡ææ¶åGOçï¼ä½æ¯ï¼æ²¡æGOçäºåçæ¯ä¸å®æ´çã
æä»¥ï¼ GOè¯è¨ãGOæ¶æå¦ä¹ å®äºä¹åï¼å±ä»¬åå»æ个å马æªï¼å®æäºåçç第äºé¨å: ãIstio + K8S CRDçæ¶æä¸å¼åå®æã , 帮å©å¤§å®¶å½»åºç©¿éäºåçã
âæ¬æç®å½ï¼
- 说å¨åé¢
- åºäº Gin æ¡æ¶ç Restå¾®æå¡æ¶æä¸éå
- å顾ï¼java+go å¤è¯è¨ äºåçå¾®æå¡æ¶æ
- gin æ¡æ¶ ç®ä»
- Gin æ¡æ¶çå®è£
å使ç¨
- ä½¿ç¨ Gin æ¡æ¶ç¼å Hello World
- main.goçæ§è¡è¿ç¨
- Gin Restå¾®æå¡ç模å设计
- Gin Restå¾®æå¡ç®å½ç»æ
- Ginæ¡æ¶WEBå¼åå
¥é¨
- åºæ¬å®è£
- Rest请æ±è·¯ç±çé
ç½®å使ç¨
- Rest请æ±è·¯å¾ç设置
- router.GETæ¹æ³çæºç åæ
- Giné
置路ç±çä¸ä¸ªä¸»è¦æ¹æ³
- è·¯ç±åç»
- 大è§æ¨¡è·¯ç±çå¤æ件é
ç½®
- è·ååæ°
- è·åè·¯å¾åæ°
- è·åURLåæ°
- è·åPostæ¹æ³
- URLåæ°åPoståæ°çè·åæ¡ä¾
- æ件ä¸ä¼
- 表åéªè¯
- è®¾ç½®æ ¡éªçè§å
- content-typeç»å®
- Gin æ°æ®è¿åç±»å
- Gin éå®å
- å¼æ¥æ§è¡
- ä¼è¯æ§å¶
- cookieç¸å
³
- sessionç¸å
³
- tokenç¸å
³
- ginä¸é´ä»¶
- é»è®¤Ginä¸é´ä»¶
- èªå®ä¹Ginä¸é´ä»¶
- ä¸é´ä»¶æ§å¶çæ¹æ³
- å±é¨ä¸é´ä»¶
- Ginçè·¯ç±æ¡æ¶ï¼httprouteræ¡æ¶
- åç¼æ ååºæ°æ ï¼radix treeï¼çåºå«ï¼
- ginæ¡æ¶è·¯ç±åç
- gin注åè·¯ç±è¿ç¨
- å¾®æå¡ç»ä¸é
置管ç
- 为ä»ä¹éè¦åå¸å¼é
ç½®ä¸å¿
- åå¸å¼é
ç½®ä¸å¿ææ¯éå
- Spring Cloud Nacos
- Spring Cloud Eureka
- golangçåèé
ç½®ç»ä»¶viper
- éè¿ viperå®ç°ç»ä¸é
置管çåè½
- 1ãviperçä»ç»
- 2ãviperçå®è£
å使ç¨
- 2.1 Viper对象çå建
- 2.2 é¢è®¾ä¸äºé»è®¤é
ç½®
- 2.3 key大å°åé®é¢
- 2.4 ä»é
ç½®æ件读å
- 2.5 æ¯æåªäºæä»¶æ ¼å¼
- 3ãåé
ç½®æ件
- 4ãä»ç¯å¢åé读å
- 5ãä»å½ä»¤è¡å·¥å
·çé项åæ°Flags读å
- viper + pflag ç»å使ç¨
- Go è¯è¨æ ååºä¸ç flag å
- flagå
ç»å®è§£æåæ°æä¸ç§å®ä¹æ¹å¼
- viper + flag ç»å使ç¨
- 6ãä»è¿ç¨key/valueåå¨è¯»å
- 7ãçå¬é
ç½®åå
- 7.1 çå¬æ¬å°é
ç½®æ件åå
- 7.2 çå¬è¿ç¨é
ç½®åå
- éè¿ Nacoså®ç°ç»ä¸é
置管çåè½
- SpringCloud åå¸å¼é
ç½®ä¸å¿éå
- ç¨java+go å¤è¯è¨ äºåçå¾®æå¡æ¶æä¸é
ç½®ä¸å¿ç ä½ç½®
- Nacosä½ä¸ºé
ç½®ä¸å¿ä½¿ç¨
- 为ä»ä¹ä½¿ç¨Nacos
- Nacosæ¯ä»ä¹ï¼
- Nacosçç»æåç»ä»¶
- Nacosçå®è£
å使ç¨
- é
ç½®æ°æ®çä¸ä¸ªæ ¸å¿æ¦å¿µï¼NamespaceãGroupãData ID
- å½å空é´(Namespace) ï¼ ç¯å¢é离
- é
ç½®åç»ï¼Groupï¼ ï¼ é¡¹ç®é离
- é
ç½®é( Data IDï¼ ï¼ åè½é离
- é
ç½®éå
å«ä¸ä¸ªä¸ä¸ªçé
置项
- go nacos sdk ç使ç¨
- nacos-sdk-go çå®æ¹demo
- 示ä¾ï¼go nacoså®ç°é
置读åä¸çå¬é
ç½®åå
- nacos goå¨æé
ç½®
- ãGolang å£ç»ãè¿æ 5Wåå¾
åå¸
- åèèµæ
- ææ¯èªç±çå®ç°è·¯å¾ PDF è·å
âåºäº Gin æ¡æ¶ç Restå¾®æå¡æ¶æä¸éåï¼
âå顾ï¼java+go å¤è¯è¨ äºåçå¾®æå¡æ¶æ
ä¸ä¸ªæå°¼æ©æ导 ä¸ä¸ª6å¹´å°ä¼ä¼´ç®åï¼ä½¿ç¨java+go å¤è¯è¨ äºåçå¾®æå¡æ¶æï¼
è¿ä¸ªæ¶æï¼é常çæ°ï¼ å¸®å© 6å¹´å°ä¼ï¼æ¶äºä¸ä¸ª60Wå¹´èªä¼è´¨offer
主è¦çæ¶æå¾å¾å¦ä¸ï¼
GO BFF å¨ææ¯æ¶æä¸ï¼ ä½¿ç¨ RestFull Apiæå¡å± + Dubbo RPC æ¶è´¹å±çåå±æ¶æã
RestFull Apiæå¡å± ï¼å¯¹å端æä¾ Rest æå¡
Dubbo RPC æ¶è´¹å± , 对å端å®ç° Dubbo é«æ§è½è¿ç¨è°ç¨ã
å¨è¿ä¸ªæ¶æä¸ï¼ ç¨java+go å¤è¯è¨ äºåçå¾®æå¡æ¶æä¸çåå¸å¼ é ç½®ä¸å¿ ï¼æ¯nacos
ègo ä¸çviperæ¡æ¶ï¼æ²¡æ对 nacos æä¾æ¯æ æ以ï¼å±ä»¬èªå·±å®ç°ï¼å¯¹ nacos çæ¯æ
âGin æ¡æ¶ ç®ä»ï¼
Gin æ¯ä¸ä¸ªåºäº Go è¯è¨ç Web æ¡æ¶ï¼å®å ·æé«æ§è½ãæå¦æç¨ãè½»é级çç¹ç¹ï¼è¢«å¹¿æ³åºç¨äºæ建 RESTful API åå¾®æå¡çåºæ¯ã
Gin æ¡æ¶æä¾äºä¸°å¯çä¸é´ä»¶æ¯æï¼å¯ä»¥æ¹ä¾¿å°å®ç°è¯·æ±è·¯ç±ãåæ°è§£æãæ¥å¿è®°å½ãé误å¤ççåè½ã
Gin æ¡æ¶ç设计çµææ¥èªäº Martini æ¡æ¶ï¼ä½ç¸æ¯ä¹ä¸ï¼Gin æ¡æ¶æ´å¿«ãæ´ç¨³å®ãæ´æç¨ã
Gin å ·æç±»ä¼¼äº Martini ç API é£æ ¼ï¼å¹¶ä¸å®ä½¿ç¨äºèåçå¼æºé¡¹ç® httprouter çèªå®ä¹çæ¬ä½ä¸ºè·¯ç±åºç¡ï¼ä½¿å¾å®çæ§è½è¡¨ç°æ´é«æ´å¥½ï¼ç¸è¾ Martini 大约æé«äº 40 åã
å¦å¤ gin é¤äºå¿«ä»¥å¤ï¼è¿å ·å¤å°å·§ãç²¾ç¾ä¸æç¨çç¹æ§ï¼ç®å广å Go è¯è¨å¼åè çåç±ï¼æ¯ææµè¡ç HTTP Web æ¡æ¶
ä» Github Star ä¸æ¥ç, ginæ¡æ¶çè¶å¿å¦ä¸ï¼
âGin æ¡æ¶çå®è£ å使ç¨ï¼
è¦å®è£ Gin æ¡æ¶ï¼ä½ éè¦å å®è£ Go è¯è¨ç¯å¢ï¼å¹¶è®¾ç½®å¥½ GOPATH å PATH ç¯å¢åéãç¶åå¯ä»¥ä½¿ç¨ä»¥ä¸å½ä»¤å®è£ Ginï¼
go get -u github.com/gin-gonic/gin
è¿å°ä» GitHub ä¸è½½ Gin æ¡æ¶çææ°çæ¬å¹¶å®è£ å°ä½ ç GOPATH ç®å½ä¸ã
å¨å®è£ å®æ¯åï¼æ们å¯ä»¥çå°é¡¹ç®æ ¹ç®å½ä¸ç go.mod æ件ä¹ä¼åçç¸åºçæ¹å.
æå¼ go.mod æ件ï¼æ¥çå¦ä¸ï¼
module github.com/go-programming-tour-book/blog-service
go 1.14
require (
github.com/gin-gonic/gin v1.9.0 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
...
)
è¿äºæ£æ£å°±æ¯ gin æç¸å ³èçææ模åå .
大家å¯è½ä¼å¥½å¥ï¼ä¸ºä»ä¹ github.com/gin-gonic/gin åé¢ä¼åºç° indirect æ è¯ï¼
å¨æ§è¡å½ä»¤go get æ¶ï¼Go module ä¼èªå¨æ´çgo.mod æ件ï¼å¦ææå¿ è¦ä¼å¨é¨åä¾èµå çåé¢å¢å // indirect注éã
ä¸è¬èè¨ï¼è¢«æ·»å // indirect 注éçå ,è¯å®æ¯é´æ¥ä¾èµçå ï¼
è没ææ·»å // indirect注éçå åæ¯ç´æ¥ä¾èµçå ï¼ä»ä¹å«åç´æ¥ä¾èµå¢ ï¼ å³æç¡®çåºç°å¨æ个importè¯å¥ä¸ã
ç¶èï¼éè¦çé强è°çæ¯ï¼å¹¶ä¸æ¯ææçé´æ¥ä¾èµé½ä¼åºç°å¨ go.modæ件ä¸ãé´æ¥ä¾èµåºç°å¨go.modæ件çæ åµï¼å¯è½ç¬¦åä¸é¢æååºæ¯çä¸ç§æå¤ç§ï¼
- ç´æ¥ä¾èµæªå¯ç¨ Go module
- ç´æ¥ä¾èµ go.mod æ件ä¸ç¼ºå¤±é¨åä¾èµ
åå°ä¸é¢ç go.mod æ件ï¼æ¥çå¦ä¸ï¼
module github.com/go-programming-tour-book/blog-service
go 1.14
require (
github.com/gin-gonic/gin v1.9.0 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
...
)
å®æææ¯æ们ç´æ¥éè¿è°ç¨ go get å¼ç¨çï¼ ä¸ºå¥å äº // indirect注é ï¼
æ¯å 为å¨æ们å®è£ æ¶ï¼è¿ä¸ªé¡¹ç®æ¨¡åè¿æ²¡æçæ£çå»ä½¿ç¨å®æ导è´çï¼è¿æ²¡åå¨æ个importè¯å¥ä¸ åºç° ï¼ã
å¦å¤ä½ ä¼æ³¨æå°ï¼å¨ go.mod æ件ä¸æ类似 go 1.14 è¿æ ·çæ è¯ä½ï¼ä¸»è¦ä¸ä½ å建 Go modules æ¶ç Go çæ¬æå ³ã
âä½¿ç¨ Gin æ¡æ¶ç¼å Hello World
å¨å®æåç½®å¨ä½åï¼å¨æ¬èæ们å å°ä¸ä¸ª Demo è¿è¡èµ·æ¥ï¼ççä¸ä¸ªæç®åç HTTP æå¡è¿è¡èµ·æ¥æ¯æä¹æ ·çï¼
ä½¿ç¨ Gin æ¡æ¶ç¼å Web åºç¨ç¨åºé常ç®åï¼ä»¥ä¸æ¯ä¸ä¸ªç®åç示ä¾ï¼
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello, World!")
})
router.Run(":8080")
}
è¿ä¸ªåºç¨ç¨åºå建äºä¸ä¸ªç®åç HTTP æå¡å¨ï¼çå¬æ¬å°ç 8080 端å£ï¼å¹¶å¨è®¿é®æ ¹è·¯å¾æ¶è¿å "Hello, World!"ã
ä½ å¯ä»¥ä½¿ç¨ go run å½ä»¤è¿è¡è¿ä¸ªåºç¨ç¨åºï¼
go run main.go
ä¹å¯ä»¥å¨ goland å·¥å ·ä¸ç´æ¥å¯å¨ ï¼å ·ä½å¦ä¸ï¼
ç¶åå¨æµè§å¨ä¸è®¿é® http://localhost:8080 å°±å¯ä»¥çå° "Hello, World!" çååºäºã
å½ç¶ï¼è¿åªæ¯ Gin æ¡æ¶çä¸ä¸ªç®å示ä¾ï¼ä½ å¯ä»¥æ ¹æ®èªå·±çéæ±ç¼åæ´å¤æç Web åºç¨ç¨åºã
âmain.goçæ§è¡è¿ç¨
æ¥ä¸æ¥æ们è¿è¡ main.go æ件ï¼æ¥çè¿è¡ç»æï¼å¦ä¸ï¼
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET / --> main.main.func1 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8080
æ们å¯ä»¥çå°å¯å¨äºæå¡åï¼è¾åºäºè®¸å¤è¿è¡ä¿¡æ¯ï¼
å¨è¿éæ们对è¿è¡ä¿¡æ¯åä¸ä¸ªåæ¥çæ¦æ¬åæï¼å为以ä¸å大åï¼
é¦å æ¯ginçè¿è¡æ¨¡å¼ï¼å½å为Release 模å¼ã
- using code: gin.SetMode(gin.ReleaseMode)
è¿æ¯ Go è¯è¨ä¸ä½¿ç¨ Gin æ¡æ¶æ¶ï¼è®¾ç½®è¿è¡æ¨¡å¼ä¸º Release 模å¼ç代ç ã
å¨ Release 模å¼ä¸ï¼Gin æ¡æ¶ä¼å ³éè°è¯ä¿¡æ¯åå æ è·è¸ªï¼ä»¥æé«æ§è½åå®å ¨æ§ã
è¿ä¸ªè®¾ç½®é常å¨åºç¨ç¨åºç main å½æ°æåå§å代ç ä¸è¿è¡ã
并建议è¥å¨æµè¯ç¯å¢æ¶åæ¢ä¸ºdebug模å¼ï¼gin.SetMode(gin.DebugMode) åæ¢ä¸ºdebug模å¼ï¼
func main() {
router := gin.Default()
gin.SetMode(gin.DebugMode)
router.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello, World!")
})
router.Run(":8080")
}
æ¥ä¸æ¥ï¼æ¯è¯·æ±çè·¯ç±æ³¨åï¼æ³¨åäº GET /ping çè·¯ç±ï¼å¹¶è¾åºå ¶è°ç¨æ¹æ³çæ¹æ³åã
[GIN-debug] GET / --> main.main.func1 (3 handlers)
æ¥ä¸æ¥ï¼æ¯çå¬ç«¯å£ä¿¡æ¯ï¼æ¬æ¬¡å¯å¨æ¶çå¬ 8080 端å£ï¼ç±äºæ²¡æ设置端å£å·çä¿¡æ¯ï¼å æ¤é»è®¤ä¸º 8080ã
[GIN-debug] Listening and serving HTTP on :8080
âGin Restå¾®æå¡ç模å设计ï¼
å¨å®æäºåæ¥ç示ä¾æ¼ç¤ºåï¼æ¥ä¸æ¥å°±æ¯è¿å ¥å ·ä½çé¢å¤å¼åé¶æ®µï¼ä¸è¬å¨æ£å¼è¿å ¥ä¸å¡å¼ååï¼æ们ä¼é对æ¬æ¬¡éæ±çè¿ä»£å 容è¿è¡å¤ç±»ç设计åè¯å®¡ï¼æ 设计ä¸å¼åã
ä½æ¯é®é¢å¨äºï¼æ们ç®åè¿ç¼ºå¾å¤åå§åçä¸è¥¿æ²¡æåï¼å æ¤å¨æ¬ç« èä¸ï¼æ们主è¦é对项ç®ç®å½ç»æãæ¥å£æ¹æ¡ãè·¯ç±æ³¨åãæ°æ®åºç设计è¿è¡æèå设计å¼åã
âGin Restå¾®æå¡ç®å½ç»æ
æ们å å°é¡¹ç®çæ åç®å½ç»æå建起æ¥ï¼ä¾¿äºåç»çå¼åï¼æç»ç®å½ç»æå¦ä¸ï¼
gin-rest
âââ configs
âââ docs
âââ global
âââ internal
â âââ dao
â âââ middleware
â âââ model
â âââ routers
â âââ service
âââ pkg
âââ storage
âââ scripts
âââ third_party
- configsï¼é ç½®æ件ã
- docsï¼ææ¡£éåã
- globalï¼å ¨å±åéã
- internalï¼å é¨æ¨¡åã daoï¼æ°æ®è®¿é®å±ï¼Database Access Objectï¼ï¼ææä¸æ°æ®ç¸å ³çæä½é½ä¼å¨ dao å±è¿è¡ï¼ä¾å¦ MySQLãElasticSearch çãmiddlewareï¼HTTP ä¸é´ä»¶ãmodelï¼æ¨¡åå±ï¼ç¨äºåæ¾ model 对象ãroutersï¼è·¯ç±ç¸å ³é»è¾å¤çãserviceï¼é¡¹ç®æ ¸å¿ä¸å¡é»è¾ã
- pkgï¼é¡¹ç®ç¸å ³ç模åå ã
- storageï¼é¡¹ç®çæç临æ¶æ件ã
- scriptsï¼åç±»æ建ï¼å®è£ ï¼åæçæä½çèæ¬ã
- third_partyï¼ç¬¬ä¸æ¹çèµæºå·¥å ·ã
âGinæ¡æ¶WEBå¼åå ¥é¨ï¼
âåºæ¬å®è£
1.é¦å éè¦å®è£ Goï¼éè¦1.10+çæ¬ï¼ï¼ç¶åå¯ä»¥ä½¿ç¨ä¸é¢çGoå½ä»¤å®è£ Ginã
go get -u github.com/gin-gonic/gin
2.å°å ¶å¯¼å ¥æ¨ç代ç ä¸ï¼
import "github.com/gin-gonic/gin"
âRest请æ±è·¯ç±çé ç½®å使ç¨
è·¯ç±çæ¬è´¨æ¯åç¼æ ï¼å©ç¨åç¼æ æ¥å®ç°è·¯ç±çåè½ã
âRest请æ±è·¯å¾ç设置
Ginæ¡æ¶çRestè·¯ç±ä½¿ç¨é常ç®åï¼å¯ä»¥éè¿å®ä¹è·¯ç±ä»¥åå¤ç该路ç±å¯¹åºçHandleræ¥æ¥æ¶ç¨æ·çWeb请æ±ã
以ä¸æ¯ä¸ä¸ªä½¿ç¨Ginæ¡æ¶çè·¯ç±ç¤ºä¾ï¼
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello, World!")
})
router.Run(":8080")
}
gin.Default() 表示ï¼å®ä¾åä¸ä¸ªGIN对象
router.Run(":8080") 表示ï¼å¯å¨ä¸ä¸ªHTTPæå¡è¿ç¨ï¼é»è®¤çå¬å¨8080端å£
router.GET(...) æ¯è·¯ç±è®¾ç½®ã
routerè·¯ç±çè·¯å¾è®¾ç½®ï¼éµå¾ªRestfulé£æ ¼ï¼éç¨URLå®ä½ï¼HTTPæè¿°æä½ï¼, ä¸é¢æ¯è·¯ç±é ç½®çå 个ä¾åï¼
//å®ä¹handleræ¹æ³, 类似äºjavaä¸ç Controller
func getting(c *gin.Context) {
c.JSON(http.StatusOK,gin.H{
"message":"getting",
})
}
//å®ä¹handleræ¹æ³, 类似äºjavaä¸ç Controller
func posting(c *gin.Context) {
c.JSON(http.StatusOK,gin.H{
"message":"posting",
})
}
// 设置路ç±
router := gin.Default()
// 第ä¸ä¸ªåæ°æ¯ï¼è·¯å¾ï¼ 第äºä¸ªåæ°æ¯ï¼å
·ä½æä½ func(c *gin.Context)
router.GET("/Get", getting)
router.POST("/Post", posting)
router.PUT("/Put", putting)
router.DELETE("/Delete", deleting)
// é»è®¤å¯å¨çæ¯ 8080端å£
router.Run()
ârouter.GETæ¹æ³çæºç åæ
routerçGETæ¹æ³ï¼åæ°è¡¨ä¸åå«æ¯è·¯å¾åå¤ä¸ªhandler
// GET is a shortcut for router.Handle("GET", path, handle).
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
return group.handle(http.MethodGet, relativePath, handlers)
}
å¤çå¨ handlerFuncçå ·ä½ç±»å:
// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)
æ以ï¼å®ä¹å¤çå¨çæ¶åï¼éè¦æç §ä¸é¢è¿ç§æ ¼å¼å®ä¹å½æ°:
//å®ä¹handleræ¹æ³, 类似äºjavaä¸ç Controller
func getting(c *gin.Context) {
c.JSON(http.StatusOK,gin.H{
"message":"getting",
})
}
JSONä¸çkeyæ¯HTTPçç¶æç ï¼JSONä¸çvalueæ¯ä¸ä¸ªmapã
为å¥å¢ï¼ å 为 gin.H å°±æ¯ä¸ä¸ªmap:
// H is a shortcut for map[string]interface{}
type H map[string]any
æ以ï¼æç §ä¸é¢çæ¹å¼å®ä¹å¤çå¨ï¼ä¹æ¯å¯ä»¥ç:
func pong(c *gin.Context) {
var m = map[string]string{
"message": "pong",
}
c.JSON(http.StatusOK, m)
}
âGiné 置路ç±çä¸ä¸ªä¸»è¦æ¹æ³
Giné 置路ç±æ¯ä¸ºäºå¤çHTTP请æ±ã
Ginæ¡æ¶ä¸é½æåHTTP请æ±ç¸äºå¯¹åºçæ¹æ³æ¥å®ä¹è·¯ç±ãèHTTP请æ±å å«ä¸åæ¹æ³ï¼å æ¬GET,POST,PUT,PATCH,OPTIONS,HEAD,DELETEçä¸ç§æ¹æ³ã
ä¸é¢æ¯Ginä¸ç³»åç è·¯ç±å®ä¹æ¡ä¾
router := gin.New()
router.GET("/testGet",func(c *gin.Context){
//å¤çé»è¾
})
router.POST("/testPost",func(c *gin.Context){
//å¤çé»è¾
})
router.PUT("/testPut",func(c *gin.Context){
//å¤çé»è¾
})
router.DELETE("/testDelete",func(c *gin.Context){
//å¤çé»è¾
})
router.PATCH("/testPatch",func(c *gin.Context){
//å¤çé»è¾
})
router.OPTIONS("/testOptions",func(c *gin.Context){
//å¤çé»è¾
})
router.OPTIONS("/testHead",func(c *gin.Context){
//å¤çé»è¾
})
âè·¯ç±åç»
Groupæ¯ä¸ä¸ªè·¯ç±åç»å¨ï¼å¯ä»¥å°ä¸ç»è·¯ç±è§åç»ç»å¨ä¸èµ·ï¼æ¹ä¾¿ç®¡çåç»´æ¤ã
Ginæ¡æ¶çè·¯ç±åç»å¯ä»¥éè¿Groupå½æ°å®ç°ã
ä¸é¢æ¯æ²¡æ使ç¨Groupæ¹æ³è¿è¡è·¯ç±é ç½®çä¾åï¼
func main() {
router := gin.Default()
router.GET("/goods/list",goodsList)
router.POST("/goods/add",createGoods)
_ = router.Run()
}
使ç¨è·¯ç±åç»æ¹åï¼ä¸ä¸é¢ç代ç æ¯åæ ·çææ:
func main() {
router := gin.Default()
goodsGroup := router.Group("/goods")
goodsGroup.GET("/list", goodsList)
goodsGroup.GET("/add", createGoods)
_ = router.Run()
}
å¾å¤çä¸å¡ä»£ç ï¼ä¼å®ä¹urlçä¸åçæ¬ï¼å¨è¿ç§åºæ¯ä¸ï¼å°±å¯ä»¥ä½¿ç¨çæ¬å·æ¥åç»ï¼
// 两个路ç±ç»ï¼é½å¯ä»¥è®¿é®ï¼å¤§æ¬å·æ¯ä¸ºäºä¿è¯è§è
v1 := r.Group("/v1")
{
// éè¿ localhost:8080/v1/hello访é®ï¼ä»¥æ¤ç±»æ¨
v1.GET("/hello", sayHello)
v1.GET("/world", sayWorld)
}
v2 := r.Group("/v2")
{
v2.GET("/hello", sayHello)
v2.GET("/world", sayWorld)
}
r.Run(":8080")
访é®çæ¶åï¼å¯ä»¥éè¿ä¸é¢çæ¹å¼ï¼è®¿é®v1çæ¬çå°åï¼
localhost:8080/v1/hello
localhost:8080/v1/world
访é®çæ¶åï¼å¯ä»¥éè¿ä¸é¢çæ¹å¼ï¼è®¿é®v2çæ¬çå°åï¼
localhost:8080/v2/hello
localhost:8080/v2/world
â大è§æ¨¡è·¯ç±çå¤æ件é ç½®ï¼
å½æ们çè·¯ç±åå¾é常å¤çæ¶åï¼é£ä¹å»ºè®®éµå¾ªä»¥ä¸æ¥éª¤ï¼
- 建ç«routerså ï¼å°ä¸å模åæåå°å¤ä¸ªgoæ件
- æ¯ä¸ªgoæ件æä¾ä¸ä¸ªè·¯ç±é ç½®æ¹æ³ï¼è¯¥æ¹æ³æ³¨åå®ç°ä¸ä¸ªåç»çææçè·¯ç±
- ä¹åmainæ¹æ³å¨è°ç¨æ件çè·¯ç±é ç½®æ¹æ³ï¼å®ç°è·¯ç±æ³¨å
ä¾åï¼ç¬¬ä¸ä¸ªè·¯ç±åç»goæ件ï¼
/src/../routers/apiRouter.go
package routers
// è¿éæ¯ä¸ä¸ªè·¯ç±é
ç½®æ¹æ³ ï¼ routerså
ä¸æä¸ä¸ªrouter对å¤å¼æ¾çæ¹æ³
func LoadRouter(e *gin.Engine) {
e.Group("v1")
{
v1.GET("/post", postHandler)
v1.GET("/get", getHandler)
}
...
}
ä¾åï¼ç¬¬äºä¸ªè·¯ç±åç»goæ件ï¼
/src/../routers/uaaRouter.go
package routers
// è¿éæ¯ä¸ä¸ªè·¯ç±é
ç½®æ¹æ³ ï¼ æ¯routerså
ä¸æä¸ä¸ªrouter对å¤å¼æ¾çæ¹æ³
func LoadUaaRouter(e *gin.Engine) {
e.Group("v1")
{
v1.GET("/post", postHandler)
v1.GET("/get", getHandler)
}
...
}
mainæ件å®ç°ï¼
func main() {
r := gin.Default()
// è°ç¨è¯¥æ¹æ³å®ç°æ³¨å
routers.LoadRouter(r)
routers.LoadUaaRouter(r) // 代表è¿æå¤ä¸ª
r.Run()
}
è§æ¨¡å¦æ继ç»æ©å¤§ä¹ææ´å¥½çå¤çæ¹å¼ï¼å»ºè®®å«å¤ªå¤§ï¼å°æå¡æå好ï¼ï¼
项ç®è§æ¨¡æ´å¤§çæ¶åï¼æ们å¯ä»¥éµå¾ªä»¥ä¸æ¥éª¤ï¼
- 1.建ç«routerså ï¼å é¨åå模åï¼å ï¼ï¼æ¯ä¸ªå æ个router.goæ件ï¼è´è´£è¯¥æ¨¡åçè·¯ç±æ³¨å
- 2.建ç«setup_router.goæ件ï¼å¹¶ç¼åä¸ä¸ªä¸ç¨çè·¯ç±åå§åæ¹æ³
- 3.main.goä¸æå¦ä¸æ¹å¼åå ¥éè¦æ³¨åçè·¯ç±ï¼å¯è¿è¡è·¯ç±çåå§å
- 建ç«routerså ï¼å é¨åå模åï¼å ï¼ï¼æ¯ä¸ªå æ个router.goæ件ï¼è´è´£è¯¥æ¨¡åçè·¯ç±æ³¨å
âââ routers
â â
â âââ say
â â âââ sayWorld.go
â â âââ router.go
â â
â âââ hello
â â âââ helloWorld.go
â â âââ router.go
â â
â âââ setup_router.go
â
âââ main.go
- 建ç«setup_router.goæ件ï¼å¹¶ç¼åä¸ä¸ªä¸ç¨çè·¯ç±åå§åæ¹æ³ï¼
type Register func(*gin.Engine)
func Init(routers ...Register) *gin.Engine {
// 注åè·¯ç±
rs := append([]Register{}, routers...)
r := gin.New()
// éåè°ç¨æ¹æ³
for _, register := range rs {
register(r)
}
return r
}
- main.goä¸æå¦ä¸æ¹å¼åå ¥éè¦æ³¨åçè·¯ç±ï¼å¯è¿è¡è·¯ç±çåå§åï¼
func main() {
// 设置éè¦å è½½çè·¯ç±é
ç½®
r := routers.Init(
say.Routers,
hello.Routers, // åé¢è¿å¯ä»¥æå¤ä¸ª
)
r.Run(":8080")
}
âè·ååæ°ï¼
âè·åè·¯å¾åæ°
å¯ä»¥éè¿å¹é çæ¹å¼ï¼è·åè·¯å¾ä¸çåæ°
- : åªè½å¹é 1个
- * å¯ä»¥å¹é ä»»æ个æ°
æ¹å¼ä¸,ä½¿ç¨ : åªå¹é 1个åæ°
ä¾åï¼
// æ¤è§åè½å¤å¹é
/user/xxxè¿ç§æ ¼å¼ï¼ä½ä¸è½å¹é
/user/ æ /userè¿ç§æ ¼å¼
router.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, "Hello %s", name)
})
è¿énameä¼ä½ä¸ºåæ°ï¼ä¾å¦è®¿é®http://localhost:8080/user/aaaï¼name便çäºaaa
æ¹å¼äº,ä½¿ç¨ * å¯ä»¥å¹é ä»»æ个æ°
router.GET("/user/:name/*action", func(c *gin.Context) {
name := c.Param("name")
action := c.Param("action")
message := name + " is " + action
c.String(http.StatusOK, message)
})
è§åï¼
/user/:name/*action
æ¤è§åæ¢è½å¹é /user/aaa/ æ ¼å¼ï¼ä¹è½å¹é /user/aaa/other1/other2 è¿ç§æ ¼å¼
localhost:8080/user/aaa/
访é®ï¼ localhost:8080/user/aaa/other1/
访é®ï¼ localhost:8080/user/aaa/other1/other2/
注æ* åªè½å¨æåç¨
âè·åURLåæ°
- è·åURLåæ°å¯ä»¥éè¿DefaultQuery()æQuery()æ¹æ³è·å
- è¥åæ°ä¸åå¨ï¼DefaultQuery()è¿åé»è®¤å¼ï¼Query()è¿å空串
r.GET("/user", func(c *gin.Context) {
//æå®é»è®¤å¼
name := c.DefaultQuery("name", "normal")
//è·åå
·ä½å¼
age := c.Query("age")
c.String(http.StatusOK, fmt.Sprintf("hello %s, your age is %s", name, age))
})
âè·åPostæ¹æ³
r.POST("/form", func(c *gin.Context) {
// 设置é»è®¤å¼
types := c.DefaultPostForm("type", "post")
username := c.PostForm("username")
password := c.PostForm("password")
// è¿å¯ä»¥ä½¿ç¨Queryå®ç° Get + Postçç»å
name := c.Query("name")
c.JSON(200, gin.H{
"username": username,
"password": password,
"types": types,
"name": name,
})
})
âURLåæ°åPoståæ°çè·åæ¡ä¾
ä¸é¢æ¯ URLåæ°åPoståæ°çè·åæ¡ä¾
func main() {
router := gin.Default()
router.GET("/welcome", welcome)
router.POST("/login", login)
router.POST("/post", getPost)
_ = router.Run()
}
// è·åGETä¼ å
func welcome(c *gin.Context) {
firstName := c.DefaultQuery("firstname", "unknown")
lastName := c.DefaultQuery("lastname", "unknown")
c.JSON(http.StatusOK, gin.H{
"first_name": firstName,
"last_name": lastName,
})
}
// è·åPOSTä¼ å
func login(c *gin.Context) {
username := c.DefaultPostForm("username", "test")
password := c.DefaultPostForm("password", "test")
c.JSON(http.StatusOK, gin.H{
"username": username,
"password": password,
})
}
// æ··åè·ååæ°
func getPost(c *gin.Context) {
// è·åGETåæ°
id := c.Query("id")
page := c.DefaultQuery("page", "0")
// è·åPOSTåæ°
name := c.PostForm("name")
message := c.DefaultPostForm("message", "")
c.JSON(http.StatusOK, gin.H{
"id": id,
"page": page,
"name": name,
"message": message,
})
}
âæ件ä¸ä¼ ï¼
å个æ件è·åï¼
å¨ä½¿ç¨ Gin æ¡æ¶æ¶ï¼å¯ä»¥ä½¿ç¨ c.FormFile() æ¹æ³æ¥è·åä¸ä¼ æ件ãè¿ä¸ªæ¹æ³ä¼è¿åä¸ä¸ª *multipart.FileHeader 对象ï¼å®å å«äºä¸ä¼ æ件çä¿¡æ¯ï¼æ¯å¦æ件åãæ件大å°ãæ件类åçãä½ å¯ä»¥éè¿è¿ä¸ªå¯¹è±¡è·åæ件çå 容ï¼å¹¶è¿è¡ç¸åºçå¤çã
以ä¸æ¯ä¸ä¸ªç®åç示ä¾ä»£ç ï¼æ¼ç¤ºäºå¦ä½ä½¿ç¨ c.FormFile() æ¹æ³æ¥ä¸ä¼ æ件ï¼
func uploadFile(c *gin.Context) {
file, err := c.FormFile("file")
if err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("ä¸ä¼ æ件失败: %s", err.Error()))
return
}
// ä¿åæ件å°æ¬å°
err = c.SaveUploadedFile(file, file.Filename)
if err != nil {
c.String(http.StatusInternalServerError, fmt.Sprintf("ä¿åæ件失败: %s", err.Error()))
return
}
c.String(http.StatusOK, fmt.Sprintf("æ件 %s ä¸ä¼ æå", file.Filename))
}
å¨ä¸é¢ç代ç ä¸ï¼æ们é¦å ä½¿ç¨ c.FormFile() æ¹æ³è·åä¸ä¼ æ件ãå¦æè·å失败ï¼æ们ä¼è¿åä¸ä¸ªé误信æ¯ãå¦æè·åæåï¼æ们就å¯ä»¥ä½¿ç¨ c.SaveUploadedFile() æ¹æ³å°æ件ä¿åå°æ¬å°ãæåï¼æ们è¿åä¸ä¸ªæåä¸ä¼ çä¿¡æ¯ã
å¨ä½¿ç¨ c.FormFile() æ¹æ³æ¶ï¼éè¦æ³¨æçæ¯ï¼åæ°ååºè¯¥ä¸ HTML 表åä¸çæ件ä¸ä¼ æ§ä»¶ç name å±æ§ç¸åãå¨ä¸é¢ç示ä¾ä¸ï¼æ们å设ä¸ä¼ æ件æ§ä»¶ç name å±æ§ä¸º "file"ã
ä¸ä¼ æ件çæ¶åï¼ä¹å¯ä»¥éè¿éå¶å¤§å°ï¼åè代ç å¦ä¸ï¼
r := gin.Default()
// ç»è¡¨åéå¶ä¸ä¼ å¤§å° (é»è®¤ 32 MiB)
r.MaxMultipartMemory = 8 << 20 // 8 MiB
r.POST("/upload", func(c *gin.Context) {
file, err := c.FormFile("file")
if err != nil {
c.String(500, "ä¸ä¼ æ件åºé")
}
// ä¸ä¼ å°æå®è·¯å¾
c.SaveUploadedFile(file, "C:/desktop/"+file.Filename)
c.String(http.StatusOK, "fileName:", file.Filename)
})
å¤ä¸ªæ件è·åï¼åªå±ç¤ºæ ¸å¿é¨åï¼ï¼
// è·åMultipartForm
form, err := c.MultipartForm()
if err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("get err %s", err.Error()))
}
// è·åæææ件
files := form.File["files"]
for _, file := range files {
// é个å
fmt.Println(file.Filename)
}
c.String(200, fmt.Sprintf("upload ok %d files", len(files)))
â表åéªè¯ï¼
GINæä¾äºä¸¤ç§æ¹æ³æ¥è¿è¡è¡¨åéªè¯: Must Bind / Should Bindã"Must Bind" å "Should Bind" æ¯å¨ç¼åç¨åºæ¶å¸¸ç¨ç两个æ¦å¿µã
- "Must Bind" æå³çå¿ é¡»ç»å®æ个åéæè åæ°ï¼å¦åç¨åºå°æ æ³æ£å¸¸è¿è¡ãè¿ç§æ åµé常åºç°å¨å¿ éçé 置项æè å¿ éçè¾å ¥åæ°æªè¢«æ£ç¡®è®¾ç½®çæ åµä¸ã
- "Should Bind" å表示建议ç»å®æ个åéæè åæ°ï¼ä½æ¯å¦ææªç»å®ä¹ä¸ä¼å½±åç¨åºçæ£å¸¸è¿è¡ãè¿ç§æ åµé常åºç°å¨å¯éçé 置项æè å¯éçè¾å ¥åæ°æªè¢«è®¾ç½®çæ åµä¸ã
ç®èè¨ä¹ï¼"Must Bind" è¡¨ç¤ºå¿ é¡»ç»å®ï¼å¦åç¨åºæ æ³æ£å¸¸è¿è¡ï¼"Should Bind" 表示建议ç»å®ï¼ä½ä¸æ¯å¿ é¡»çã
å¨ Gin æ¡æ¶ä¸è¿è¡è¡¨åéªè¯ï¼å¯ä»¥ä½¿ç¨ Gin æä¾ç binding å å validator å æ¥å®ç°ãæ3æ¥éª¤ï¼
- 1.å¯¼å ¥ binding å validator å
- 2.å®ä¹è¡¨åç»æä½ï¼å¹¶ä½¿ç¨ binding:"" å validate:"" æ è®°å段
- 3.å¨è·¯ç±å¤çå½æ°ä¸ä½¿ç¨ ShouldBindWith æ¹æ³è§£æ请æ±åæ°ï¼å¹¶ä½¿ç¨ validator å ç ValidateStruct æ¹æ³è¿è¡éªè¯
å ·ä½æ¥éª¤å¦ä¸ï¼
- å¯¼å ¥ binding å validator å ï¼
import (
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"gopkg.in/go-playground/validator.v9"
)
- å®ä¹è¡¨åç»æä½ï¼å¹¶ä½¿ç¨ binding:"required" å validate:"" æ è®°å段ï¼
type LoginForm struct {
Username string `json:"username" binding:"required" validate:"required"`
Password string `json:"password" binding:"required" validate:"required"`
}
å ¶ä¸ï¼
- binding:"required" 表示该å段å¨è¯·æ±ä¸å¿ é¡»åå¨ï¼å¦åä¼è¿å 400 é误ï¼
- validate:"required" 表示该åæ®µå¿ é¡»æå¼ï¼å¦åä¼è¿å 422 é误ã
- å¨è·¯ç±å¤çå½æ°ä¸ä½¿ç¨ ShouldBindWith æ¹æ³è§£æ请æ±åæ°ï¼å¹¶ä½¿ç¨ validator å ç ValidateStruct æ¹æ³è¿è¡éªè¯ï¼
func Login(c *gin.Context) {
var form LoginForm
if err := c.ShouldBindWith(&form, binding.JSON); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
validate := validator.New()
if err := validate.Struct(form); err != nil {
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()})
return
}
// TODO: å¤çç»å½é»è¾
}
å ¶ä¸ï¼æ两个è¦ç¹ï¼
- ShouldBindWith æ¹æ³ä¼æ ¹æ®è¯·æ±å¤´ä¸ç Content-Type èªå¨è§£æ请æ±åæ°ï¼ç¬¬äºä¸ªåæ°æå®äºè§£æå¨ç±»åï¼è¿é使ç¨äº JSON 解æå¨ï¼
- ValidateStruct æ¹æ³ä¼æ ¹æ®è¡¨åç»æä½çæ è®°è¿è¡éªè¯ï¼å¦ææé误åè¿åé误信æ¯ã
è¿æ ·å°±å¯ä»¥å¨ Gin æ¡æ¶ä¸è¿è¡è¡¨åéªè¯äºã
æ¥æ¶è¡¨å请æ±ï¼è·åç¨æ·ååå¯ç :
type LoginForm struct {
Username string `json:"username" binding:"required" validate:"required"`
Password string `json:"password" binding:"required" validate:"required"`
}
func main() {
router := gin.Default()
router.POST("/login", func(c *gin.Context) {
var loginForm LoginForm
if err := c.ShouldBind(&loginForm); err != nil {
fmt.Println(err.Error())
c.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"msg": "login",
})
})
_ = router.Run()
}
使ç¨POST请æ±åéJSONæ°æ®
http://localhost:8080/login
éªè¯å¤±è´¥
请æ±ä¿¡æ¯:
{
"password":"123"
}
请æ±çç»æ
éªè¯æå
请æ±ä¿¡æ¯:
{
"username":"David",
"password":"123"
}
请æ±çç»æ
âè®¾ç½®æ ¡éªçè§å
ä¸é¢çæ¡ä¾ï¼è®¾ç½®äº validate:"required" ã å¦ærequiredå段没ææ¶å°ï¼é误æ¥å¿ä¼åç¥ï¼
{
"error": "Key: 'LoginForm.Username' Error:Field validation for 'Username' failed on the 'required' tag"
}
é¤æ¤ä¹å¤ï¼è¿å¯ä»¥æå¾å¤çæ ¡éªãæ¯å¦ï¼éè¿tag设置èå´æ ¡éªï¼ä¾å¦
binding:"required,gt=10" =ã 代表该å¼éè¦å¤§äº10
time_format:"2006-01-02" time_utc:"1" =ã æ¶é´æ ¼å¼ æ ¡éª
æ¤å¤ï¼è¿å 许èªå®ä¹æ ¡éªæ¹å¼
âcontent-typeç»å®
å¨ Gin æ¡æ¶ä¸ï¼å¯ä»¥éè¿ ShouldBindJSON() æ¹æ³å°è¯·æ±ä½ä¸ç Content-Type æ¯ application/jsonçæ°æ®ä¸æå®çç»æä½è¿è¡ç»å®ãä¸é¢æ¯ä¸ä¸ªä½¿ç¨ ç»å®è¯·æ±ä½ç示ä¾ï¼
package main
import (
"github.com/gin-gonic/gin"
)
type Login struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
func main() {
r := gin.Default()
r.POST("/login", func(c *gin.Context) {
var login Login
// å°requestçbodyä¸çæ°æ®ï¼æç
§jsonæ ¼å¼è§£æå°ç»æä½
if err := c.ShouldBindJSON(&login); err != nil {
// å¦æåéçä¸æ¯jsonæ ¼å¼ï¼é£ä¹è¾åºï¼ "error": "invalid character '-' in numeric literal"
c.JSON(400, gin.H{"error": err.Error()})
return
}
// ...
})
r.Run()
}
å¨ä¸é¢ç示ä¾ä¸ï¼æ们å®ä¹äºä¸ä¸ª Login ç»æä½ï¼ç¨äºåå¨ç»å½è¯·æ±ä¸çç¨æ·ååå¯ç ã
å¨è·¯ç±å¤çå½æ°ä¸ï¼æä»¬ä½¿ç¨ ShouldBindJSON() æ¹æ³å°è¯·æ±ä½ä¸ç JSON æ°æ®ä¸ Login ç»æä½è¿è¡ç»å®ã
å¦æç»å®å¤±è´¥ï¼æ们å°è¿åä¸ä¸ª 400 é误ååºï¼å¦åæ们å°ç»§ç»å¤çç»å½é»è¾ã
éè¦æ³¨æçæ¯ï¼è¿é使ç¨äº binding:"required" æ ç¾æ¥æå® Username å Password åæ®µå¿ é¡»åå¨ãå¦æ请æ±ä½ä¸ç¼ºå°è¿äºå段ï¼ç»å®å°å¤±è´¥ï¼å¹¶è¿åä¸ä¸ªé误ååºã
é¤äºä½¿ç¨ShouldBindJSONï¼ä¹å¯ä»¥ä½¿ç¨Bindæ¹æ³ï¼åè代ç å¦ä¸ï¼
r.POST("/loginJSON", func(c *gin.Context) {
// 声ææ¥æ¶çåé
var login Login
// é»è®¤ç»å®formæ ¼å¼
if err := c.Bind(&login); err != nil {
// æ ¹æ®è¯·æ±å¤´ä¸content-typeèªå¨æ¨æ
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// è¾åºç»æ
c.JSON(http.StatusOK, gin.H{
"status": "200",
"user": login.User,
"password": login.Password,
})
})
Bind å ShouldBindJSON é½æ¯ Gin æ¡æ¶ä¸ç¨äºå°è¯·æ±ä½ä¸çæ°æ®ç»å®å°ç»æä½ä¸çæ¹æ³ã
å®ä»¬çåºå«å¨äºï¼Bind æ¹æ³ä¼æ ¹æ®è¯·æ±å¤´ä¸ç Content-Type èªå¨éæ©ç»å®æ¹æ³ï¼è ShouldBindJSON æ¹æ³ååªä¼ç»å® JSON æ ¼å¼ç请æ±ä½ã
举个ä¾åï¼å¦æ请æ±å¤´ä¸ç Content-Type æ¯ application/jsonï¼é£ä¹ Bind æ¹æ³å ShouldBindJSON æ¹æ³é½ä¼å°è¯·æ±ä½ä¸ç JSON æ°æ®ç»å®å°ç»æä½ä¸ãä½å¦æ Content-Type æ¯ application/xmlï¼é£ä¹ Bind æ¹æ³ä¼éæ©ç»å® XML æ ¼å¼ç请æ±ä½ï¼è ShouldBindJSON æ¹æ³åä¼è¿åé误ï¼å 为å®åªè½ç»å® JSON æ ¼å¼ç请æ±ä½ã
å æ¤ï¼å¦æä½ ç¡®å®è¯·æ±ä½ä¸çæ°æ®æ¯ JSON æ ¼å¼ï¼å¯ä»¥ç´æ¥ä½¿ç¨ ShouldBindJSON æ¹æ³ï¼å¦åå»ºè®®ä½¿ç¨ Bind æ¹æ³ã
å¨è¿è¡ç»å®æ¶ï¼å¯ä»¥ä½¿ç¨ Content-Type 请æ±å¤´æ¥æå®è¯·æ±ä½çæ ¼å¼ã
âGin æ°æ®è¿åç±»åï¼
gin常è§çä¸ç§ååºæ°æ®ï¼JSONãXMLãYAML
// 1.JSON
r.GET("/someJSON", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Json",
"status": 200,
})
})
// 2.XML
r.GET("/someXML", func(c *gin.Context) {
c.XML(200, gin.H{"message": "abc"})
})
// 3.YAML
r.GET("/someYAML", func(c *gin.Context) {
c.YAML(200, gin.H{"name": "zhangsan"})
})
// 4.protobuf
r.GET("/someProtoBuf", func(c *gin.Context) {
reps := []int64{1, 2}
data := &protoexample.Test{
Reps: reps,
}
c.ProtoBuf(200, data)
})
âGin éå®åï¼
å¨ Gin ä¸ï¼å¯ä»¥ä½¿ç¨ c.Redirect() æ¹æ³è¿è¡éå®åã该æ¹æ³æ¥å两个åæ°ï¼éå®åçç®æ URL åéå®åçç¶æç ãä¾å¦ï¼ä»¥ä¸ä»£ç å°ä¼å°æµè§å¨éå®åå° https://www.example.comï¼
func redirectHandler(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "https://www.example.com")
}
å ¶ä¸ï¼http.StatusMovedPermanently æ¯ä¸ä¸ªå¸¸éï¼è¡¨ç¤º 301 ç¶æç ãä½ ä¹å¯ä»¥ä½¿ç¨å ¶ä»ç¶æç ï¼ä¾å¦ http.StatusFoundï¼302ï¼ã
å¦æä½ æ³è¦å¨ URL ä¸å å«æ¥è¯¢åæ°ï¼å¯ä»¥å°å®ä»¬æ·»å å°ç®æ URL ä¸ãä¾å¦ï¼ä»¥ä¸ä»£ç å°ä¼å°æµè§å¨éå®åå° https://www.example.com?foo=barï¼
func redirectHandler(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "https://www.example.com?foo=bar")
}
âå¼æ¥æ§è¡ï¼
å¨ Gin ä¸å¼æ¥æ§è¡å¯ä»¥ä½¿ç¨ Go è¯è¨çåç¨ï¼goroutineï¼æ¥å®ç°ãå¨å¤ç请æ±çå¤çå½æ°ä¸ï¼å¯ä»¥ä½¿ç¨ go å ³é®åæ¥å¯å¨ä¸ä¸ªæ°çåç¨ï¼ä½¿å¾å¤çå½æ°å¯ä»¥ç«å³è¿åï¼å¹¶ä¸æ°çåç¨å¯ä»¥å¨åå°ç»§ç»æ§è¡ã
举个ä¾åï¼å¦ææ们éè¦å¨å¤çå½æ°ä¸æ§è¡ä¸ä¸ªæ¯è¾èæ¶çæä½ï¼å¯ä»¥è¿æ ·åï¼
func handleRequest(c *gin.Context) {
// éè¦æä¸ä¸ªå¯æ¬
copyContext := c.Copy()
// å¯å¨ä¸ä¸ªæ°çåç¨æ¥æ§è¡èæ¶æä½
go func() {
// æ§è¡èæ¶æä½
time.Sleep(5 * time.Second)
// æä½å®æåï¼å¯ä»¥éè¿ c.Writer åå
¥ååºæ°æ®
copyContext.Writer.WriteString("èæ¶æä½å®æ")
}()
}
å¨è¿ä¸ªä¾åä¸ï¼æ们使ç¨äºå¿åå½æ°æ¥å¯å¨ä¸ä¸ªæ°çåç¨ï¼è¯¥åç¨ä¼æ§è¡ä¸ä¸ªèæ¶æä½ï¼ç¶åå¨æä½å®æåéè¿ c.Writer åå ¥ååºæ°æ®ã
éè¦æ³¨æçæ¯ï¼å¨åç¨ä¸è®¿é® Gin çä¸ä¸æ对象 c æ¶ï¼éè¦ä½¿ç¨éå ï¼ä»¥é¿å ç«ææ¡ä»¶ãæ¤å¤ï¼è¿éè¦æ³¨æåç¨çæ°éï¼é¿å è¿å¤çåç¨å¯¼è´ç³»ç»èµæºèå°½ã
âä¼è¯æ§å¶ï¼
- cookieç¸å ³
- sessionç¸å ³
- tokenç¸å ³
âcookieç¸å ³
- å¯ä»¥éè¿ GetCookie æ¹æ³è·å客æ·ç«¯è¯·æ±ä¸æºå¸¦ç cookie
- å¯ä»¥éè¿ SetCookie æ¹æ³è®¾ç½® cookie
å¨ Gin ä¸ï¼å¯ä»¥éè¿ SetCookie æ¹æ³è®¾ç½® cookieï¼ä¾å¦ï¼
func main() {
router := gin.Default()
router.GET("/set-cookie", func(c *gin.Context) {
c.SetCookie("username", "johndoe", 3600, "/", "localhost", false, true)
c.String(http.StatusOK, "Cookie has been set")
})
router.Run(":8080")
}
å¨ä¸é¢çä¾åä¸ï¼æä»¬ä½¿ç¨ SetCookie æ¹æ³è®¾ç½®äºä¸ä¸ªå为 "username" ç cookieï¼å®çå¼ä¸º "johndoe"ï¼è¿ææ¶é´ä¸º 3600 ç§ï¼è·¯å¾ä¸º "/"ï¼åå为 "localhost"ï¼ä¸å¯ç¨å®å ¨æ å¿ï¼å¯ç¨ HTTPOnly æ å¿ãå¨å®¢æ·ç«¯å¯ä»¥éè¿ document.cookie å±æ§è¯»å该 cookieã
å¦å¤ï¼å¯ä»¥éè¿ GetCookie æ¹æ³è·å客æ·ç«¯è¯·æ±ä¸æºå¸¦ç cookieï¼ä¾å¦ï¼
func main() {
router := gin.Default()
router.GET("/get-cookie", func(c *gin.Context) {
username, err := c.Cookie("username")
if err != nil {
c.String(http.StatusBadRequest, "Cookie not found")
} else {
c.String(http.StatusOK, "Hello "+username)
}
})
router.Run(":8080")
}
å¨ä¸é¢çä¾åä¸ï¼æä»¬ä½¿ç¨ Cookie æ¹æ³è·å客æ·ç«¯è¯·æ±ä¸å为 "username" ç cookie çå¼ï¼å¹¶å°å ¶ä½ä¸ºå符串æ¼æ¥å°ååºä¸ãå¦æ客æ·ç«¯è¯·æ±ä¸æ²¡ææºå¸¦è¯¥ cookieï¼åè¿å "Cookie not found"ã
âsessionç¸å ³
Session æ¯ä¸ç§å¨å®¢æ·ç«¯åæå¡å¨ä¹é´ä¿åç¶æçæºå¶ï¼å®å¯ä»¥ç¨æ¥åå¨ç¨æ·çç»å½ä¿¡æ¯ãè´ç©è½¦ä¿¡æ¯çãGin æä¾äºä¸ä¸ªä¸é´ä»¶ gin-contrib/sessionsï¼å®å¯ä»¥å¸®å©æä»¬å¨ Gin ä¸ä½¿ç¨ Sessionã
è¦ä½¿ç¨ Gin Sessionï¼æ们éè¦å å®è£ gin-contrib/sessions å ãå¯ä»¥ä½¿ç¨ä»¥ä¸å½ä»¤è¿è¡å®è£ ï¼
go get github.com/gin-contrib/sessions
å®è£ å®æåï¼æ们éè¦å¨ä»£ç ä¸å¼å ¥ gin-contrib/sessions å ï¼å¹¶å建ä¸ä¸ª Session åå¨å¼æãGin æ¯æå¤ç§ Session åå¨å¼æï¼å æ¬å ååå¨ãCookie åå¨ãRedis åå¨çã以ä¸æ¯ä¸ä¸ªä½¿ç¨ Cookie åå¨ç示ä¾ï¼
import (
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// 设置 Session ä¸é´ä»¶
store := cookie.NewStore([]byte("secret"))
r.Use(sessions.Sessions("mysession", store))
// 设置路ç±
r.GET("/set", func(c *gin.Context) {
session := sessions.Default(c)
session.Set("username", "johndoe")
session.Save()
c.JSON(200, gin.H{"message": "Session saved"})
})
r.GET("/get", func(c *gin.Context) {
session := sessions.Default(c)
username := session.Get("username")
c.JSON(200, gin.H{"username": username})
})
r.Run(":8080")
}
å¨ä¸é¢ç示ä¾ä¸ï¼æ们é¦å å建äºä¸ä¸ª Cookie åå¨å¼æï¼å¹¶å°å ¶ä½ä¸º Session ä¸é´ä»¶æ·»å å° Gin ä¸ãç¶åï¼æ们å®ä¹äºä¸¤ä¸ªè·¯ç±ï¼ä¸ä¸ªç¨äºè®¾ç½® Sessionï¼å¦ä¸ä¸ªç¨äºè·å Sessionã
å¨è®¾ç½® Session çè·¯ç±ä¸ï¼æä»¬ä½¿ç¨ sessions.Default(c) è·åå½å请æ±ç Session 对象ï¼å¹¶ä½¿ç¨ session.Set() æ¹æ³è®¾ç½®ä¸ä¸ªé®å¼å¯¹ã
å¨è·å Session çè·¯ç±ä¸ï¼æ们åæ ·ä½¿ç¨ sessions.Default(c) è·åå½å请æ±ç Session 对象ï¼å¹¶ä½¿ç¨ session.Get() æ¹æ³è·åä¹å设置çé®å¼å¯¹ã
éè¦æ³¨æçæ¯ï¼Session ä¸é´ä»¶éè¦å¨è·¯ç±ä¹åæ·»å ï¼è¿æ ·æè½å¨è·¯ç±ä¸ä½¿ç¨ Sessionã
å¦å¤ï¼Session åå¨å¼æä¸ç secret åæ°åºè¯¥æ¯ä¸ä¸ªéæºå符串ï¼ç¨äºå å¯ Session æ°æ®ã
âtokenç¸å ³
é常为äºåå¸å¼åå®å ¨æ§ï¼æ们ä¼éåæ´å¥½çæ¹å¼ï¼æ¯å¦ä½¿ç¨Token 认è¯ï¼æ¥å®ç°è·¨å访é®ï¼é¿å CSRF æ»å»ï¼è¿è½å¨å¤ä¸ªæå¡é´å ±äº«ã
Token æ¯ä¸ç§ç¨äºèº«ä»½éªè¯åææçæºå¶ï¼é常ç¨äºä¿æ¤ Web åºç¨ç¨åºä¸çææèµæºã
å¨ Gin ä¸ï¼å¯ä»¥ä½¿ç¨ JWTï¼JSON Web Tokenï¼ä½ä¸º Token æºå¶ã
ä½¿ç¨ Gin å JWT å®ç° Token é´æçæ¥éª¤å¦ä¸ï¼
- å¨ç¨æ·ç»å½æååï¼çæä¸ä¸ª JWT Tokenï¼å¹¶å°å ¶è¿åç»å®¢æ·ç«¯ã
- 客æ·ç«¯å¨æ¯æ¬¡è¯·æ±ä¸å° Token ä½ä¸ºè¯·æ±å¤´åéç»æå¡å¨ã
- æå¡å¨å¨æ¥æ¶å°è¯·æ±åï¼è§£æ Tokenï¼éªè¯å ¶æææ§åæ£ç¡®æ§ã
- å¦æ Token ææï¼åå 许ç¨æ·è®¿é®ç¸åºçèµæºï¼å¦åï¼è¿åé误信æ¯ã
å¨ Gin ä¸ï¼å¯ä»¥ä½¿ç¨ç¬¬ä¸æ¹åºå¦ jwt-go æ¥å®ç° JWT ççæå解æãå ·ä½å®ç°æ¹å¼å¯ä»¥åè以ä¸ä»£ç ï¼
import (
"github.com/gin-gonic/gin"
"github.com/dgrijalva/jwt-go"
)
// çæ JWT Token
func generateToken(userId int64) (string, error) {
token := jwt.New(jwt.SigningMethodHS256)
claims := token.Claims.(jwt.MapClaims)
claims["userId"] = userId
tokenString, err := token.SignedString([]byte("secret"))
if err != nil {
return "", err
}
return tokenString, nil
}
// é´æä¸é´ä»¶
func authMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
tokenString := c.GetHeader("Authorization")
if tokenString == "" {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return []byte("secret"), nil
})
if err != nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
userId := int64(claims["userId"].(float64))
c.Set("userId", userId)
c.Next()
} else {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
}
}
// 示ä¾è·¯ç±
func main() {
r := gin.Default()
// ç»å½è·¯ç±
r.POST("/login", func(c *gin.Context) {
// 模æç»å½æå
userId := int64(123)
token, err := generateToken(userId)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "Internal Server Error"})
return
}
c.JSON(http.StatusOK, gin.H{"token": token})
})
// éè¦é´æçè·¯ç±
r.GET("/protected", authMiddleware(), func(c *gin.Context) {
userId := c.MustGet("userId").(int64)
c.JSON(http.StatusOK, gin.H{"userId": userId})
})
r.Run(":8080")
}
å¨ä¸é¢ç示ä¾ä¸ï¼æ们å®ä¹äºä¸ä¸ªçæ JWT Token çå½æ° generateToken åä¸ä¸ªé´æä¸é´ä»¶ authMiddlewareã
å¨ç»å½æååï¼æ们å°çæç Token è¿åç»å®¢æ·ç«¯ã
å¨éè¦é´æçè·¯ç±ä¸ï¼æä»¬ä½¿ç¨ authMiddleware ä½ä¸ºä¸é´ä»¶æ¥éªè¯ Token çæææ§:
- å¦æ Token ææï¼åå°ç¨æ·ç userId åå¨å°ä¸ä¸æä¸
- å¦åè¿åé误信æ¯ã
âGin ä¸é´ä»¶ï¼
ginä¸é´ä»¶ï¼ç±»ä¼¼spring mvc çæ¦æªå¨ ãè¿æ»¤å¨ã
ginä¸é´ä»¶ä½ç¨å°±æ¯å¨å¤çå ·ä½çroute请æ±æ¶ï¼æååä¸äºä¸å¡ï¼è¿å¯ä»¥å¨ä¸å¡æ§è¡å®åæ§è¡ä¸äºæä½ãæ¯å¦èº«ä»½æ ¡éªãæ¥å¿æå°çæä½ã
ä¸é´ä»¶å为ï¼å ¨å±ä¸é´ä»¶ å è·¯ç±ä¸é´ä»¶ï¼åºå«å¨äºåè ä¼ä½ç¨äºææè·¯ç±ã
å ¶å®ä½¿ç¨router := gin.Default()å®ä¹routeæ¶ï¼é»è®¤å¸¦äºLogger()åRecovery()ã
çç gin.Default() æºç å°±äºè§£äºï¼
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery())
return engine
}
âé»è®¤Ginä¸é´ä»¶
Ginæ¬èº«ä¹æä¾äºä¸äºä¸é´ä»¶ç»æ们使ç¨ï¼
func BasicAuth(accounts Accounts) HandlerFunc // 身份认è¯
func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc
func Bind(val interface{}) HandlerFunc //æ¦æªè¯·æ±åæ°å¹¶è¿è¡ç»å®
func ErrorLogger() HandlerFunc //é误æ¥å¿å¤ç
func ErrorLoggerT(typ ErrorType) HandlerFunc //èªå®ä¹ç±»åçé误æ¥å¿å¤ç
func Logger() HandlerFunc //æ¥å¿è®°å½
func LoggerWithConfig(conf LoggerConfig) HandlerFunc
func LoggerWithFormatter(f LogFormatter) HandlerFunc
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc
func Recovery() HandlerFunc
func RecoveryWithWriter(out io.Writer) HandlerFunc
func WrapF(f http.HandlerFunc) HandlerFunc //å°http.HandlerFuncå
è£
æä¸é´ä»¶
func WrapH(h http.Handler) HandlerFunc //å°http.Handlerå
è£
æä¸é´ä»¶
âèªå®ä¹Ginä¸é´ä»¶
èªå®ä¹ä¸é´ä»¶çæ¹å¼å¾ç®åï¼æ们åªéè¦å®ç°ä¸ä¸ªå½æ°ï¼è¿ågin.HandlerFuncç±»åçåæ°å³å¯ï¼
// HandlerFunc æ¬è´¨å°±æ¯ä¸ä¸ªå½æ°ï¼å
¥å为 *gin.Context
// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)
èªå®ä¹Ginä¸é´ä»¶ ç¨äºæ£æ¥token ,示ä¾ä»£ç ï¼
func TokenRequired() gin.HandlerFunc {
return func(c *gin.Context) {
var token string
for k, v := range c.Request.Header {
if k == "x-token" {
token = v[0]
}
fmt.Println(k, v, token)
}
if token != "test" {
c.JSON(http.StatusOK, gin.H{
"msg": "login failed",
})
c.Abort()
}
c.Next()
}
}
func main() {
router := gin.Default()
router.Use(TokenRequired())
router.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
_ = router.Run()
}
âä¸é´ä»¶æ§å¶çæ¹æ³
å¨ Gin æ¡æ¶ä¸ï¼ä¸é´ä»¶ç顺åºé常éè¦ï¼å 为å®ä»¬æç §æ·»å ç顺åºä¾æ¬¡æ§è¡ãå¦ææ¨å¸æä¸é´ä»¶ä»¥ç¹å®ç顺åºæ§è¡ï¼å¯ä»¥ä½¿ç¨ Gin æ¡æ¶æä¾ç Use() æ¹æ³æ¥æ·»å ä¸é´ä»¶ãä¾å¦ï¼å¦ææ¨å¸æå¨æ¥å¿ä¸è®°å½è¯·æ±ä¹åå æ§è¡èº«ä»½éªè¯ä¸é´ä»¶ï¼ååºè¯¥å æ·»å 身份éªè¯ä¸é´ä»¶ï¼ç¶ååæ·»å æ¥å¿ä¸é´ä»¶ï¼å¦ä¸æ示ï¼
router := gin.Default()
// æ·»å 身份éªè¯ä¸é´ä»¶
router.Use(authMiddleware)
// æ·»å æ¥å¿ä¸é´ä»¶
router.Use(loggerMiddleware)
// æ·»å è·¯ç±å¤çå½æ°
router.GET("/hello", func(c *gin.Context) {
c.String(http.StatusOK, "Hello, World!")
})
router.Run(":8080")
å¨è¿ä¸ªä¾åä¸ï¼authMiddleware ä¼å¨ loggerMiddleware ä¹åæ§è¡ï¼å 为å®å 被添å å° Gin çä¸é´ä»¶å¤çé¾ä¸ã
å¦å¤ï¼ginæä¾äºä¸¤ä¸ªå½æ°Abort()åNext()ï¼äºè åºå«å¨äºï¼
next()å½æ°ä¼è·³è¿å½åä¸é´ä»¶ä¸next()åçé»è¾ï¼å½ä¸ä¸ä¸ªä¸é´ä»¶æ§è¡å®æååæ§è¡å©ä½çé»è¾
abort()å½æ°æ§è¡ç»æ¢å½åä¸é´ä»¶ä»¥åçä¸é´ä»¶æ§è¡ï¼ä½æ¯ä¼æ§è¡å½åä¸é´ä»¶çåç»é»è¾
举ä¾åæ´å¥½ç解ï¼æ们注åä¸é´ä»¶é¡ºåºä¸ºm1ãm2ãm3ï¼å¦æéç¨next() æ§è¡é¡ºåºå°±æ¯
m1çnext()åé¢ãm2çnext()åé¢ãm3çnext()åé¢ã
ä¸å¡é»è¾
m3çnext()åç»ãm2çnext()åç»ãm1çnext()åç»ã
é£å¦æm2ä¸é´è°ç¨äºAbort()ï¼åm3åä¸å¡é»è¾ä¸ä¼æ§è¡ï¼åªä¼æ§è¡m2çnext()åç»ãm1çnext()åç»ã
40å²èæ¶æå¸å°¼æ©æ示ï¼
è¿ä¸ªæµç¨ï¼åjavaçè¿æ»¤å¨è´£ä»»é¾æ¨¡å¼ï¼ä½å ¶ä¸è´
âå±é¨ä¸é´ä»¶
Gin æ¡æ¶ä¸æ两ç§ç±»åçä¸é´ä»¶ï¼
- å ¨å±ä¸é´ä»¶
- å±é¨ä¸é´ä»¶ã
å ¨å±ä¸é´ä»¶æ¯å¨ Gin å®ä¾å建æ¶æ·»å çï¼å®ä»¬å°åºç¨äºææçè·¯ç±è¯·æ±ã
å ¨å±ä¸é´ä»¶å¯ä»¥éè¿ Use() æ¹æ³æ·»å å° Gin å®ä¾ä¸ï¼ä¾å¦ï¼
router := gin.Default()
router.Use(Logger())
å¨ä¸é¢ç代ç ä¸ï¼Logger() å½æ°æ¯ä¸ä¸ªå ¨å±ä¸é´ä»¶ï¼å®å°å¨ææ请æ±ä¹åæå°è¯·æ±çä¿¡æ¯ã
å±é¨ä¸é´ä»¶åªä¼åºç¨äºæäºè·¯ç±è¯·æ±ã
- å¯ä»¥å¨è·¯ç±è®¾ç½®çæ¶åï¼è®¾ç½®å±é¨ä¸é´ä»¶
- å¯ä»¥ä½¿ç¨ Group æ¹æ³å建ä¸ä¸ªè·¯ç±ç»ï¼ç¶åå¨è¿ä¸ªè·¯ç±ç»ä¸è®¾ç½®å±é¨ä¸é´ä»¶
å¨è·¯ç±è®¾ç½®çæ¶åï¼è®¾ç½®å±é¨ä¸é´ä»¶ï¼
router := gin.Default()
// æ·»å å
¨å±ä¸é´ä»¶
router.Use(Logger())
// éè¿ Handle()æ·»å å±é¨ä¸é´ä»¶
router.GET("/posts", Auth(), GetPosts)
å¨ä¸é¢ç代ç ä¸ï¼Auth() å½æ°æ¯ä¸ä¸ªå±é¨ä¸é´ä»¶ï¼å®åªä¼å¨ /posts è·¯ç±è¯·æ±ä¸åºç¨ã
å¨ Gin ä¸ï¼å¯ä»¥ä½¿ç¨ Group æ¹æ³å建ä¸ä¸ªè·¯ç±ç»ï¼ç¶åå¨è¿ä¸ªè·¯ç±ç»ä¸è®¾ç½®å±é¨ä¸é´ä»¶ã
ä¾å¦ï¼ä»¥ä¸ä»£ç å建äºä¸ä¸ªè·¯ç±ç» /apiï¼å¹¶å¨è¿ä¸ªè·¯ç±ç»ä¸è®¾ç½®äºä¸ä¸ªå±é¨ä¸é´ä»¶ authMiddlewareï¼
func authMiddleware(c *gin.Context) {
// æ£æ¥ç¨æ·æ¯å¦å·²ç»ç»å½
// å¦æç¨æ·å·²ç»ç»å½ï¼å继ç»å¤ç请æ±
// å¦æç¨æ·æªç»å½ï¼åè¿å 401 Unauthorized é误
}
func main() {
r := gin.Default()
api := r.Group("/api")
api.Use(authMiddleware)
api.GET("/users", func(c *gin.Context) {
// å¤ç GET /api/users 请æ±
})
api.POST("/users", func(c *gin.Context) {
// å¤ç POST /api/users 请æ±
})
r.Run()
}
å¨ä¸é¢ç代ç ä¸ï¼authMiddleware å½æ°æ¯ä¸ä¸ªä¸é´ä»¶å½æ°ï¼ç¨äºæ£æ¥ç¨æ·æ¯å¦å·²ç»ç»å½ãapi æ¯ä¸ä¸ªè·¯ç±ç»ï¼è¡¨ç¤ºææ以 /api å¼å¤´ç请æ±é½ä¼è¿å ¥è¿ä¸ªè·¯ç±ç»ãapi.Use(authMiddleware) 表示å¨è¿ä¸ªè·¯ç±ç»ä¸ä½¿ç¨ authMiddleware ä¸é´ä»¶ã
è¿æ ·ï¼ææ以 /api å¼å¤´ç请æ±é½ä¼å è¿å ¥ authMiddleware ä¸é´ä»¶å½æ°è¿è¡èº«ä»½éªè¯ï¼å¦æ身份éªè¯éè¿ï¼å继ç»å¤ç请æ±ï¼å¦åè¿å 401 Unauthorized é误ã
âGinçè·¯ç±æ¡æ¶ï¼httprouteræ¡æ¶
Ginæ¯Martiniçæçé«å¥½å¤,
ç©¶å ¶åå æ¯ï¼å 为Gin使ç¨äºhttprouterè¿ä¸ªè·¯ç±æ¡æ¶, httprouterçgitå°åæ¯: httprouteræºç (https://github.com/julienschmidt/httprouter).
httprouterå ¶å®å°±æ¯ä½¿ç¨äºä¸ä¸ªradix tree(åç¼æ )æ¥ç®¡ç请æ±çURL,
âåç¼æ ååºæ°æ ï¼radix treeï¼çåºå«ï¼
trieåå«åç¼æ ï¼æ¯ä¸ä¸ªå¤åæ ï¼å¹¿æ³åºç¨äºå符串æç´¢ï¼æ¯ä¸ªæ èç¹åå¨ä¸ä¸ªå符ï¼ä»æ ¹èç¹å°ä»»æä¸ä¸ªå¶åç»ç¹ä¸²èµ·æ¥å°±æ¯ä¸ä¸ªå符串ï¼radix treeæ¯ä¼åä¹åçåç¼æ ï¼å¯¹ç©ºé´è¿ä¸æ¥å缩ã
ä¸å¾å·¦ä¾§æ¯å符串 sex,seed,sleep,son å个å段串çTrieæ°æ®ç»æ表示. å¯ç¨çå°sleepè¿ä¸ªå符串éè¦5个èç¹è¡¨ç¤º. å ¶å®eåé¢åªè·ä¸ä¸ªp, ä¹å°±æ¯åªæä¸ä¸ªåèç¹, æ¯å®å ¨å¯ä»¥åç¶èç¹å缩å并ç. å³ä¾§æ¯ä¼ååçæ°æ®ç»æ, èçäºç©ºé´,åæ¶ä¹æé«äºæ¥è¯¢æç(左边å符串sleepæ¥è¯¢éè¦5æ¥, å³è¾¹åªéè¦3æ¥), è¿å°±æ¯radix tree.
âginæ¡æ¶è·¯ç±åç
httprouteræ¯ä¸ä¸ªé«æ§è½è·¯ç±ååå¨ï¼å®è´è´£å°ä¸åæ¹æ³çå¤ä¸ªè·¯å¾åå«æ³¨åå°å个handleå½æ°ï¼å½æ¶å°è¯·æ±æ¶ï¼è´è´£å¿«éæ¥æ¾è¯·æ±çè·¯å¾æ¯å¦æç¸å¯¹åºçå¤çå½æ°ï¼å¹¶ä¸è¿è¡ä¸ä¸æ¥ä¸å¡é»è¾å¤çãgolangçginæ¡æ¶éç¨äºhttprouterè¿è¡è·¯ç±å¹é ï¼httprouter æ¯éè¿radix treeæ¥è¿è¡é«æçè·¯å¾æ¥æ¾ï¼åæ¶è·¯å¾è¿æ¯æ两ç§éé 符å¹é ã
httprouterä¼å¯¹æ¯ç§httpæ¹æ³ï¼postãgetçï¼é½ä¼çæä¸æ£µåºæ°æ ï¼å ¶ä¸æ èç¹nodeç»æå¦ä¸ï¼
type nodestruct {
path string //该èç¹å¯¹åºçpath
indices string //åèç¹pathç第ä¸ä¸ªbyteçéå
wildChild bool //æ¯å¦éé
符
nType nodeType //èç¹ç±»å
priority uint32
children []*node //åèç¹
handlers HandlersChain //handleå¦æä¸ä¸ºnilï¼å说ææ¯ä¸ä¸ªè·¯å¾å符串çç»ç¹!!!
fullPath string
}
type nodeType uint8
const (
static nodeType =iota // default æ®éèç¹
root //æ ¹èç¹
param //åæ°èç¹ /user/{id}ï¼id å°±æ¯ä¸ä¸ªåæ°èç¹
catchAll //éé
符
)
âgin注åè·¯ç±è¿ç¨ï¼
å¦ä¸å·¦è¾¹æ¯/sex,/sleep,/sonçå é¨åç°, å³è¾¹æ¯æå ¥/seed åå é¨çåç°:
ginè·¯ç±
ç®åçæ¥è¯´æ¯ä¸ä¸ªæ³¨åç url é½ä¼éè¿ / åå为 n 个æ èç¹ï¼httprouter ä¼æä¸äºåºå«ï¼ä¼åå¨æ ¹åè£ï¼ï¼ç¶åæå°ç¸åº method æ ä¸å»ï¼æ以ä¸å¡ä¸æå ç§ä¸åç method æ¥å£ï¼å°±ä¼äº§ç对åºçåç¼æ ã
å¨ httprouter ä¸ï¼èç¹è¢«å为 4 ç§ç±»åï¼
- static - éæèç¹ï¼/user /api è¿ç§
- root - æ ¹ç»ç¹
- param - åæ°èç¹ /user/{id}ï¼id å°±æ¯ä¸ä¸ªåæ°èç¹
- catchAll - éé 符
å ¶å®æ´ä¸ªå¹é çè¿ç¨ä¹æ¯è¾ç®åï¼éè¿å¯¹åºç method æ¿å°åç¼æ ï¼ç¶åå¼å§è¿è¡ä¸ä¸ªå¹¿åº¦ä¼å çå¹é ã
è¿éå¼å¾å¦ä¹ çä¸ç¹æ¯ï¼httprouter 对ä¸çº§èç¹çæ¥æ¾è¿è¡äºä¼åï¼ç®åæ¥è¯´å°±æ¯æå½åèç¹çä¸çº§èç¹çé¦åæ¯ç»´æ¤å¨æ¬èº«ï¼å¹é æ¶å è¿è¡ç´¢å¼çæ¥æ¾ã
注æï¼
ginä¸ç¸åhttpæ¹æ³çè·¯ç±æ ä¸ï¼åæ°ç»ç¹åéæç»ç¹æ¯å²çªçï¼ä¹å°±æ¯:
get æ¹æ³ï¼å¦æåå¨äº /user/:nameè¿æ ·çè·¯å¾ï¼å°±ä¸è½åæ·»å /user/getNameè¿æ ·çéæè·¯å¾ï¼å¦åä¼æ¥å²çªï¼
âå¾®æå¡ç»ä¸é 置管çï¼
å¨åºç¨ç¨åºçè¿è¡çå½å¨æä¸ï¼æç´æ¥çå ³ç³»ä¹ä¸å°±æ¯åºç¨çé 置读ååæ´æ°ã
â为ä»ä¹éè¦åå¸å¼é ç½®ä¸å¿
åå¸å¼é ç½®ä¸å¿çä¼å¿å¨äºå®å¯ä»¥å°å¤ä¸ªæ¨¡åç³»ç»çåé ç½®æ件ï¼å ¨é¨é ç½®å¨é ç½®ä¸å¿ç»ä¸ç®¡çï¼æ ééå¯æå¡å¨å³å¯å¨æå·æ°å è½½é 置信æ¯ã
æ¤å¤ï¼åå¸å¼é ç½®ä¸å¿è¿å¯ä»¥å®ç°ä¸åç¯å¢é ç½®é离(å¼åãæµè¯ãé¢åå¸ãç°åº¦/线ä¸),é«æ§è½ãé«å¯ç¨æ§ï¼è¯·æ±éå¤ãé«å¹¶åçç¹æ§ã
âåå¸å¼é ç½®ä¸å¿ææ¯éå
- ç®å主æµçåå¸å¼é ç½®ä¸å¿ï¼spring cloud configãapolloånacosï¼spring cloud configå±äºjavaçspringä½ç³»ï¼æ们就èèapolloånacos
- apolloånacosï¼apolloæ¯æºç¨å¼æºãnacosæ¯é¿éå¼æº aãapollo大èå ¨ï¼åè½å®åï¼nacoså°èå ¨ï¼å¯ä»¥å¯¹æ¯ædjangoåflaskçåºå«bãé¨ç½²nacosæ´å ç®åcãnacosä¸æ¢æ¯æé ç½®ä¸å¿è¿æ¯ææå¡æ³¨åååç°dãé½æ¯æåç§è¯è¨ï¼ä¸è¿apolloæ¯ç¬¬ä¸æ¹æ¯æçï¼nacosæ¯å®æ¹æ¯æåç§è¯è¨
- nacoså®ç½ï¼https://nacos.io/zh-cn/
- apolloçgitå°åï¼https://github.com/apolloconfig/apollo
- apolloånacosåè½å¯¹æ¯
åè½ç¹ | apollo | nacos |
å¼æºæ¶é´ | 2016.5 | 2018.6 |
é ç½®å®æ¶æ¨é | æ¯æ(httpé¿è½®è¯¢) | æ¯æ(httpé¿è½®è¯¢) |
é ç½®åæ» | æ¯æ | æ¯æ |
ç°åº¦åå¸ | æ¯æ | å¾ æ¯æ |
æé管ç | æ¯æ | æ¯æ |
å¤cluster | æ¯æ | æ¯æ |
çå¬æ¥è¯¢ | æ¯æ | æ¯æ |
å¤è¯è¨ | 主æµè¯è¨ | 主æµè¯è¨ï¼å®æ¹æ¯æï¼ |
é讯åè®® | http | http |
âSpring Cloud Nacos
âä¼ç¹ï¼
1ï¼å¼ç®±å³ç¨ï¼éç¨äºdubboï¼spring cloudç
2ï¼AP模åï¼æ°æ®æç»ä¸è´æ§
3ï¼æ³¨åä¸å¿ï¼é ç½®ä¸å¿äºåä¸(äºåä¸ä¹ä¸ä¸å®æ¯ä¼ç¹)ï¼æä¾æ§å¶å°ç®¡ç
4ï¼çº¯å½äº§ï¼åç§æä¸æææ¡£ï¼ä¹ ç»ååä¸èéª
â缺ç¹ï¼
1ï¼ååå¼æºä¸ä¹ ï¼ç¤¾åºç度ä¸å¤ï¼ä¾ç¶åå¨bug
âSpring Cloud Eurekaï¼
âä¼ç¹ï¼
1ï¼Spring Cloud å®æ¹æ¨è
2ï¼AP模åï¼æ°æ®æç»ä¸è´æ§
3ï¼å¼ç®±å³ç¨ï¼å ·ææ§å¶å°ç®¡ç
â缺ç¹ï¼
1ï¼å®¢æ·ç«¯æ³¨åæå¡ä¸æ¥ææä¿¡æ¯ï¼èç¹å¤çæ åµä¸ï¼ç½ç»ï¼æå¡ç«¯ååè¿å¤§ï¼ä¸æµªè´¹å å
2ï¼å®¢æ·ç«¯æ´æ°æå¡ä¿¡æ¯éè¿ç®åç轮询æºå¶ï¼å½æå¡æ°é巨大æ¶ï¼æå¡å¨ååè¿å¤§ã
3ï¼cluster伸缩æ§ä¸å¼ºï¼æå¡ç«¯clusteréè¿å¹¿æå¼çå¤å¶ï¼å¢å æå¡å¨åå
4ï¼Eureka2.0 éæºï¼Spring Cloudææ°çæ¬è¿æ¯ä½¿ç¨ç1.Xçæ¬çEurekaï¼
âgolangçåèé ç½®ç»ä»¶viper
é¤äºåå¸å¼å¾®æå¡çé ç½®ä¸å¿nacosãeurekaè¿äºã
golangä¸ï¼ä¹èªå·±çåèé ç½®ä¸å¿ viper ï¼è¿ä¸ªä½¿ç¨è¯çå¾é«ã
å¯ä»¥è¯´ï¼viper æ¯golangçåèé ç½®ç»ä»¶ä¸ï¼ä½¿ç¨çé常é«çä¸ä¸ªã
viper ç»å consulçç»ä»¶ï¼ä¹è½å ·å¤åå¸å¼é 置管çè½åã
âéè¿ viperå®ç°ç»ä¸é 置管çåè½ï¼
â1ãviperçä»ç»
viperæ¯goä¸ä¸ªå¼ºå¤§çæµè¡çé 置解å³æ¹æ¡çåºãæ大é项ç®é½ä½¿ç¨è¯¥åºï¼æ¯å¦hugo, dockerçã
Viper æ¯ä¸ä¸ªå®æ´ç Go åºç¨ç¨åºé 置解å³æ¹æ¡ï¼ä¼å¿å°±å¨äºå¼å项ç®ä¸ä½ ä¸å¿ å»æå¿é ç½®æ件çæ ¼å¼èæ¯è®©ä½ è ¾åºææ¥ä¸æ³¨äºé¡¹ç®çå¼åã
viper å ¶ç¹æ§å¦ä¸ï¼
- æ¯æ JSON/TOML/YAML/HCL/envfile/Java properties çå¤ç§æ ¼å¼çé ç½®æ件ï¼
- å¯ä»¥è®¾ç½®çå¬é ç½®æ件çä¿®æ¹ï¼ä¿®æ¹æ¶èªå¨å è½½æ°çé ç½®ï¼
- ä»ç¯å¢åéãå½ä»¤è¡é项åio.Readerä¸è¯»åé ç½®ï¼
- ä»è¿ç¨é 置系ç»ä¸è¯»ååçå¬ä¿®æ¹ï¼å¦ etcd/Consulï¼
- 代ç é»è¾ä¸æ¾ç¤ºè®¾ç½®é®å¼
注ï¼Viper让éè¦éå¯æå¡å¨æè½ä½¿é ç½®çæçæ¥åä¸å»ä¸å¤è¿ï¼ï¼ï¼è¿ææ¯VIperæ大çé å
å®åºæ¬ä¸å¯ä»¥å¤çææç±»åçé ç½®éæ±åæ ¼å¼, viperæ¯æåè½
- 设置é»è®¤é ç½®
- æ¯æåç§é ç½®æ件ï¼å¦JSONï¼TOML, YAML, HCL, envfileåJavaå±æ§é ç½®æ件
- æ¯æçå¬æ件åå以åéæ°è¯»åé ç½®
- æ¯æä»ç¯å¢åé读åé ç½®
- æ¯æä»è¿ç¨é 置系ç»(etcdæConsul)读åé ç½®ï¼å¹¶è½çå¬è¿ç¨é 置修æ¹
- æ¯æä»å½ä»¤è¡æ å¿Flag读åé ç½®ï¼æ¯å¦æé cobra使ç¨
- æ¯æ读åç¼å²åºæ°æ®
Viper主è¦ä¸ºæ们å以ä¸å·¥ä½:
- æ¥æ¾ãå è½½å解ç»JSONãTOMLãYAMLãHCLãINIãenvfileæJavaå±æ§æ ¼å¼çé ç½®æ件ã
- æä¾ä¸ç§æºå¶æ¥ä¸ºä¸åçé ç½®é项设置é»è®¤å¼ã
- æä¾ä¸ç§æºå¶æ¥ä¸ºéè¿å½ä»¤è¡æ å¿æå®çé项设置è¦çå¼ã
- æä¾å«åç³»ç»ï¼ä»¥ä¾¿å¨ä¸ç ´åç°æ代ç çæ åµä¸è½»æ¾éå½ååæ°ã
- å½ç¨æ·æä¾äºä¸é»è®¤å¼ç¸åçå½ä»¤è¡æé ç½®æ件æ¶ï¼å¾å®¹æåºåå®ä»¬ã
â2ãviperçå®è£ å使ç¨
vieprçå®è£ å¾ç®åï¼ç´æ¥åå·¥ç¨ä¸ä½¿ç¨go getå½ä»¤å®è£ å³å¯
$ go get github.com/spf13/viper
â2.1 Viper对象çå建
使ç¨viper.New()å½æ°å建ä¸ä¸ªViper Structï¼å¦ï¼
viper := viper.New()
Viperçæ¯viperåºç主è¦å®ç°å¯¹è±¡, viperæä¾äºä¸é¢çæ¹æ³å¯ä»¥è·åViperå®ä¾ï¼
func GetViper() *Viper
func New() *Viper
func NewWithOptions(opts ...Option) *Viper
func Sub(key string) *Viper
使ç¨viper.GetViper()è·åçä¸ºå ¨å±çViperå®ä¾å¯¹è±¡ï¼é»è®¤ä½¿ç¨viperå 使ç¨ä¹æ¯è¯¥å ¨å±Viperå®ä¾ã
æ¥çviperçæºç ï¼å¯ä»¥çå°viperé»è®¤æä¾äºä¸ä¸ªå ¨å±çViperå®ä¾ï¼
var v *Viper
func init() {
v = New()
}
// New returns an initialized Viper instance.
func New() *Viper {
v := new(Viper)
v.keyDelim = "."
v.configName = "config"
v.configPermissions = os.FileMode(0o644)
v.fs = afero.NewOsFs()
v.config = make(map[string]interface{})
v.override = make(map[string]interface{})
v.defaults = make(map[string]interface{})
v.kvstore = make(map[string]interface{})
v.pflags = make(map[string]FlagValue)
v.env = make(map[string][]string)
v.aliases = make(map[string]string)
v.typeByDefValue = false
v.logger = jwwLogger{}
v.resetEncoding()
return v
}
NewåNewWithOptions为æ们æä¾äºå建å®ä¾çæ¹æ³
func New1() *viper.Viper {
return viper.New()
}
func New2() *viper.Viper {
return viper.NewWithOptions()
}
Sub为æ们读ååé 置项æä¾äºä¸ä¸ªæ°çå®ä¾Viper
v := viper.Sub("db")
url := v.Get("url")
log.Printf("mysql url:%s\n", url)
â2.2 é¢è®¾ä¸äºé»è®¤é ç½®
éè¿SetDefault é²èï¼å¯ä»¥ é ç½®é»è®¤å¼
viper.SetDefault("key1","value1")
viper.SetDefault("key2","value2")
viper.SetDefault("ContentDir", "content")
viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})
viper.SetDefault("redis.port", 6379)
viper.SetDefault("mysql.url", "root:root@tcp(127.0.0.1:3306)/stock?charset=utf8mb4&parseTime=True&loc=Local")
â2.3 key大å°åé®é¢
viperçé ç½®çkeyå¼æ¯ä¸åºå大å°åï¼å¦ï¼
# å°åçkey
viper.set("test","this is a test value")
# 大åçkeyï¼ä¹å¯ä»¥è¯»å°å¼
fmt.Println(viper.get("TEST"))//è¾åº"this is a test value"
â2.4 ä»é ç½®æ件读å
ç´æ¥æå®æ件路å¾
viper.SetConfigFile("./config.yaml")
viper.ReadInConfig()
fmt.Println(viper.Get("test"))
å¤è·¯å¾æ¥æ¾
viper.SetConfigName("config") // é
ç½®æ件åï¼ä¸éè¦åç¼å
viper.SetConfigType("yml") // é
ç½®æä»¶æ ¼å¼
viper.AddConfigPath("/etc/appname/") // æ¥æ¾é
ç½®æ件çè·¯å¾
viper.AddConfigPath("$HOME/.appname") // æ¥æ¾é
ç½®æ件çè·¯å¾
viper.AddConfigPath(".") // æ¥æ¾é
ç½®æ件çè·¯å¾
err := viper.ReadInConfig() // æ¥æ¾å¹¶è¯»åé
ç½®æ件
if err != nil { // å¤çé误
panic(fmt.Errorf("Fatal error config file: %w n", err))
}
读åé ç½®æ件æ¶ï¼å¯è½ä¼åºç°é误ï¼å¦ææ们æ³å¤ææ¯å¦æ¯å 为æ¾ä¸å°æ件èæ¥éçï¼å¯ä»¥å¤æerræ¯å¦ä¸ºConfigFileNotFoundErrorã
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
} else {
}
}
ä¸é¢æ们çä¸ä¸æä½å®ä¾ï¼ å çæ们çé ç½®æ件app.ymlæ件ï¼
app:
name: viper-test
mode: dev
db:
mysql:
url: "root:root@tcp(127.0.0.1:3306)/stock?charset=utf8mb4&parseTime=True&loc=Local"
redis:
host: 127.0.0.1
port: 6067
db: 0
passwd: 123456
åå§åé ç½®
func InitConfig() (*viper.Viper, error) {
v := viper.New()
v.AddConfigPath(".") // æ·»å é
ç½®æ件æ索路å¾ï¼ç¹å·ä¸ºå½åç®å½
v.AddConfigPath("./configs") // æ·»å å¤ä¸ªæç´¢ç®å½
v.SetConfigType("yaml") // å¦æé
ç½®æ件没æåç¼ï¼å¯ä»¥ä¸ç¨é
ç½®
v.SetConfigName("app.yml") // æ件åï¼æ²¡æåç¼
// v.SetConfigFile("configs/app.yml")
// 读åé
ç½®æ件
if err := v.ReadInConfig(); err == nil {
log.Printf("use config file -> %s\n", v.ConfigFileUsed())
} else {
return nil,err
}
return v, nil
}
é¦å è¿éæ们添å ä¸ä¸ªé ç½®æ件æ索路å¾ï¼ç¹å·è¡¨ç¤ºå½åè·¯å¾ï¼æ索路å¾å¯ä»¥æ·»å å¤ä¸ªç¶å设置äºé ç½®æ件类åï¼è¿éæ们设置æ件类å为yamlï¼
æ¥çæ们设置äºé ç½®æ件å称ï¼è¿ä¸ªæ件å¯ä»¥ä»é ç½®çæ索路å¾ä»æ¥æ¾ã
æåæ们éè¿æä¾çReadInConfig()å½æ°è¯»åé ç½®æ件
读åé ç½®æ件
// éè¿.å·æ¥åºåä¸åå±çº§ï¼æ¥è·åé
ç½®å¼
log.Printf("app.mode=%s\n", v.Get("app.mode"))
log.Printf("db.mysql.url=%s\n", v.Get("db.mysql.url"))
log.Printf("db.redis.host=%s\n", v.GetString("db.redis.host"))
log.Printf("db.redis.port=%d\n", v.GetInt("db.redis.port"))
// 使ç¨Subè·ååé
ç½®ï¼ç¶åè·åé
ç½®å¼
v2 := v.Sub("db")
log.Printf("db.mysql.url:%s\n", v2.Sub("mysql").GetString("url"))
log.Printf("db.redis.host:%s\n", v2.Sub("redis").GetString("host"))
log.Printf("db.redis.port:%s\n", v2.Sub("redis").GetInt("port"))
viperè¿æä¾äºå¦ä¸è·åç±»åè·åé 置项å¼ï¼
æ³¨ï¼ å ¶ä¸éè¦çä¸ä¸ªå½æ°IsSetå¯ä»¥ç¨æ¥å¤ææ个keyæ¯å¦è¢«è®¾ç½®
â2.5 æ¯æåªäºæä»¶æ ¼å¼
æ们ä¸ç´å¨è¯´ï¼viperæ¯æå¤ç§ä¸åæ ¼å¼çé ç½®æ件ï¼å°åºæ¯åªäºæ ¼å¼å¢ï¼å¦ä¸ï¼
- json
- toml
- yaml
- yml
- properties
- props
- prop
- hcl
- tfvars
- dotenv
- env
- ini
â3ãåé ç½®æ件
é¤äºè¯»åé ç½®æ件å¤ï¼viperä¹æ¯æå°é ç½®å¼åå ¥é ç½®æ件ï¼viperæä¾äºå个å½æ°ï¼ç¨äºå°é ç½®ååæ件ãå个å½æ°å¦ä¸ï¼
- WriteConfig
WriteConfigå½æ°ä¼å°é ç½®åå ¥é¢å 设置好路å¾çé ç½®æ件ä¸ï¼å¦æé ç½®æ件åå¨ï¼åè¦çï¼å¦æ没æï¼åå建ã
- SafeWriteConfig
SafeWriterConfigä¸WriteConfigå½æ°å¯ä¸çä¸åæ¯å¦æé ç½®æ件åå¨ï¼åä¼è¿åä¸ä¸ªé误ã
- WriteConfigAs
WriteConfigAsä¸WriteConfigå½æ°çä¸åæ¯éè¦ä¼ å ¥é ç½®æ件ä¿åè·¯å¾ï¼viperä¼æ ¹æ®æ件åç¼å¤æåå ¥æ ¼å¼ã
- SafeWriteConfigAs
SafeWriteConfigAsä¸WriteConfigAsçå¯ä¸ä¸åæ¯å¦æé ç½®æ件åå¨ï¼åè¿åä¸ä¸ªé误ã
使ç¨SafeWriteConfig()åWriteConfig()æ¶ï¼å¯ä»¥å 设å®SetConfigFile()设å®é ç½®æ件çè·¯å¾ã
ä¸é¢æ¯ä¸ä¸ªé ç½®åå ¥ç示ä¾ï¼
v := New1()
v.SetConfigFile("./hello.yml")
log.Printf("config path:%+v\n", v.ConfigFileUsed())
v.SetDefault("author","CKeen")
v.SetDefault("email", "[email protected]")
v.Set("hello", "foo")
v.Set("slice", []string {"slice1","slice2","slice3"})
v.SetDefault("test.web", "https://ckeen.cn")
v.WriteConfig()
//v.WriteConfigAs("./hello.yml")
å¦æ使ç¨SafeWriteConfigAs()æè WriteConfigAs()æ¹æ³ï¼åç´æ¥ä¼ å ¥é ç½®æ件路å¾å³å¯ã
â4ãä»ç¯å¢åé读å
viperæ¯æç¯å¢åéçå½æ°ï¼
func (v *Viper) AutomaticEnv() // å¼å¯ç»å®ç¯å¢åé
func (v *Viper) BindEnv(input ...string) error // ç»å®ç³»ç»ä¸æ个ç¯å¢åé
func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer)
func (v *Viper) SetEnvPrefix(in string)
è¦è®©viper读åç¯å¢åéï¼æ两ç§æ¹å¼ï¼
- æ¹å¼ä¸ï¼è°ç¨AutomaticEnvå½æ°ï¼å¼å¯ç¯å¢åé读å
fmt.Println(viper.Get("path"))
//å¼å§è¯»åç¯å¢åéï¼å¦æ没æè°ç¨è¿ä¸ªå½æ°ï¼åä¸é¢æ æ³è¯»åå°pathçå¼
viper.AutomaticEnv()
//ä¼ä»ç¯å¢åé读åå°è¯¥å¼ï¼æ³¨æä¸ç¨åºå大å°å
fmt.Println(viper.Get("path"))
- æ¹å¼äºï¼ä½¿ç¨BindEnvç»å®æ个ç¯å¢åé
//å°pç»å®å°ç¯å¢åéPATH,注æè¿é第äºä¸ªåæ°æ¯ç¯å¢åéï¼è¿éæ¯åºå大å°åç
viper.BindEnv("p", "PATH")
//é误ç»å®æ¹å¼ï¼path为å°åï¼æ æ³è¯»åå°PATHçå¼
//viper.BindEnv("p","path")
fmt.Println(viper.Get("p"))//éè¿på¯ä»¥è¯»åPATHçå¼
使ç¨å½æ°SetEnvPrefixå¯ä»¥ä¸ºææç¯å¢åé设置ä¸ä¸ªåç¼ï¼è¿ä¸ªåç¼ä¼å½±åAutomaticEnvåBindEnvå½æ°
os.Setenv("TEST_PATH","test")
viper.SetEnvPrefix("test")
viper.AutomaticEnv()
//æ æ³è¯»åpathçå¼ï¼å 为æ¤æ¶å ä¸åç¼ï¼viperä¼å»è¯»åTEST_PATHè¿ä¸ªç¯å¢åéçå¼
fmt.Println(viper.Get("path"))//è¾åº:nil
fmt.Println(viper.Get("test_path"))//è¾åºï¼test
ç¯å¢åé大å¤æ¯ä½¿ç¨ä¸åå·(_)ä½ä¸ºåé符çï¼å¦ææ³æ¿æ¢ï¼å¯ä»¥ä½¿ç¨SetEnvKeyReplacerå½æ°ï¼å¦ï¼
//设置ä¸ä¸ªç¯å¢åé
os.Setenv("USER_NAME", "test")
//å°ä¸çº¿å·æ¿æ¢ä¸º-å.
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_", ".", "_"))
//读åç¯å¢åé
viper.AutomaticEnv()
fmt.Println(viper.Get("user.name"))//éè¿.访é®
fmt.Println(viper.Get("user-name"))//éè¿-访é®
fmt.Println(viper.Get("user_name"))//åæ¥çä¸å线ä¹å¯ä»¥è®¿é®
é»è®¤çæ åµä¸ï¼å¦æ读åå°çç¯å¢åéå¼ä¸ºç©º(注æï¼ä¸æ¯ç¯å¢åéä¸åå¨ï¼èæ¯å ¶å¼ä¸ºç©º)ï¼ä¼ç»§ç»åä¼å级æ´ä½æ°æ®æºå»æ¥æ¾é ç½®ï¼å¦ææ³é»æ¢è¿ä¸è¡ä¸ºï¼è®©ç©ºçç¯å¢åéå¼ææï¼åå¯ä»¥è°ç¨AllowEmptyEnvå½æ°ï¼
viper.SetDefault("username", "admin")
viper.SetDefault("password", "123456")
//é»è®¤æ¯AllowEmptyEnv(false)ï¼è¿é设置为true
viper.AllowEmptyEnv(true)
viper.BindEnv("username")
os.Setenv("USERNAME", "")
fmt.Println(viper.Get("username"))//è¾åºä¸ºç©ºï¼å 为ç¯å¢åéUSERNAME空
fmt.Println(viper.Get("password"))//è¾åºï¼123456
â5ãä»å½ä»¤è¡å·¥å ·çé项åæ°Flags读å
viper主è¦æä¾äºä»¥ä¸å个æ¹æ³ï¼å¯ä»¥ç»å®è¡åæ°çè¾åºçé项å¼ï¼
func (v *Viper) BindFlagValue(key string, flag FlagValue) error
func (v *Viper) BindFlagValues(flags FlagValueSet) (err error)
func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error
func (v *Viper) BindPFlags(flags *pflag.FlagSet) error
viperå¯ä»¥å解æå½ä»¤è¡åºç¸å ³flagåºä¸èµ·å·¥ä½ï¼ä»å½ä»¤è¡è¯»åé ç½®ï¼ä¸¤ç§æ¹å¼ï¼
- viperå ç½®äºå¯¹pflagåºçæ¯æ
- viperçææ¥å£è®©æ们å¯ä»¥æ¯ææ©å±å ¶ä»çflagåº
âviper + pflag ç»å使ç¨
pflag æ¯ä¸ä¸ª Go è¯è¨çå½ä»¤è¡åæ°è§£æåºï¼å®å¯ä»¥æ¹ä¾¿å°è§£æå½ä»¤è¡åæ°å¹¶å°å ¶è½¬æ¢ä¸º Go è¯è¨ä¸çåéã
ç¸æ¯æ ååºä¸ç flag å ï¼pflag å æä¾äºæ´å¤çåè½åé项ï¼ä¾å¦æ¯æçé项ãé¿é项ãé»è®¤å¼ãå¿ éåæ°ãå¯éåæ°çã
pflag å ç使ç¨æ¹æ³å flag å 类似ï¼ä½æ´å çµæ´»åæ¹ä¾¿ã
ä½ å¯ä»¥ä½¿ç¨ go get å½ä»¤æ¥å®è£ pflag å ï¼
go get github.com/spf13/pflag
viper + pflag ç»å使ç¨çæ¡ä¾å¦ä¸ï¼
pflag.Int("port", 8080, "server http port")
pflag.Parse()
viper.BindPFlags(pflag.CommandLine)
fmt.Println(viper.GetInt("port"))//è¾åº8080
âGo è¯è¨æ ååºä¸ç flag å
å¦ææ们没æ使ç¨pflagåºï¼ä½åæ³è®©viper帮æ们读åå½ä»¤è¡åæ°å¢ï¼
Go è¯è¨æ ååºä¸ç flag å æä¾äºå½ä»¤è¡åæ°è§£æçåè½ã
flag å å¯ä»¥æ¹ä¾¿å°è§£æå½ä»¤è¡åæ°å¹¶å°å ¶è½¬æ¢ä¸º Go è¯è¨ä¸çåéã
flag å æ¯æçé项åé¿é项ï¼è¿å¯ä»¥è®¾ç½®é»è®¤å¼å解æå¿ éåæ°ã
ä½¿ç¨ flag å é常ç®åï¼ä½ åªéè¦å¨ç¨åºä¸å®ä¹éè¦è§£æçåæ°ï¼ç¶åè°ç¨ flag.Parse() å½æ°è¿è¡è§£æå³å¯ã
以ä¸æ¯ä¸ä¸ªç¤ºä¾ï¼
package main
import (
"flag"
"fmt"
)
func main() {
var name string
var age int
flag.StringVar(&name, "name", "world", "a string")
flag.IntVar(&age, "age", 18, "an int")
flag.Parse()
fmt.Printf("Hello, %s! You are %d years old.\n", name, age)
}
å¨ä¸é¢çä¾åä¸ï¼æ们å®ä¹äºä¸¤ä¸ªéè¦è§£æçåæ° name å ageï¼åå«æ¯å符串类ååæ´æ°ç±»åã
ä½¿ç¨ flag.StringVar() å flag.IntVar() å½æ°å°åæ°ä¸åéç»å®ï¼å¹¶è®¾ç½®é»è®¤å¼å说æä¿¡æ¯ã
æåè°ç¨ flag.Parse() å½æ°è¿è¡è§£æï¼å³å¯è·åå½ä»¤è¡åæ°å¹¶è¾åºç»æã
å¦ä½æµè¯å¢ï¼
æµè¯çæ¹å¼ä¹ä¸ï¼ éè¿å½ä»¤è¡
go run main.go -name =å¼ ä¸ -age=30
æµè¯çæ¹å¼ä¹ä¸ï¼ éè¿goland
æ§è¡ç»æï¼
âflagå ç»å®è§£æåæ°æä¸ç§å®ä¹æ¹å¼
âæ¹å¼ä¸ï¼flag.xxx()
ä¾å¦ï¼flag.Int, flag.String, è¿å解æåéç±»åçæé
package main
import (
"flag"
"fmt"
)
func main() {
host := flag.String("host", "127.0.0.1", "请è¾å
¥hostå°å")
port := flag.Int("port", 3306, "请è¾å
¥ç«¯å£å·")
flag.Parse() // 解æåæ°
fmt.Printf("%s:%d\n", *host, *port)
}
æ§è¡: go run main.go -host=127.0.0.1 -port=3306
è¾åºï¼127.0.0.1:3306
å½ç¶ä½ ä¹å¯ä»¥ç´æ¥æ§è¡go run main.go, è¿æ¶åå°±ä¼ä½¿ç¨ä½ çé»è®¤å¼
âæ¹å¼äºï¼ flag.XxxVar()
å¨æ¹å¼ä¸åé¢å¤äºä¸ªVar
ä¾å¦ï¼flag.IntVar, flag.StringVar
package main
import (
"flag"
"fmt"
)
var host string
var port int
func init() { // æ¯ä¸ªæ件ä¼èªå¨æ§è¡çå½æ°
flag.StringVar(&host, "host", "127.0.0.1", "请è¾å
¥hostå°å")
flag.IntVar(&port, "port", 3306, "请è¾å
¥ç«¯å£å·")
}
func main() {
flag.Parse() // 解æåæ°
fmt.Printf("%s:%d\n", host, port)
}
æ§è¡ç»æåä¸
3.èªå®ä¹åæ°è§£æflag.Var(),
æ们å¯ä»¥çä¸flag.goæºç :
func Var(value Value, name string, usage string) {
CommandLine.Var(value, name, usage)
}
使ç¨flag.Varå½æ°ç¬¬ä¸ä¸ªåæ°æ们éè¦ä¼ å ¥ä¸ä¸ªValueç±»åçå¼ï¼Valueæ¯ä¸ä¸ªæ¥å£ç±»åï¼å®ä¹äºä¸¤ä¸ªæ¹æ³ï¼
type Value interface {
String() string
Set(string) error //Setæ¥å£å³å®äºå¦ä½è§£æflagçå¼
}
æ¥ä¸æ¥æ们å»å®ç°è¿ä¸¤ä¸ªæ¹æ³ï¼
package main
import (
"flag"
"fmt"
"strings"
)
// èªå®ä¹ç±»å
type HandsomeBoys []string
// å®ç°String()æ¹æ³
func (h *HandsomeBoys) String() string {
return fmt.Sprintf("%v", *h)
}
// å®ç°Setæ¹æ³, Setæ¥å£å³å®äºå¦ä½è§£æflagçå¼
func (h *HandsomeBoys) Set(s string) error {
for _, v := range strings.Split(s, ",") {
*h = append(*h, v)
}
return nil
}
// å®ä¹ä¸ä¸ªHandsomeBoysç±»åçåé
var boys HandsomeBoys
func init() {
// ç»å®åéboys
flag.Var(&boys, "boys", "请è¾å
¥ä¸ç»å¸
æ°çç·å©å称ï¼-boys=å½äºæ,å´å½¦ç¥")
}
func main() {
flag.Parse()
fmt.Println(boys)
}
è¿è¡ä»£ç ï¼ go run main.go -boys=å½äºæ,å´å½¦ç¥
âviper + flag ç»å使ç¨
flag å viper é½æ¯ Go è¯è¨ä¸å¸¸ç¨çå½ä»¤è¡åæ°è§£æåºï¼å®ä»¬å¯ä»¥æ¹ä¾¿å°è§£æå½ä»¤è¡åæ°å¹¶å°å ¶è½¬æ¢ä¸º Go è¯è¨ä¸çåéãé常æ åµä¸ï¼æ们å¯ä»¥ä½¿ç¨ flag å æ¥è§£æå½ä»¤è¡åæ°ï¼èä½¿ç¨ viper å æ¥è§£æé ç½®æ件ãå¨å®é å¼åä¸ï¼æ们å¯è½ä¼åæ¶ä½¿ç¨å½ä»¤è¡åæ°åé ç½®æ件æ¥é ç½®ç¨åºï¼è¿æ¶å°±å¯ä»¥å° flag å viper ç»åèµ·æ¥ä½¿ç¨ã
ä¸é¢æ¯ä¸ä¸ªç¤ºä¾ç¨åºï¼æ¼ç¤ºäºå¦ä½ä½¿ç¨ flag å viper ç»å使ç¨ï¼
package main
import (
"flag"
"fmt"
"github.com/spf13/viper"
)
func main() {
// å®ä¹å½ä»¤è¡åæ°
var name string
var age int
flag.StringVar(&name, "name", "", "a string")
flag.IntVar(&age, "age", 0, "an int")
// 解æå½ä»¤è¡åæ°
flag.Parse()
// å è½½é
ç½®æ件
viper.SetConfigName("config") // é
ç½®æ件å
viper.AddConfigPath(".") // é
ç½®æ件路å¾
err := viper.ReadInConfig()
if err != nil {
fmt.Println("Error reading config file:", err)
}
// 读åé
ç½®æ件ä¸çåæ°
viper.SetDefault("name", "world")
viper.SetDefault("age", 18)
name = viper.GetString("name")
age = viper.GetInt("age")
// è¾åºç»æ
fmt.Printf("Hello, %s! You are %d years old.\n", name, age)
}
å¨ä¸é¢çä¾åä¸ï¼æ们é¦å å®ä¹äºå½ä»¤è¡åæ° name å ageï¼å¹¶ä½¿ç¨ flag å 解æå½ä»¤è¡åæ°ã
ç¶åä½¿ç¨ viper å å è½½é ç½®æ件ï¼å¹¶è¯»åé ç½®æ件ä¸çåæ°ã
注æï¼æä»¬ä½¿ç¨ viper.SetDefault() å½æ°è®¾ç½®äºé»è®¤å¼ï¼ä»¥é²é ç½®æ件ä¸æ²¡æå®ä¹ç¸åºçåæ°ã
æåè¾åºç»æãè¿æ ·ï¼æ们就å¯ä»¥éè¿å½ä»¤è¡åæ°åé ç½®æ件æ¥é ç½®ç¨åºäºã
â6ãä»è¿ç¨key/valueåå¨è¯»å
å¨Viperä¸å¯ç¨è¿ç¨æ¯æï¼éè¦å¨ä»£ç ä¸å¿åå¯¼å ¥viper/remoteè¿ä¸ªå ã
_ "github.com/spf13/viper/remote"
Viperå°è¯»åä»Key/Valueåå¨ä¸çè·¯å¾æ£ç´¢å°çé ç½®å符串ï¼å¦JSONãTOMLãYAMLæ ¼å¼ï¼ã
viperç®åæ¯æConsul/Etcd/firestoreä¸ç§Key/Valueçåå¨ç³»ç»ã
ä¸é¢ææ¥æ¼ç¤ºä»etcd读åé ç½®ï¼
- é¦å æ们å®è£ cryptçå·¥å ·
go get github.com/bketelsen/crypt/bin/crypt
- 使ç¨cryptçå½ä»¤ï¼å°app.ymlçæ件添å å°etcd
crypt set --endpoint=http://127.0.0.1:2379 -plaintext /config/app.yml /Users/ckeen/viper/configs/app.yml
ä¸é¢çå½ä»¤ï¼è®¾ç½®æ¬å°çæ件ï¼å°etcd
crypt set --endpoint=etcd clusterå°å -plaintext etcdè·¯å¾ æ¬å°æ件
- æ·»å viperçæä½è¿ç¨èµæºçé ç½®
_ "github.com/spf13/viper/remote"
- å®ç°ä»è¿ç¨è¯»åé ç½®
func InitConfigFromRemote() (*viper.Viper,error) {
v := viper.New()
// è¿ç¨é
ç½®
v.AddRemoteProvider("etcd","http://127.0.0.1:2379","config/app.yml")
//v.SetConfigType("json")
v.SetConfigFile("app.yml")
v.SetConfigType("yml")
if err := v.ReadRemoteConfig(); err == nil {
log.Printf("use config file -> %s\n", v.ConfigFileUsed())
} else {
return nil, err
}
return v, nil
}
func main(){
v, err := InitConfigFromRemote()
if err != nil {
log.Printf("read remote error:%+v\n")
}
log.Printf("remote read app.mode=%+v\n", v.GetString("app.mode"))
log.Printf("remote read db.mysql.url=%+v\n", v.GetString("db.mysql.url"))
}
- æµè¯æå°ç»æ
â7ãçå¬é ç½®åå
viperæä¾å¦ä¸ä¸¤ç§çå¬é ç½®çå½æ°ï¼ä¸ä¸ªæ¯æ¬å°ççå¬åä¸ä¸ªè¿ç¨çå¬çï¼
func (v *Viper) WatchConfig()
func (v *Viper) WatchRemoteConfig() error
func (v *Viper) WatchRemoteConfigOnChannel() error
â7.1 çå¬æ¬å°é ç½®æ件åå
æ们主è¦çä¸ä¸çå¬æ¬å°æ件åæ´ç示ä¾
v, err := InitConfig()
if err != nil {
log.Fatalf("viper读å失败, error:%+v\n",err)
}
// çå¬å°æ件åååçåè°
v.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed:", e.Name)
fmt.Println(v.Get("db.redis.passwd"))
})
v.WatchConfig()
// é»å¡è¿ç¨éåº
time.Sleep(time.Duration(1000000) * time.Second)
æ们使ç¨åé¢çInitConfig()æ¹æ³æ¥åå§åæ¬å°æ件读åé ç½®ï¼ç¶å设å®äºçå¬å½æ°ï¼æå使ç¨WatchConfig()å¼å¯æ¬å°æ件çå¬ã
å½æ们修æ¹æ¬å°é ç½®configs/app.ymlçdb.redis.passwdçå¼ï¼ç¶åä¿ååï¼æ们å¯ä»¥çå°æ§å¶å°ææå°ææ°ä¿®æ¹åçå¼ï¼ä¸è¦æ们éæ°å»è·åã
â7.2 çå¬è¿ç¨é ç½®åå
é¤äºè½å¤çå¬æ¬å°çé ç½®ååï¼viper æ¯æçå¬è¿ç¨é ç½®ååã
Viper æä¾äºä¸ä¸ª RemoteConfig æ¹æ³ï¼å¯ä»¥ç¨æ¥å è½½è¿ç¨é ç½®æ件ï¼ä¾å¦ä»ä¸ä¸ª HTTP æè ETCD çè¿ç¨é ç½®ä¸å¿å è½½é ç½®æ件ã
å¨å è½½è¿ç¨é ç½®æ件ä¹åï¼ä½ å¯ä»¥ä½¿ç¨ WatchRemoteConfig æ¹æ³æ¥çè§é ç½®æ件çååï¼å½è¿ç¨é ç½®æ件åçååæ¶ï¼Viper ä¼èªå¨éæ°å è½½é ç½®æ件ï¼å¹¶è§¦åç¸åºçåè°å½æ°ã
ä¸é¢æ¯ä¸ä¸ªç¤ºä¾ä»£ç ï¼
viper.AddRemoteProvider("etcd", "http://localhost:2379", "/config/app.yaml")
viper.SetConfigType("yaml")
err := viper.ReadRemoteConfig()
if err != nil {
// å¤çé误
}
viper.WatchRemoteConfigOnChannel()
å¨è¿ä¸ªç¤ºä¾ä»£ç ä¸ï¼æ们使ç¨äº AddRemoteProvider æ¹æ³æ¥æ·»å ä¸ä¸ª ETCD è¿ç¨é ç½®ä¸å¿ä½ä¸º Viper çé ç½®æºï¼å¹¶æå®äºè¦å è½½çé ç½®æ件路å¾ã
ç¶åä½¿ç¨ ReadRemoteConfig æ¹æ³æ¥å è½½è¿ç¨é ç½®æ件ã
æåä½¿ç¨ WatchRemoteConfigOnChannel æ¹æ³æ¥çè§é ç½®æ件çååï¼å½é ç½®æ件åçååæ¶ï¼Viper ä¼èªå¨éæ°å è½½é ç½®æ件ï¼å¹¶è§¦åç¸åºçåè°å½æ°ã
âéè¿ Nacoså®ç°ç»ä¸é 置管çåè½
âSpringCloud åå¸å¼é ç½®ä¸å¿éå
ç®åæ主æµçåå¸å¼é ç½®ä¸å¿ä¸»è¦æ
- spring cloud config
- apollo
- nacos
etcd åzookeeper ï¼ ä¹ä½ä¸ºé ç½®ä¸å¿ä½¿ç¨ï¼ä½æ¯æ²¡æ nacos æ®éã
ä¸è¬èè¨ï¼å¨spring cloudå±äºspringä½ç³», å°±èèapollo(æºç¨)ånacos(é¿é),é½æ¯ç®åæ¯è¾æµè¡ä¸ç»´æ¤æ´»è·ç2个é ç½®ä¸å¿;
a . apollo大èå ¨, åè½å®å, nacoså°èå ¨;
b . é¨ç½²nacosæ´å ç®å;
c .nacosä¸æ¢æ¯æé ç½®ä¸å¿ï¼è¿æ¯ææå¡æ³¨åååç°;
d . é½æ¯æåç§è¯è¨, ä¸è¿apolloæ¯ç¬¬ä¸æ¹æ¯æç,nacosæ¯å®æ¹æ¯æåç§è¯è¨;
nacoså¾æ´»è·, ä¸è¿ççåºæ¥nacosæ³è¦æ建ççæéå¿æ´å¤§, ä¸è¿æ¶è´¹æå¾ææ¾;
å¦å¤ï¼nacos å±äº SpringCloud alibaba æ¡æ¶çæ ¸å¿ç»ä»¶ã
âç¨ java+go å¤è¯è¨ äºåçå¾®æå¡æ¶æä¸é ç½®ä¸å¿ç ä½ç½®
ä¸ä¸ªæå°¼æ©æ导 ä¸ä¸ª6å¹´å°ä¼ä¼´ç®åï¼ä½¿ç¨java+go å¤è¯è¨ äºåçå¾®æå¡æ¶æï¼å¸®å© 6å¹´å°ä¼ï¼æ¶60Wå¹´èª
主è¦çæ¶æå¾å¾å¦ä¸ï¼
å¨è¿ä¸ªæ¶æä¸ï¼ ç¨java+go å¤è¯è¨ äºåçå¾®æå¡æ¶æä¸çåå¸å¼ é ç½®ä¸å¿ ï¼æ¯nacos
ègo ä¸çviperæ¡æ¶ï¼æ²¡æ对 nacos æä¾æ¯æ æ以ï¼å±ä»¬èªå·±å®ç°ï¼å¯¹ nacos çæ¯æ
âNacosä½ä¸ºé ç½®ä¸å¿ä½¿ç¨ï¼
â为ä»ä¹ä½¿ç¨Nacos
âå 为ç°å¨ä¸»æµå ¬å¸å·²ç»å¼å§æ¨è使ç¨nacosäºï¼nacosç两大主è¦ä½ç¨ï¼
- ä¸ä¸ªæ¯æå¡æ³¨ååç°ï¼
- ä¸ä¸ªæ¯é 置管çã
âNacosæ¯ä»ä¹ï¼
Nacosæ¯ä¸ä¸ªæ´æäºæ建äºåçåºç¨çå¨ææå¡åç°ãé 置管çåæå¡ç®¡çå¹³å°ã
å®æä¾äºä¸ç»ç®åæç¨çç¹æ§éï¼å¸®å©æ¨å¿«éå®ç°å¨ææå¡åç°ãæå¡é ç½®ãæå¡å æ°æ®åæµé管ç ã
âNacosçç»æåç»ä»¶
Nacosçç»æåç»ä»¶å æ¬ï¼
- æå¡ç®¡çï¼å®ç°æå¡CRUD,ååCRUD,æå¡å¥åº·ç¶ææ£æ¥ï¼æå¡æé管ççåè½ã
- é 置管çï¼å®ç°é 置管CRUD,çæ¬ç®¡çï¼ç°åº¦ç®¡çï¼çå¬ç®¡çï¼æ¨é轨迹ï¼èåæ°æ®çåè½ã
- å æ°æ®ç®¡çï¼æä¾å æ°æ®CURD åææ è½åã
- æ件æºå¶ï¼å®ç°ä¸ä¸ªæ¨¡åå¯åå¯åè½åï¼å®ç°æ©å±ç¹SPIæºå¶ã
- äºä»¶æºå¶ï¼å®ç°å¼æ¥åäºä»¶éç¥ï¼sdkæ°æ®ååå¼æ¥éç¥çé»è¾ã
- æ¥å¿æ¨¡åï¼ç®¡çæ¥å¿åç±»ï¼æ¥å¿çº§å«ï¼æ¥å¿å¯ç§»æ¤æ§(å°¤å ¶é¿å å²çª),æ¥å¿æ ¼å¼ï¼å¼å¸¸ç +帮å©ææ¡£ã
- åè°æºå¶ï¼sdkéç¥æ°æ®ï¼éè¿ç»ä¸ç模å¼åè°ç¨æ·å¤çã
- æå¡ç®¡çï¼å®ç°æå¡CRUDï¼ååCRUDï¼æå¡å¥åº·ç¶ææ£æ¥ï¼æå¡æé管ççåè½
- é 置管çï¼å®ç°é 置管CRUDï¼çæ¬ç®¡çï¼ç°åº¦ç®¡çï¼çå¬ç®¡çï¼æ¨é轨迹ï¼èåæ°æ®çåè½
- å æ°æ®ç®¡çï¼æä¾å æ°æ®CURD åææ è½å
- æ件æºå¶ï¼å®ç°ä¸ä¸ªæ¨¡åå¯åå¯åè½åï¼å®ç°æ©å±ç¹SPIæºå¶
- äºä»¶æºå¶ï¼å®ç°å¼æ¥åäºä»¶éç¥ï¼sdkæ°æ®ååå¼æ¥éç¥çé»è¾
- æ¥å¿æ¨¡åï¼ç®¡çæ¥å¿åç±»ï¼æ¥å¿çº§å«ï¼æ¥å¿å¯ç§»æ¤æ§ï¼å°¤å ¶é¿å å²çªï¼ï¼æ¥å¿æ ¼å¼ï¼å¼å¸¸ç +帮å©ææ¡£
- åè°æºå¶ï¼sdkéç¥æ°æ®ï¼éè¿ç»ä¸ç模å¼åè°ç¨æ·å¤çãæ¥å£åæ°æ®ç»æéè¦å ·å¤å¯æ©å±æ§
- 寻å模å¼ï¼è§£å³ipï¼ååï¼nameserverã广æçå¤ç§å¯»å模å¼ï¼éè¦å¯æ©å±
- æ¨éééï¼è§£å³serverä¸åå¨ãserveré´ãserverä¸sdké´æ¨éæ§è½é®é¢
- 容é管çï¼ç®¡çæ¯ä¸ªç§æ·ï¼åç»ä¸ç容éï¼é²æ¢åå¨è¢«åçï¼å½±åæå¡å¯ç¨æ§
- æµé管çï¼æç §ç§æ·ï¼åç»çå¤ä¸ªç»´åº¦å¯¹è¯·æ±é¢çï¼é¿é¾æ¥ä¸ªæ°ï¼æ¥æ大å°ï¼è¯·æ±æµæ§è¿è¡æ§å¶
- ç¼åæºå¶ï¼å®¹ç¾ç®å½ï¼æ¬å°ç¼åï¼serverç¼åæºå¶ã容ç¾ç®å½ä½¿ç¨éè¦å·¥å ·
- å¯å¨æ¨¡å¼ï¼æç §åæºæ¨¡å¼ï¼é 置模å¼ï¼æå¡æ¨¡å¼ï¼dns模å¼ï¼æè all模å¼ï¼å¯å¨ä¸åçç¨åº+UI
- ä¸è´æ§åè®®ï¼è§£å³ä¸åæ°æ®ï¼ä¸åä¸è´æ§è¦æ±æ åµä¸ï¼ä¸åä¸è´æ§æºå¶
- åå¨æ¨¡åï¼è§£å³æ°æ®æä¹ åãéæä¹ ååå¨ï¼è§£å³æ°æ®åçé®é¢
- Nameserverï¼è§£å³namespaceå°clusteridçè·¯ç±é®é¢ï¼è§£å³ç¨æ·ç¯å¢ä¸nacosç©çç¯å¢æ å°é®é¢
- CMDBï¼è§£å³å æ°æ®åå¨ï¼ä¸ä¸æ¹cmdbç³»ç»å¯¹æ¥é®é¢ï¼è§£å³åºç¨ï¼äººï¼èµæºå ³ç³»
- Metricsï¼æ´é²æ åmetricsæ°æ®ï¼æ¹ä¾¿ä¸ä¸æ¹çæ§ç³»ç»æé
- Traceï¼æ´é²æ åtraceï¼æ¹ä¾¿ä¸SLAç³»ç»æéï¼æ¥å¿ç½å¹³åï¼æ¨é轨迹çè½åï¼å¹¶ä¸å¯ä»¥å计é计费系ç»æé
- æ¥å ¥ç®¡çï¼ç¸å½äºé¿éäºå¼éæå¡ï¼åé 身份ã容éãæéè¿ç¨
- ç¨æ·ç®¡çï¼è§£å³ç¨æ·ç®¡çï¼ç»å½ï¼ssoçé®é¢
- æé管çï¼è§£å³èº«ä»½è¯å«ï¼è®¿é®æ§å¶ï¼è§è²ç®¡ççé®é¢
- 审计系ç»ï¼æ©å±æ¥å£æ¹ä¾¿ä¸ä¸åå ¬å¸å®¡è®¡ç³»ç»æé
- éç¥ç³»ç»ï¼æ ¸å¿æ°æ®åæ´ï¼æè æä½ï¼æ¹ä¾¿éè¿SMSç³»ç»æéï¼éç¥å°å¯¹åºäººæ°æ®åæ´
- OpenAPIï¼æ´é²æ åResté£æ ¼HTTPæ¥å£ï¼ç®åæç¨ï¼æ¹ä¾¿å¤è¯è¨éæ
- Consoleï¼æç¨æ§å¶å°ï¼åæå¡ç®¡çãé 置管ççæä½
- SDKï¼å¤è¯è¨sdk
- Agentï¼dns-f类似模å¼ï¼æè ä¸meshçæ¹æ¡éæ
- CLIï¼å½ä»¤è¡å¯¹äº§åè¿è¡è½»éå管çï¼ågitä¸æ ·å¥½ç¨
âNacosçå®è£ å使ç¨
å®è£ åé¨ç½²ç¹å«ç®åï¼æ¯æåæºé¨ç½²åclusteré¨ç½²ï¼è¯¦æ å¯åènacosçå®ç½ã
é¨ç½²å®æä¹åï¼ç»å½è¿å ¥
å¯ä»¥çå°é 置管çéè¾¹çé ç½®å表ï¼é¡µé¢é¿è¿ä¸ªæ ·åï¼
å¯ä»¥çå°é 置管çéè¾¹çé ç½®å表çä¸ä¸ªè¯¦æ ï¼é¡µé¢é¿è¿ä¸ªæ ·åï¼
âé ç½®æ°æ®çä¸ä¸ªæ ¸å¿æ¦å¿µï¼NamespaceãGroupãData ID
âå½å空é´(Namespace) ï¼ç¯å¢é离
Namespace ç常ç¨åºæ¯ä¹ä¸æ¯ä¸åç¯å¢çé ç½®é离ãä¾å¦
- å¼åç¯å¢
- æµè¯ç¯å¢
- ç产ç¯å¢çèµæºï¼å¦é ç½®ãæå¡ï¼é离çã
ä¸åçå½å空é´ä¸ï¼å¯ä»¥åå¨ç¸åç Group æ Data ID çé ç½®ã
ä¸åçå½å空é´ä¸ï¼å¯ä»¥åå¨ç¸åå称çé ç½®åç»(Group)æé ç½®éã
å½ç¶ï¼è¿ä¸ªä¸æ¯ç»å¯¹çãã
ä¹å¯ä»¥ç¨æ¥è¿è¡ç§æ·é离ã
âé ç½®åç»ï¼Groupï¼ï¼é¡¹ç®é离
é ç½®åç»ç常è§åºæ¯ï¼ä¸åçåºç¨æç»ä»¶ä½¿ç¨äºç¸åçé 置类åï¼å¦ database_url é ç½®å MQ_topic é ç½®ã
é ç½®åç»å°±æ¯å¯¹é ç½®éè¿è¡åç»ãNacosä¸çä¸ç»é ç½®éï¼æ¯ç»ç»é ç½®ç维度ä¹ä¸ã
éè¿ä¸ä¸ªææä¹çå符串ï¼å¦ Buy æ Trade ï¼å¯¹é ç½®éè¿è¡åç»ï¼ä»èåºå Data ID ç¸åçé ç½®éã
å½æ¨å¨ Nacos ä¸å建ä¸ä¸ªé ç½®æ¶ï¼å¦ææªå¡«åé ç½®åç»çå称ï¼åé ç½®åç»çå称é»è®¤éç¨ DEFAULT_GROUPã
âé ç½®é( Data IDï¼ï¼åè½é离
ä¸ç»ç¸å ³æè ä¸ç¸å ³çé 置项çéå称为é ç½®éãData ID é常ç¨äºç»ç»ååç³»ç»çé ç½®éã
æ¯ä¸ªé ç½®éé½å¯ä»¥è¢«ä¸ä¸ªææä¹çå称æ è¯ï¼å°±æ¯é ç½®éçIDå³Data IDã
é ç½®é( Data IDï¼ç常è§åºæ¯ ï¼ åè½é离ã
å¨ç³»ç»ä¸ï¼ä¸ä¸ªé ç½®æ件é常就æ¯ä¸ä¸ªé ç½®éï¼å å«äºç³»ç»å个æ¹é¢çé ç½®ã
ä¾å¦ï¼ä¸ä¸ªé ç½®éå¯è½å å«äºæ°æ®æºã线ç¨æ± ãæ¥å¿çº§å«çé 置项ã
ä»åè½è§è§ï¼å¯ä»¥é离åºï¼ æ°æ®æºé ç½®éï¼ çº¿ç¨æ± é ç½®éï¼ççã
âé ç½®éå å«ä¸ä¸ªä¸ä¸ªçé 置项
é ç½®éä¸å å«çä¸ä¸ªä¸ªé ç½®å 容就æ¯é 置项ã
ä¸ä¸ªå ·ä½çå¯é ç½®çåæ°ä¸å ¶å¼åï¼é常以 param-key=param-value çå½¢å¼åå¨ã
ä¾å¦æ们常é 置系ç»çæ¥å¿è¾åºçº§å«ï¼logLevel=INFO|WARN|ERRORï¼ å°±æ¯ä¸ä¸ªé 置项ã
âgo nacos sdk ç使ç¨
Nacos-sdk-goæ¯NacosçGoè¯è¨å®¢æ·ç«¯sdk ï¼å®å®ç°äºæå¡åç°åå¨æé ç½®çåè½
- nacos-sdk-goå°åï¼https://github.com/nacos-group/nacos-sdk-go
- nacos-sdk-goä¸æææ¡£å°åï¼https://github.com/nacos-group/nacos-sdk-go/blob/master/README_CN.md
- go nacoséæ±åæ â .è½è·åå°é ç½®â¡.è½çå¬å°é ç½®æ件çä¿®æ¹â¢.nacoså³æ¯ä¸ä¸ªé ç½®ä¸å¿ï¼ä¹æ¯ä¸ä¸ªæ³¨åä¸å¿
Nacos-sdk-go 客æ·ç«¯ç»ä»¶çå®è£
使ç¨go getå®è£ SDKï¼
$ go get -u github.com/nacos-group/nacos-sdk-go/v2
Nacos-sdk-go 客æ·ç«¯ç»ä»¶ç使ç¨éå¶
- æ¯æGo>=v1.15çæ¬
- æ¯æNacos>2.xçæ¬
ânacos-sdk-go çå®æ¹demo
nacos-sdk-go çå®æ¹demo
第ä¸æ¥ï¼ é å nacosæå¡å¨ï¼å建 serverConfig 对象
// å建serverConfig çä¾å
serverConfigs := []constant.ServerConfig{
*constant.NewServerConfig(
"console1.nacos.io",
80,
constant.WithScheme("http"),
constant.WithContextPath("/nacos"),
),
*constant.NewServerConfig(
"console2.nacos.io",
80,
constant.WithScheme("http"),
constant.WithContextPath("/nacos"),
),
}
// å建serverConfig ç第2个ä¾å
serverConfigs := []constant.ServerConfig{
{
IpAddr: "console1.nacos.io",
ContextPath: "/nacos",
Port: 80,
Scheme: "http",
},
{
IpAddr: "console2.nacos.io",
ContextPath: "/nacos",
Port: 80,
Scheme: "http",
},
}
第äºæ¥ï¼ é å nacos 客æ·ç«¯ï¼å建 clientConfig对象
// å建clientConfig
clientConfig := constant.ClientConfig{
NamespaceId: "e525eafa-f7d7-4029-83d9-008937f9d468", // å¦æéè¦æ¯æå¤namespaceï¼æ们å¯ä»¥å建å¤ä¸ªclient,å®ä»¬æä¸åçNamespaceIdãå½namespaceæ¯publicæ¶ï¼æ¤å¤å¡«ç©ºå符串ã
TimeoutMs: 5000,
NotLoadCacheAtStart: true,
LogDir: "/tmp/nacos/log",
CacheDir: "/tmp/nacos/cache",
LogLevel: "debug",
}
// å建clientConfigçå¦ä¸ç§æ¹å¼
clientConfig := *constant.NewClientConfig(
constant.WithNamespaceId("e525eafa-f7d7-4029-83d9-008937f9d468"), //å½namespaceæ¯publicæ¶ï¼æ¤å¤å¡«ç©ºå符串ã
constant.WithTimeoutMs(5000),
constant.WithNotLoadCacheAtStart(true),
constant.WithLogDir("/tmp/nacos/log"),
constant.WithCacheDir("/tmp/nacos/cache"),
constant.WithLogLevel("debug"),
)
第ä¸æ¥ï¼å建å¨æé 置客æ·ç«¯
// å建å¨æé
置客æ·ç«¯
_, _ := clients.CreateConfigClient(map[string]interface{}{
"serverConfigs": serverConfigs,
"clientConfig": clientConfig,
})
// å建å¨æé
置客æ·ç«¯çå¦ä¸ç§æ¹å¼ (æ¨è)
configClient, err := clients.NewConfigClient(
vo.NacosClientParam{
ClientConfig: &clientConfig,
ServerConfigs: serverConfigs,
},
)
åé 置客æ·ç«¯ç±»ä¼¼ï¼æå¡åç°å®¢æ·ç«¯ä¹æ¯è¿ä¹å¹²ç
// å建æå¡åç°å®¢æ·ç«¯
_, _ := clients.CreateNamingClient(map[string]interface{}{
"serverConfigs": serverConfigs,
"clientConfig": clientConfig,
})
// å建æå¡åç°å®¢æ·ç«¯çå¦ä¸ç§æ¹å¼ (æ¨è)
namingClient, err := clients.NewNamingClient(
vo.NacosClientParam{
ClientConfig: &clientConfig,
ServerConfigs: serverConfigs,
},
)
â示ä¾ï¼go nacoså®ç°é 置读åä¸çå¬é ç½®åå
示ä¾åè½ï¼
- go nacoså®ç°é 置读å
- çå¬é ç½®åå
package main
import (
"fmt"
"github.com/nacos-group/nacos-sdk-go/v2/clients"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
"time"
)
func main() {
sc := []constant.ServerConfig{
{
IpAddr: "192.168.56.121",
Port: 8848,
},
}
// å建clientConfig
cc := constant.ClientConfig{
NamespaceId: "dubbo", // å¦æéè¦æ¯æå¤namespaceï¼æ们å¯ä»¥åºæ¯å¤ä¸ªclient,å®ä»¬æä¸åçNamespaceIdãå½namespaceæ¯publicæ¶ï¼æ¤å¤å¡«ç©ºå符串ã
TimeoutMs: 5000,
NotLoadCacheAtStart: true,
LogDir: "tmp/nacos/log", //å»ætmpåé¢ç/ï¼è¿æ ·å°±ä¼é»è®¤ä¿åå°å½å项ç®ç®å½ä¸
CacheDir: "tmp/nacos/cache",
LogLevel: "debug",
}
configClient, err := clients.CreateConfigClient(map[string]interface{}{
"serverConfigs": sc,
"clientConfig": cc,
})
if err != nil {
panic(err)
}
content, err := configClient.GetConfig(vo.ConfigParam{
DataId: "crazymaker-common-dev.yml",
Group: "DEFAULT_GROUP"})
if err != nil {
panic(err)
}
fmt.Println(content) //å符串 - yaml
//çå¬é
置修æ¹
err = configClient.ListenConfig(vo.ConfigParam{
DataId: "crazymaker-common-dev.yml",
Group: "DEFAULT_GROUP",
OnChange: func(namespace, group, dataId, data string) {
fmt.Println("é
ç½®æ件åå")
fmt.Println("group:" + group + ", dataId:" + dataId + ", data:" + data)
},
})
time.Sleep(3000 * time.Second)
}
è¿è¡ä»£ç ï¼è¾å ¥äºé ç½®æ件éè¾¹çå 容
CacheDirçä½ç¨ï¼
- é ç½®äºCacheDirçæ¶åï¼æ¬å°é¡¹ç®ä¸ä¼çæCacheDirçç®å½
- ä¸æ¦nacosä¸å¯ç¨ï¼æ æ³è®¿é®nacosï¼å¦ææ们æé ç½®CachaDirï¼é£ä¹å°±ä¼è¯»åæ¬å°é 置信æ¯
ânacos goå¨æé ç½®
ç±»ä¼¼äº æ°æ®åºç crudï¼ä»¥åååéç¥
- åå¸é ç½®ï¼PublishConfig
使ç¨nacos goå¨æåå¸é ç½®
success, err := configClient.PublishConfig(vo.ConfigParam{
DataId: "dataId",
Group: "group",
Content: "hello world!222222"})
- å é¤é ç½®ï¼DeleteConfig
使ç¨nacos goå¨æå é¤é ç½®
success, err = configClient.DeleteConfig(vo.ConfigParam{
DataId: "dataId",
Group: "group"})
- è·åé ç½®ï¼GetConfig
使ç¨nacos goå¨æå é¤é ç½®
content, err := configClient.GetConfig(vo.ConfigParam{
DataId: "dataId",
Group: "group"})
- çå¬é ç½®ååï¼ListenConfig
使ç¨nacos goå¨æçå¬é ç½®åå
err := configClient.ListenConfig(vo.ConfigParam{
DataId: "dataId",
Group: "group",
OnChange: func(namespace, group, dataId, data string) {
fmt.Println("group:" + group + ", dataId:" + dataId + ", data:" + data)
},
})
- åæ¶é ç½®çå¬ï¼CancelListenConfig
使ç¨nacos goåæ¶é ç½®çå¬
err := configClient.CancelListenConfig(vo.ConfigParam{
DataId: "dataId",
Group: "group",
})
- æç´¢é ç½®ï¼SearchConfig
使ç¨nacos goæç´¢é ç½®
configPage,err := configClient.SearchConfig(vo.SearchConfigParam{
Search: "blur",
DataId: "",
Group: "",
PageNo: 1,
PageSize: 10,
})
çå¬é ç½®ååæ¼ç¤ºï¼
ä¸é¢çæ¡ä¾éè¾¹ï¼å·²ç»æäº çå¬é ç½®åå代ç
å¨nacos æ§å¶å°ï¼ä¿®æ¹ä¸ä¸é ç½®æ件
åå¸ä¸æ³¢,
å±ä»¬ç¨åºçæ§å¶å°ï¼å°±å¯ä»¥çå° ï¼ çå¬å°çæ°æ®
è¦æ³¨æçæ¯ï¼
è¿éè¿åçæ¯æ´ä¸ªçæ°æ®éï¼èä¸ä» ä» æ¯ååäºçé£ä¸ªæ°æ®é¡¹
âãGolang å£ç»ãè¿æ 5Wåå¾ åå¸ï¼
æ¬æï¼ä» ä» æ¯ãGolang å£ç»ã ç第3é¨åããGolang å£ç»ãåé¢çå 容 æ´å 精彩ï¼æ¶åå°é«å¹¶åãåå¸å¼å¾®æå¡æ¶æã WEBå¼åæ¶æã
å¦æéè¦å ãGolang å£ç»ãï¼ è¯·å°å ¬å·ãææ¯èªç±åãã
æåï¼å¦æå¦ä¹ è¿ç¨ä¸éå°é®é¢ï¼å¯ä»¥æ¥å°¼æ©çé«å¹¶å社åºãææ¯èªç±åãã
âææ¯èªç±çå®ç°è·¯å¾ PDF è·åï¼
âå®ç°ä½ çæ¶æèªç±ï¼
- ãåé8å¾1模æ¿ï¼äººäººå¯ä»¥åæ¶æãPDF
- ã10Wqpsè¯è®ºä¸å°ï¼å¦ä½æ¶æï¼Bç«æ¯è¿ä¹åçï¼ï¼ï¼ãPDF
- ãé¿éäºé¢ï¼åä¸çº§ã亿级æ°æ®ï¼å¦ä½æ§è½ä¼åï¼ æç§ä¹¦çº§ çæ¡æ¥äºãPDF
- ãå³°å¼21WQpsã亿级DAUï¼å°æ¸¸æãç¾äºä¸ªç¾ãæ¯æä¹æ¶æçï¼ãPDF
- ã100亿级订åæä¹è°åº¦ï¼æ¥ä¸ä¸ªå¤§åçæåæ¹æ¡ãPDF
- ã2个大å 100亿级 è¶ å¤§æµé 红å æ¶ææ¹æ¡ãPDF
⦠æ´å¤æ¶ææç« ï¼æ£å¨æ·»å ä¸
âå®ç°ä½ ç ååºå¼ èªç±ï¼
- ãååºå¼å£ç»ï¼10Wåï¼å®ç°Springååºå¼ç¼ç¨èªç±ãPDF
- è¿æ¯èçæ¬ ãFluxãMonoãReactor å®æï¼å²ä¸æå ¨ï¼ãPDF
âå®ç°ä½ ç spring cloud èªç±ï¼
- ãSpring cloud Alibaba å¦ä¹ å£ç»ã PDF
- ãååºå表 Sharding-JDBC åºå±åçãæ ¸å¿å®æï¼å²ä¸æå ¨ï¼ãPDF
- ãä¸ææå®ï¼SpringBootãSLF4jãLog4jãLogbackãNettyä¹é´æ··ä¹±å ³ç³»ï¼å²ä¸æå ¨ï¼ãPDF
âå®ç°ä½ ç linux èªç±ï¼
- ãLinuxå½ä»¤å¤§å ¨ï¼2Wå¤åï¼ä¸æ¬¡å®ç°Linuxèªç±ãPDF
âå®ç°ä½ ç ç½ç» èªç±ï¼
- ãTCPå议详解 (å²ä¸æå ¨)ãPDF
- ãç½ç»ä¸å¼ 表ï¼ARP表, MAC表, è·¯ç±è¡¨ï¼å®ç°ä½ çç½ç»èªç±ï¼ï¼ãPDF
âå®ç°ä½ ç åå¸å¼é èªç±ï¼
- ãRedisåå¸å¼éï¼å¾è§£ - ç§æ - å²ä¸æå ¨ï¼ãPDF
- ãZookeeper åå¸å¼é - å¾è§£ - ç§æãPDF
âå®ç°ä½ ç çè ç»ä»¶ èªç±ï¼
- ãéåä¹çï¼ Disruptor åçãæ¶æãæºç ä¸æç©¿éãPDF
- ãç¼åä¹çï¼Caffeine æºç ãæ¶æãåçï¼å²ä¸æå ¨ï¼10Wå è¶ çº§é¿æï¼ãPDF
- ãç¼åä¹çï¼Caffeine ç使ç¨ï¼å²ä¸æå ¨ï¼ãPDF
- ãJava Agent æ¢éãåèç å¢å¼º ByteBuddyï¼å²ä¸æå ¨ï¼ãPDF
âå®ç°ä½ ç é¢è¯é¢ èªç±ï¼
4000页ãå°¼æ©Javaé¢è¯å®å ¸ãPDF 40个ä¸é¢
....
注ï¼ä»¥ä¸å°¼æ© æ¶æç¬è®°ãé¢è¯é¢ çPDFæ件ï¼è¯·å°ãææ¯èªç±åãå ¬å·è·å
è¿éè¦å¥èªç±ï¼å¯ä»¥åè¯å°¼æ©ã å°¼æ©å¸®ä½ å®ç°.......