在微服務架構中,Web 層作為系統的入口門面,承擔著請求路由、權限校驗和服務聚合等核心功能。本文將圍繞 Gin 框架與 Consul 注冊中心的集成展開,詳細講解 Web 服務如何實現服務注冊與發現,幫助你構建可擴展的微服務前端架構。
承接微服務注冊中心詳解
我在前面文章中也寫了gRPC服務層集成consul,需要的可以回去看一下。
一、Web 層服務集成 Consul 概述
1.1 Web 層在微服務中的定位
Web 層(如 Gin 服務)通常扮演以下多重角色:
- API 網關:作為系統統一入口,處理跨域請求、身份認證、流量限流等非業務邏輯,避免底層服務重復實現通用功能
- 服務聚合器:將多個底層微服務的能力組合成復合 API,減少客戶端與后端的交互次數(如同時獲取用戶信息和訂單列表)
- 請求路由樞紐:根據業務邏輯將請求轉發到對應服務,實現前端與后端服務的邏輯解耦
為什么 Web 層需要集成 Consul?
在單體架構中,服務地址通常是硬編碼的;但在微服務場景下:
- 底層服務可能部署多個實例(如用戶服務有 3 個副本),需要動態獲取可用地址
- 服務可能因擴縮容、故障轉移而變更 IP 端口,手動維護地址列表不現實
- 負載均衡需要從注冊中心獲取實時服務列表,實現流量的動態分配
1.2 服務注冊與發現的雙重需求
1.2.1 服務注冊的必要性
即使作為前端服務,Web 層仍需注冊到 Consul 的核心原因:
- 反向調用場景:后臺管理系統、數據同步服務可能需要調用 Web 層 API,需通過注冊中心獲取地址
- 負載均衡需求:Nginx 等反向代理可從 Consul 動態獲取 Web 服務地址,避免手動配置上游服務器
- 全局服務治理:統一在 Consul 中監控所有服務的健康狀態,包括 Web 層的運行情況
1.2.2 服務發現的核心作用
Web 層的服務發現機制解決了三大問題:
- 地址動態獲取:底層服務(如用戶服務)的 IP 端口變更時,Web 層無需重啟即可獲取新地址
- 多實例負載均衡:當同一服務存在多個實例時,自動選擇合適的節點調用
- 故障自動轉移:檢測到服務實例不健康時,自動跳過該實例,避免請求失敗
二、Consul 配置與服務發現實現
2.2 服務發現核心代碼解析
2.2.1 從 Consul 獲取服務地址的執行流程
您提供的代碼片段展示了從 Consul 獲取用戶服務地址并建立 gRPC 連接的完整過程。這一過程可以分解為以下關鍵步驟:
- 初始化 Consul 客戶端:從配置中讀取 Consul 服務器地址并創建客戶端
- 服務發現查詢:通過服務名稱過濾獲取目標服務實例
- 地址解析與連接:提取服務 IP 和端口,建立 gRPC 長連接
- 全局客戶端管理:將 gRPC 客戶端存儲為全局變量供后續使用
// service_discovery.go - 服務發現實現
package serviceimport ("fmt""log""github.com/hashicorp/consul/api""your-project/global""zap"
)// InitSrvConn 初始化服務連接
func InitSrvConn() {// 1. 初始化Consul客戶端配置cfg := api.DefaultConfig()consulInfo := global.ServerConfig.ConsulInfocfg.Address = fmt.Sprintf("%s:%d", consulInfo.Host, consulInfo.Port)// 2. 創建Consul客戶端client, err := api.NewClient(cfg)if err != nil {log.Fatalf("創建Consul客戶端失敗: %v", err)}// 3. 過濾查詢用戶服務data, err := client.Agent().ServicesWithFilter(fmt.Sprintf("Service==\"%s\"", global.ServerConfig.UserSrvInfo.Name))if err != nil {log.Fatalf("過濾服務失敗: %v", err)}// 4. 解析服務地址userSrvHost := ""userSrvPort := 0fmt.Println("過濾后的服務:")for _, value := range data {userSrvHost = value.AddressuserSrvPort = value.Portbreak}// 5. 驗證服務是否找到if userSrvHost == "" {zap.S().Fatal("[InitSrvConn] 連接 用戶服務失敗")return}// 6. 建立gRPC連接userconn, err := grpc.Dial(fmt.Sprintf("%s:%d", userSrvHost, userSrvPort), grpc.WithInsecure())if err != nil {zap.S().Errorw("[GetUserList] 連接 [用戶服務失敗]", "message", err.Error())}// 7. 生成gRPC客戶端并存儲為全局變量userSrvClient := proto.NewUserClient(userconn)global.UserSrvClient = userSrvClientzap.S().Infof("用戶服務連接成功: %s:%d", userSrvHost, userSrvPort)
}
2.2.2 在 Gin 中集成服務發現的完整流程
您提供的示例代碼展示了在 Gin 路由處理函數中如何使用通過 Consul 發現的服務。下面我將結合您的代碼,詳細說明服務發現如何與實際業務接口結合:
// main.go - Gin服務啟動流程
package mainimport ("context""fmt""github.com/gin-gonic/gin""google.golang.org/grpc""net/http""strconv""time""your-project/global""your-project/proto""your-project/service""your-project/response" // 假設這是模型包"your-project/forms" // 假設這是表單驗證包"your-project/models" // 假設這是模型包"your-project/utils" // 假設這是工具包"zap"
)func main() {// 1. 初始化配置initConfig()// 2. 初始化服務連接(從Consul發現服務)service.InitSrvConn()// 3. 初始化Gin引擎r := gin.Default()// 4. 注冊中間件setupMiddlewares(r)// 5. 注冊路由setupRoutes(r)// 6. 注冊Web服務到Consulif err := service.RegisterWebServiceToConsul(); err != nil {panic(fmt.Sprintf("Web服務注冊失敗: %v", err))}// 7. 啟動Gin服務addr := fmt.Sprintf(":%d", global.ServerConfig.Port)zap.S().Infof("服務啟動: %s", addr)if err := r.Run(addr); err != nil {panic(fmt.Sprintf("Gin服務啟動失敗: %v", err))}
}// 用戶控制器 - 整合業務接口示例
func setupRoutes(r *gin.Engine) {userGroup := r.Group("/users"){userGroup.GET("/", GetUserList) // 獲取用戶列表userGroup.POST("/login", PassWordLogin) // 用戶登錄}
}// 獲取用戶列表 -
func GetUserList(ctx *gin.Context) {// 從JWT中取出用戶id(假設JWT驗證已通過)claims, exists := ctx.Get("claims")if !exists {ctx.JSON(http.StatusUnauthorized, gin.H{"error": "未授權",})return}currentUser := claims.(*models.CustomClaims)zap.S().Infof("用戶信息:%v", currentUser.Id)// 分頁參數處理pn := ctx.DefaultQuery("pn", "0")pnInt, _ := strconv.Atoi(pn)pSize := ctx.DefaultQuery("psize", "10")pSizeInt, _ := strconv.Atoi(pSize)// 通過全局變量獲取已初始化的gRPC客戶端rsp, err := global.UserSrvClient.GetUserList(context.Background(), &proto.PageInfo{Pn: uint32(pnInt),PSize: uint32(pSizeInt),})if err != nil {zap.S().Errorw("[GetUserList] 查詢用戶列表失敗", "error", err)utils.HandleGrpcErrorToHttp(err, ctx) // 自定義錯誤處理函數return}// 構建響應數據result := make([]interface{}, 0)for _, value := range rsp.Data {userResponse := response.UserResponse{Id: value.Id,NickName: value.NickName,Birthday: response.JsonTime(time.Unix(int64(value.BirthDay), 0)),Gender: value.Gender,Mobile: value.Mobile,}result = append(result, userResponse)}ctx.JSON(http.StatusOK, result)
}// 用戶登錄 - 示例接口
func PassWordLogin(c *gin.Context) {zap.S().Infof("用戶登錄開始")// 表單驗證passWordLoginForm := forms.PassWordLoginForm{}if err := c.ShouldBind(&passWordLoginForm); err != nil {utils.HandleValidatorError(c, err) // 自定義驗證錯誤處理return}zap.S().Infof("用戶登錄表單驗證成功")// 驗證驗證碼if !utils.Store.Verify(passWordLoginForm.CaptchaId, passWordLoginForm.Captcha, true) {zap.S().Infof("驗證碼錯誤")c.JSON(http.StatusBadRequest, gin.H{"msg": "驗證碼錯誤",})return}// 通過全局變量獲取gRPC客戶端,調用用戶服務驗證登錄rsp, err := global.UserSrvClient.GetUserByMobile(context.Background(), &proto.MobileRequest{Mobile: passWordLoginForm.Mobile,})if err != nil {zap.S().Errorw("[PassWordLogin] 查詢用戶失敗", "error", err)utils.HandleGrpcErrorToHttp(err, c)return}// 驗證密碼(示例代碼,實際應使用加密比較)if passWordLoginForm.PassWord != rsp.PassWord {c.JSON(http.StatusBadRequest, gin.H{"msg": "密碼錯誤",})return}// 生成JWT tokentoken, err := utils.GenerateToken(rsp.Id, rsp.NickName, rsp.Role)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"msg": "生成token失敗",})return}// 返回登錄成功響應c.JSON(http.StatusOK, gin.H{"id": rsp.Id,"nick_name": rsp.NickName,"token": token,"expired_at": time.Now().Add(time.Hour * 24).Unix(),})
}
三、Gin 服務集成 Consul 的關鍵流程解析
3.1 服務發現的時機選擇
從代碼可以看出,服務發現發生在兩個關鍵階段:
- 應用啟動階段:在
main
函數中調用service.InitSrvConn()
,在服務啟動時就完成服務發現和連接建立 - 請求處理階段:在具體的路由處理函數(如
GetUserList
)中使用已初始化的全局客戶端
這種設計的優勢在于:
- 避免首次請求時的延遲(冷啟動問題)
- 通過全局變量復用連接,減少 TCP 握手開銷
- 服務啟動時若無法連接下游服務,則直接終止,避免提供不可用服務
3.2 服務發現與業務邏輯的結合
示例代碼展示了服務發現如何與實際業務邏輯結合:
-
獲取用戶列表接口:
- 從 JWT 中提取用戶身份信息
- 解析分頁參數
- 調用通過 Consul 發現的用戶服務
- 處理返回結果并構建響應
-
用戶登錄接口:
- 表單驗證和驗證碼校驗
- 調用用戶服務驗證用戶信息
- 生成 JWT 令牌并返回
3.3 代碼組織最佳實踐
根據示例代碼,建議以下代碼組織方式:
- 服務發現邏輯:封裝在
service
包中,提供初始化函數(如InitSrvConn
) - 全局客戶端:存儲在
global
包中,供所有需要調用服務的地方使用 - 路由處理:按業務模塊組織路由和處理函數(如
user_routes.go
) - 工具函數:將通用功能(如錯誤處理、表單驗證)封裝在
utils
包中
四、服務注冊中心接口與實現
4.1 服務注冊核心接口詳解
4.1.1 服務注冊的關鍵參數說明
// consul.go - 服務注冊實現中的關鍵配置項
registration := &api.AgentServiceRegistration{ID: fmt.Sprintf("%s-%d", config.Name, config.Port), // 服務ID必須唯一,建議使用"服務名-端口"格式Name: config.Name, // 服務名稱,用于服務發現時的查詢Address: "0.0.0.0", // 服務綁定地址,0.0.0.0表示監聽所有網絡接口Port: config.Port, // 服務端口,需與實際監聽端口一致Tags: []string{"gin", "web-service"}, // 服務標簽,可用于過濾(如查詢所有Gin框架的服務)Check: &api.AgentServiceCheck{// 健康檢查配置核心參數:HTTP: fmt.Sprintf("http://127.0.0.1:%d/health", config.Port), // 健康檢查URL,建議使用回環地址Interval: "10s", // 檢查間隔,過短會增加開銷,過長會延遲故障發現Timeout: "5s", // 超時時間,應小于間隔時間DeregisterCriticalServiceAfter: "30s", // 服務不健康后自動注銷的時間,避免長時間保留失效實例},
}
4.1.2 服務注冊的最佳實踐
- ID 唯一性:使用服務名 + 端口生成 ID,避免多實例部署時 ID 沖突
- 健康檢查類型:Web 服務建議使用 HTTP 檢查(相比 TCP 檢查可驗證應用層邏輯)
- 注銷延遲:
DeregisterCriticalServiceAfter
建議設為 30-60 秒,給服務重啟預留時間 - 標簽規范:通過標簽區分服務類型(如 web、srv)、環境(dev、prod)、版本(v1、v2)
4.2 健康檢查接口實現原理
4.2.1 健康檢查的兩種模式
- 主動檢查:Consul 定期向服務發送請求(如本文實現的 HTTP 檢查)
- 被動檢查:服務主動向 Consul 報告健康狀態(適用于網絡隔離場景)
4.2.2 健康檢查接口的設計要點
// health.go - 健康檢查路由實現細節
package routerimport ("github.com/gin-gonic/gin""your-project/global""time"
)// SetupHealthRoutes 配置健康檢查路由:
// - 路徑固定為/health,符合Consul默認檢查規范
// - 返回JSON格式狀態,便于機器解析和人工查看
func SetupHealthRoutes(r *gin.Engine) {r.GET("/health", func(c *gin.Context) {// 1. 檢查自身服務狀態:// - 可添加內存、CPU使用率檢查// - 驗證數據庫連接、Redis連接等基礎資源selfStatus := "UP"if global.ServerConfig.Port == 0 {selfStatus = "DOWN" // 配置未正確加載}// 2. 檢查依賴服務狀態:// - 此處簡化為判斷客戶端是否存在// - 生產環境應發送實際請求驗證服務可用性depStatus := make(map[string]string)if global.UserSrvClient != nil {// 可選:發送輕量級請求驗證服務響應ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)defer cancel()_, err := global.UserSrvClient.HealthCheck(ctx, &proto.HealthCheckRequest{})if err != nil {depStatus["user-srv"] = "DOWN"} else {depStatus["user-srv"] = "UP"}} else {depStatus["user-srv"] = "DOWN"}// 3. 返回健康狀態:// - 200狀態碼表示整體健康// - 503狀態碼表示部分依賴不可用c.JSON(200, gin.H{"status": selfStatus,"dependencies": depStatus,"timestamp": time.Now().Unix(),"service": global.ServerConfig.Name,})})
}
五、測試調試與錯誤處理
5.1 常見測試場景與調試方法
5.1.1 服務注冊驗證的三種方式
-
Consul UI 可視化驗證:
- 訪問
http://consul-host:8500
進入 Web 管理界面 - 在 "Services" 標簽頁查看
user-web
服務是否存在 - 點擊服務名稱,查看 "Checks" 標簽頁確認健康檢查狀態為 "passing"
- 訪問
-
API 接口驗證:
# 查詢所有注冊服務(返回JSON格式服務列表) curl http://consul-host:8500/v1/catalog/services# 查詢指定服務的詳細信息 curl http://consul-host:8500/v1/catalog/service/user-web# 驗證健康檢查接口(需確保Web服務已啟動) curl http://web-service-host:8021/health
-
日志動態驗證:
- 啟動 Web 服務時觀察日志,應輸出 "Web 服務已注冊到 Consul"
- 查看 Consul 服務器日志,確認收到注冊請求
5.1.2 服務發現調試技巧
// 調試服務發現的輔助函數(生產環境建議注釋掉)
func debugServiceDiscovery() {serviceName := "user-srv"host, port, err := service.GetServiceAddress(serviceName)if err != nil {log.Fatalf("服務發現失敗: 錯誤詳情, %v", err)}// 打印詳細的服務元數據,便于調試log.Printf("發現服務 %s 在 %s:%d", serviceName, host, port)// 可選:打印服務的Tags、Meta等信息
}
5.2 錯誤處理最佳實踐
5.2.1 服務發現異常的分級處理
// 帶重試和降級的服務發現函數
func GetServiceWithFallback(serviceName string) (string, int, error) {// 1. 定義重試策略:最多重試3次,間隔遞增maxRetries := 3for i := 0; i < maxRetries; i++ {host, port, err := service.GetServiceAddress(serviceName)if err == nil {return host, port, nil // 成功獲取直接返回}// 打印帶重試次數的錯誤日志,便于問題追蹤log.Printf("服務發現失敗(嘗試 %d/%d): %v", i+1, maxRetries, err)// 指數退避策略:重試間隔逐漸增加,減少服務器壓力time.Sleep(time.Duration(i+1) * 2 * time.Second)}// 2. 服務發現失敗后的降級處理:// - 從緩存中獲取歷史地址(需實現緩存機制)// - 返回預定義的備用地址(如備用服務節點)fallbackHost, fallbackPort := getFallbackServiceAddress(serviceName)if fallbackHost != "" {log.Printf("使用降級地址: %s:%d", fallbackHost, fallbackPort)return fallbackHost, fallbackPort, nil}return "", 0, fmt.Errorf("服務發現重試 %d 次失敗且無降級方案", maxRetries)
}
5.2.2 網絡異常的全鏈路處理
// 在gRPC連接中添加完整的錯誤處理鏈
userConn, err := grpc.Dial(fmt.Sprintf("%s:%d", host, port),grpc.WithInsecure(),grpc.WithBlock(),grpc.WithTimeout(5*time.Second), // 連接超時控制grpc.WithKeepaliveParams(keepalive.ClientParameters{Time: 10 * time.Second, // 發送心跳間隔Timeout: 3 * time.Second, // 心跳超時PermitWithoutStream: true, // 允許在無流時發送心跳}),
)
if err != nil {// 區分臨時性錯誤和永久性錯誤if opErr, ok := err.(*net.OpError); ok && opErr.Temporary() {log.Printf("臨時性連接錯誤,將嘗試重連: %v", err)// 此處可添加重連邏輯} else {log.Printf("永久性連接錯誤: %v", err)// 記錄錯誤并返回降級響應return nil, err}
}
六、Web 層服務集成 Consul 的優化策略
6.1 連接池與負載均衡的工程實踐
6.1.1 連接池實現原理
連接池解決的核心問題:
- TCP 連接創建開銷:每次調用都創建新連接會導致三次握手和四次揮手的性能損耗
- 文件句柄限制:大量短連接可能耗盡系統文件句柄資源
- 連接復用效率:復用已有連接可減少網絡延遲和資源占用
// 全局連接池實現細節
var (// 連接池配置參數可通過配置文件動態調整userSrvConnPool = &connPool{maxConn: 10, // 最大連接數,根據服務負載調整conns: make(chan *grpc.ClientConn, 10), // 緩沖通道實現連接池mu: sync.Mutex{}, // 互斥鎖保護連接池狀態}
)type connPool struct {maxConn intconns chan *grpc.ClientConnmu sync.Mutex// 記錄連接創建時間,用于超時回收createTimes map[*grpc.ClientConn]time.Time
}// 從連接池獲取連接的流程:
// 1. 先從通道中獲取空閑連接
// 2. 若通道為空,創建新連接(不超過maxConn限制)
// 3. 檢查連接是否可用(避免使用已關閉的連接)
func (p *connPool) Get() (*grpc.ClientConn, error) {select {case conn := <-p.conns:// 檢查連接是否有效if err := p.checkConnValid(conn); err != nil {conn.Close()return p.createNewConn()}return conn, nildefault:return p.createNewConn()}
}// 創建新連接時添加服務發現邏輯,確保獲取最新地址
func (p *connPool) createNewConn() (*grpc.ClientConn, error) {p.mu.Lock()defer p.mu.Unlock()if len(p.conns) >= p.maxConn {return nil, fmt.Errorf("連接池已滿,無法創建新連接")}host, port, err := service.GetServiceAddress("user-srv")if err != nil {return nil, err}conn, err := grpc.Dial(fmt.Sprintf("%s:%d", host, port),grpc.WithInsecure(),grpc.WithBlock(),)if err != nil {return nil, err}// 記錄連接創建時間,用于超時回收if p.createTimes == nil {p.createTimes = make(map[*grpc.ClientConn]time.Time)}p.createTimes[conn] = time.Now()return conn, nil
}// 定期清理超時連接的后臺goroutine
func (p *connPool) cleanUpExpiredConns() {ticker := time.NewTicker(30 * time.Second)defer ticker.Stop()for range ticker.C {p.mu.Lock()now := time.Now()for conn, createTime := range p.createTimes {if now.Sub(createTime) > 5*time.Minute { // 5分鐘未使用的連接conn.Close()delete(p.createTimes, conn)}}p.mu.Unlock()}
}
6.2 生產環境部署建議
6.2.1 Consul 集群高可用配置
- 節點數量:建議部署 3 或 5 個節點(奇數個),可容忍 1 或 2 個節點故障
- 數據加密:啟用 TLS 加密通信,防止中間人攻擊
- ACL 權限:配置細粒度訪問控制,區分服務注冊、查詢等操作的權限
- 數據持久化:配置 Raft 日志持久化,確保節點重啟后數據不丟失
6.2.2 連接超時與熔斷策略
- 超時設置原則:下游服務的超時時間應小于上游服務的超時時間
- 熔斷觸發條件:連續失敗次數超過閾值(如 5 次)則觸發熔斷
- 熔斷恢復策略:設置半開狀態,定期嘗試少量請求,成功后恢復正常
- 降級響應設計:熔斷期間返回預定義的降級響應,避免前端報錯
6.2.3 配置動態更新機制
-
監聽 Consul KV 變更:
// 監聽配置變更的后臺goroutine func watchConfigChanges() {client, err := getConsulClient()if err != nil {log.Fatalf("創建Consul客戶端失敗: %v", err)}kv := client.KV()lastIndex := uint64(0)for {// 查詢配置鍵值對,帶阻塞查詢options := &api.QueryOptions{WaitIndex: lastIndex,WaitTime: 10 * time.Second,}pair, meta, err := kv.Get("config/web-service", options)if err != nil {log.Printf("查詢配置失敗: %v", err)time.Sleep(1 * time.Second)continue}if pair != nil {// 解析新配置并更新全局配置if err := updateGlobalConfig(pair.Value); err != nil {log.Printf("更新配置失敗: %v", err)}}lastIndex = meta.LastIndex} }
-
配置更新注意事項:
- 避免頻繁更新導致服務抖動,設置最小更新間隔
- 關鍵配置(如數據庫連接)更新時需平滑過渡
- 配置更新后記錄變更日志,便于問題追溯
七、完整項目結構參考
通過本文的實踐,我們完成了 Gin Web 服務與 Consul 注冊中心的深度集成,覆蓋了從服務注冊、服務發現到健康檢查、錯誤處理的全流程。在實際項目中,Web 層作為微服務的門面,其穩定性直接影響用戶體驗,合理利用 Consul 的服務治理能力能夠有效提升系統的可靠性和可維護性。建議結合業務特點進一步完善負載均衡策略、連接池管理和動態配置更新機制,打造真正健壯的微服務前端架構。當遇到問題時,可通過 Consul UI、日志分析和服務發現調試工具逐步定位,確保每個環節的穩定性。
web-service/
├── config/ # 配置文件目錄,采用環境隔離設計
│ ├── base.yaml # 基礎公共配置,包含各環境通用參數
│ ├── dev.yaml # 開發環境配置,包含本地服務地址
│ ├── test.yaml # 測試環境配置,指向測試集群
│ └── prod.yaml # 生產環境配置,指向正式集群
├── global/ # 全局狀態管理
│ ├── global.go # 存儲全局配置、客戶端等對象
│ └── constants.go # 定義項目常量
├── initialize/ # 初始化邏輯封裝
│ ├── config.go # 配置文件解析與驗證
│ ├── consul.go # Consul客戶端初始化與服務注冊
│ ├── logger.go # 日志系統初始化
│ └── grpc.go # gRPC客戶端連接初始化
├── proto/ # gRPC服務定義
│ ├── user.proto # 用戶服務接口定義(.proto文件)
│ ├── user_grpc.pb.go # 生成的gRPC客戶端代碼
│ └── user.pb.go # 生成的消息結構體代碼
├── service/ # 核心業務邏輯
│ ├── discovery.go # 服務發現實現,包含負載均衡
│ ├── registration.go # 服務注冊實現,包含健康檢查
│ ├── connection_pool.go # gRPC連接池實現
│ └── fallback.go # 服務降級邏輯
├── router/ # 路由配置
│ ├── routes.go # 主路由注冊,包含中間件
│ ├── health.go # 健康檢查路由
│ ├── user_routes.go # 用戶相關API路由
│ └── auth_routes.go # 認證授權路由
├── handler/ # 接口處理函數
│ ├── user_handler.go # 用戶信息查詢、修改等接口
│ ├── auth_handler.go # 登錄、token管理等接口
│ ├── order_handler.go # 訂單相關接口(示例)
│ └── middleware.go # 自定義中間件(如JWT認證)
├── model/ # 數據模型
│ ├── request.go # 請求參數結構體
│ ├── response.go # 響應結果結構體
│ └── entity.go # 業務實體模型
├── utils/ # 工具函數
│ ├── jwt.go # JWT生成與驗證
│ ├── redis.go # Redis操作封裝
│ └── error.go # 錯誤處理工具
└── main.go # 程序入口,包含啟動流程
對于這篇文章如果大家對微服務不是很熟練的話,主要前四部分就好。可以看到在第五部分我也寫到了負載均衡,對于簡化配置來說,這是一個很重要的內容,后面的文章我會盡快寫到。
還有就是可以看到在第三部分的舉例中,我其實是將InitSrvConn 初始化服務連接這部分代碼作為全局變量來寫的,這就大大簡化了代碼量,如果不設一個全局變量,你每次都要建立一次連接,這不符合開發原則,所以關于全局變量的實踐我后面會單獨出一篇文章來寫。
制作不易,大概花了6小時來寫,如果這篇文章對大家有幫助可以點贊關注,你的支持就是我的動力😊!