Go微服務: Gin框架搭建網關, 接入熔斷器,鏈路追蹤以及服務端接入限流和鏈路追蹤

概述

  • 本文使用最簡單和快速的方式基于Gin框架搭建一個微服務的網關調用微服務的場景
  • 網關作為客戶端基于RPC調用某一服務端的服務并接入熔斷和限流以及鏈路追蹤
  • 具體場景:通過網關API查詢購物車里的數據
  • 在最后,會貼上網關和購物車服務的代碼倉庫

服務端搭建


1 )目錄結構

cart
├── domain
│      ├── model
│      │     └── cart.go
│      ├── repository
│      │     └── cart_repository.go
│      ├── service
│      │     └── cart_data_service.go
├── handler
│      └── cart.go
├── proto
│      ├── cart
│            ├── cart.proto
│            ├── cart.pb.go                  # 待生成
│            └── cart.pb.micro.go            # 待生成
├── go.mod
├── main.go
└── Makefile
  • 可以看到這是基于領域模型搭建的框架,默認是通過 go-micro生成的模板,之后進行修改的
  • proto 下的 pb.go 和 pb.micro.go 是通過 Makefile 中配置的 protoc 命令生成的
    • $ protoc --proto_path=. --micro_out=. --go_out=:. proto/cart/*.proto

2 ) 核心 main.go

package mainimport ("fmt""log""strconv""github.com/go-micro/plugins/v4/registry/consul"opentracingTool "github.com/go-micro/plugins/v4/wrapper/trace/opentracing""github.com/go-micro/plugins/v4/wrapper/ratelimiter/ratelimit"jujuratelimit "github.com/juju/ratelimit""github.com/opentracing/opentracing-go""go-micro.dev/v4""go-micro.dev/v4/registry""gitee.com/go-micro-services/cart/domain/repository""gitee.com/go-micro-services/cart/domain/service""gitee.com/go-micro-services/cart/handler"pbcart "gitee.com/go-micro-services/cart/proto/cart""gitee.com/go-micro-services/common""github.com/jinzhu/gorm"_ "github.com/jinzhu/gorm/dialects/mysql"
)var (serviceName     = "go.micro.service.cart"version         = "latest"host            = "127.0.0.1"port            = 8500address         = host + ":" + strconv.Itoa(port)mysqlConfigPath = "/micro/config/mysql"
)func main() {// 1. 指定注冊中心consulReg := consul.NewRegistry(registry.Addrs(address),)// 2. 從配置中心獲取mysql配置并創建處理mysqlConfig, consulConfigErr := common.GetConsulMysqlConfig(address, mysqlConfigPath)if consulConfigErr != nil {log.Fatal(consulConfigErr)}mysqlUrl := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", mysqlConfig.User, mysqlConfig.Pwd, mysqlConfig.Host, mysqlConfig.Port, mysqlConfig.Database)db, dbErr := gorm.Open("mysql", mysqlUrl)if dbErr != nil {log.Fatal(dbErr)}defer db.Close()rp := repository.NewCartRepository(db)// 數據庫表初始化,只執行一次, 如果本來就設計好了,則無需下面// rp.InitTable()// db.SingularTable(false) // true 則 表就是單數// 3. 鏈路追蹤配置tracer, closer, tracerErr := common.NewTracer("go.micro.service.cart", "localhost:6831")if tracerErr != nil {log.Fatal(tracerErr)}defer closer.Close()opentracing.SetGlobalTracer(tracer)// 4. 創建服務實例cartDataService := service.NewCartDataService(rp)// 5. 創建服務和初始化srv := micro.NewService()srv.Init(micro.Name(serviceName),micro.Version(version),micro.Registry(consulReg),// micro.Address("0.0.0.0:8087"), // 暴露的服務地址 這里可用可不用指定micro.WrapHandler(opentracingTool.NewHandlerWrapper(opentracing.GlobalTracer())),       // 綁定鏈路追蹤micro.WrapHandler(ratelimit.NewHandlerWrapper(jujuratelimit.NewBucket(50, 100), true)), // 添加限流)// 6. 注冊 handlerif handlerErr := pbcart.RegisterCartHandler(srv.Server(), &handler.Cart{CartDataService: cartDataService}); handlerErr != nil {log.Fatal(handlerErr)}// 7. 運行服務if runErr := srv.Run(); runErr != nil {log.Fatal(runErr)}
}
  • 以下是一些注意事項
    • 服務初始化需要借助mysql,mysql的一些連接信息是從consul配置中心獲取的
    • 在初始化的時候,需要先運行一次 rp.InitTable()db.SingularTable(false)
    • 上面 jujuratelimit.NewBucket(50, 100) 中的參數50, 100可以寫入環境變量來獲取,這個可以根據具體資源情況進行調節
  • 這里,購物車服務已經搭建完成,可以進行啟動了
    • $ sudo go run main.go
  • 啟動后,在consul中查看,發現已經注冊成功了

網關搭建


1 )目錄結構

api
├── conf
│      ├── app.ini                                 # 通用配置文件
│      ├── env.local.ini                           # 本地配置文件
│      ├── env.test.ini                            # 測試環境配置
│      ├── env.uat.ini                             # uat環境配置
│      ├── env.prod.ini                            # prod環境配置
├── controllers
│      ├── api
│           └── api.go
├── middlewares
│      └── tracer.go                               # 鏈路追蹤中間件
├── routers
│      └── router.go                               # 路由配置
├── utils
│      ├── common.go                               # 工具包通用工具
│      ├── conf.go                                 # 讀取配置工具
│      └── micro.go                                # 微服務工具
├── go.mod
├── main.go
└── Makefile

2 )通用配置信息

[app]
appName  = go.micro.api
appVersion = latest
appZeroHost = 0.0.0.0
appAddr  =  0.0.0.0:8080[consul]
address  = 127.0.0.1:8500[jaeger]
tracerAddr = 127.0.0.1:6831[hystrix]
hystrixPort = 9096

3 ) main.go 核心代碼

package mainimport ("gitee.com/go-micro-services/api/middlewares""gitee.com/go-micro-services/api/routers""gitee.com/go-micro-services/api/utils""github.com/gin-gonic/gin""go-micro.dev/v4/web"
)func main() {// 1. 創建一個默認的路由引擎ginRouter := gin.Default()ginRouter.Use(middlewares.Trace()) // 加入 tracing 中間件routers.RoutersInit(ginRouter)// 2. web網關服務開啟server := web.NewService(web.Name(utils.AppName),       // 服務名稱web.Address(utils.AppAddr),    // 服務端口web.Handler(ginRouter),        // 服務路由web.Registry(utils.ConsulReg), // 注冊中心)// 3. 啟動server.Run()
}
  • 這里,web模塊是go-micro中用于構建Web服務的部分
  • 它允許你使用標準的HTTP協議來暴露和調用微服務
  • web.NewService 函數的作用
    • 用于創建一個新的Web服務實例
    • 該函數接受一系列的配置參數
    • 用于定義服務的名稱、地址、處理程序(即路由引擎)以及注冊中心等信息
  • 下面是web.NewService函數中各個參數的意義
    • web.Name(utils.AppName): 設置服務的名稱, 這通常用于服務發現和注冊中心中的服務標識
    • web.Address(utils.AppAddr): 設置服務的監聽地址和端口號, 這決定了服務應該在哪里監聽傳入的HTTP請求
    • web.Handler(ginRouter): 設置服務的路由處理程序, 這里傳入的是基于gin框架的路由引擎實例,它定義了如何處理傳入的HTTP請求
    • web.Registry(utils.ConsulReg): 設置服務的注冊中心。這允許服務在啟動時將自己注冊到指定的注冊中心(例如Consul),以便其他服務可以發現和調用它

4 )utils 包

  • utils.common.go

    package utilsimport ("fmt""os""gopkg.in/ini.v1"
    )// 讀取通用配置
    func getConfig() *ini.File {appConfig, iniErr := ini.Load("conf/app.ini")if iniErr != nil {fmt.Printf("Fail to read file: %v", iniErr)os.Exit(1)}return appConfig
    }func init() {// 1. 讀取配置文件appConfig := getConfig()// 2. 獲取配置initConfig(appConfig)// 3. 初始化微服務initMicro(appConfig)
    }
    
  • utils/conf.go

    package utilsimport ("gopkg.in/ini.v1"
    )// 通用配置
    var AppName string
    var AppVersion string
    var AppAddr string
    var AppZeroHost string// 讀取通用配置
    func initConfig(appConfig *ini.File) {AppName = appConfig.Section("app").Key("appName").String()         // 應用名稱AppVersion = appConfig.Section("app").Key("appVersion").String()   // 應用版本AppAddr = appConfig.Section("app").Key("appAddr").String()         // 應用地址AppZeroHost = appConfig.Section("app").Key("appZeroHost").String() // 零地址
    }
    
  • utils/micro.go

    package utilsimport ("context""fmt""net""net/http""go-micro.dev/v4""go-micro.dev/v4/client""gopkg.in/ini.v1""gitee.com/go-micro-services/common""github.com/go-micro/plugins/v4/registry/consul"opentracingTool "github.com/go-micro/plugins/v4/wrapper/trace/opentracing""github.com/opentracing/opentracing-go"log "go-micro.dev/v4/logger""go-micro.dev/v4/registry""github.com/afex/hystrix-go/hystrix""github.com/go-micro/plugins/v4/wrapper/select/roundrobin"
    )// 微服務micro客戶端
    var SrvClient client.Client
    var ConsulAddr string
    var ConsulReg registry.Registry
    var TracerAddr string
    var HystrixPort string// 讀取通用配置
    func initMicro(appConfig *ini.File) {// 1. 配置注冊中心ConsulAddr = appConfig.Section("consul").Key("address").String()ConsulReg := consul.NewRegistry(registry.Addrs(ConsulAddr),)// 2. 配置鏈路追蹤 jaegerTracerAddr = appConfig.Section("jaeger").Key("tracerAddr").String()// 3. 配置熔斷HystrixPort = appConfig.Section("hystrix").Key("hystrixPort").String()setHystrix()// 4. 創建服務srv := micro.NewService()srv.Init(micro.Name(AppName),micro.Version(AppVersion),micro.Registry(ConsulReg),// 綁定鏈路追蹤micro.WrapHandler(opentracingTool.NewHandlerWrapper(opentracing.GlobalTracer())),// 添加熔斷micro.WrapClient(NewClientHystrixWrapper()),// 添加負載均衡micro.WrapClient(roundrobin.NewClientWrapper()),)SrvClient = srv.Client() // 對外暴露
    }// 鏈路追蹤配置
    func SetTracer() (opentracing.Tracer, error) {tracer, closer, err := common.NewTracer(AppName, TracerAddr)if err != nil {log.Fatal(err)}defer closer.Close()return tracer, err
    }// 熔斷器 設置
    func setHystrix() {hystrixStreamHandler := hystrix.NewStreamHandler()hystrixStreamHandler.Start()go func() {// 啟動端口err := http.ListenAndServe(net.JoinHostPort(AppZeroHost, HystrixPort), hystrixStreamHandler)if err != nil {log.Error(err)}}()
    }// 熔斷器 type
    type clientWrapper struct {client.Client
    }// 熔斷器 Call
    func (c *clientWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {return hystrix.Do(req.Service()+"."+req.Endpoint(), func() error {// run 正常執行fmt.Println(req.Service() + "." + req.Endpoint())return c.Client.Call(ctx, req, rsp, opts...)}, func(err error) error {fmt.Println(err)return err})
    }// 熔斷器 Wrapper
    func NewClientHystrixWrapper() client.Wrapper {return func(i client.Client) client.Client {return &clientWrapper{i}}
    }
    

5 )路由

package routersimport ("gitee.com/go-micro-services/api/controllers/api""github.com/gin-gonic/gin"
)func RoutersInit(r *gin.Engine) {rr := r.Group("/api"){rr.GET("/findAll", api.ApiController{}.FindAll)}
}

6 ) 控制器

package apiimport ("context""fmt""strconv""gitee.com/go-micro-services/api/utils"cart "gitee.com/go-micro-services/cart/proto/cart""github.com/gin-gonic/gin""github.com/prometheus/common/log"
)type ApiController struct{}func (con ApiController) FindAll(c *gin.Context) {log.Info("接受到 /api/findAll 訪問請求")// 1. 獲取參數user_id_str := c.Query("user_id")userId, err := strconv.ParseInt(user_id_str, 10, 64)if err != nil {c.JSON(200, gin.H{"message": "參數異常","success": false,})return}fmt.Println(userId)// 2. rpc 遠程調用:獲取購物車所有商品cartClient := cart.NewCartService("go.micro.service.cart", utils.SrvClient)cartAll, err := cartClient.GetAll(context.TODO(), &cart.CartFindAll{UserId: userId})fmt.Println(cartAll)fmt.Println("-----")c.JSON(200, gin.H{"data":    cartAll,"success": true,})
}

7 ) 服務啟動準備

  • 這里可以看到,基于gin框架來搭建了一個網關
  • 這里可以訪問 /api/findAll?user_id=xxx 來調用購物車的微服務
  • 現在我們準備下數據,在數據庫中初始化一條數據
  • 現在就可以通過:網關ip:8080/api/findAll?user_id=1 來調用了

8 )服務啟動

  • $ sudo go run main.go
  • 可以看到,服務已經啟動了

服務調用與測試


1 ) 調用

  • 訪問網關ip:8080/api/findAll?user_id=1
  • 可以看到,調用成功

2 )查看鏈路追蹤

  • 在 JAEGER UI 上, 比如本機訪問:http://127.0.0.1:16686/search
  • 可以看到鏈路追蹤上多了2個服務
  • 我們可以看下 api 的相關調用
  • 再看下 cart 的服務調用
  • 當然以上都是通常的調用,如果存在復雜的調用關系或出錯信息,也可以從這里看出來

3 )測試限流

  • 在已搭建好的熔斷面板上可以查看熔斷功能:http://127.0.0.1:9002/hystrix
  • 因為熔斷器是在客戶端,也就是網關層接入的,所以,上面填入 網關ip:/hystrix.stream,比如上面的:192.168.1.7:9096/hystrix.stream
  • 好,現在我們頻繁訪問,刷新API,來測試下
  • 可見峰值不超過 60%,限流成功

總結

  • 從上面可見,我們基于gin框架搭建網關服務和微服務基本已經調通了
  • 相關原理和更多細節參考
    • 1 ) jaeger 鏈路追蹤
      • https://blog.csdn.net/Tyro_java/article/details/137754812
    • 2 )hystrix 熔斷
      • https://blog.csdn.net/Tyro_java/article/details/137777246
    • 3 ) ratelimit 限流
      • https://blog.csdn.net/Tyro_java/article/details/137780273
  • 源碼
    • 服務端 cart 服務:https://gitee.com/go-micro-services/cart
    • 客戶端 網關api 服務: https://gitee.com/go-micro-services/api

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/12312.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/12312.shtml
英文地址,請注明出處:http://en.pswp.cn/web/12312.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

避雷:搭建AI知識庫注意事項

AI知識庫作為信息存儲和進行智能處理的核心部分,受到越來越多企業的重視。為了更好地發展,企業也紛紛開始搭建AI知識庫。然而,在搭建AI知識庫的過程中,也有很多雷區容易踩到,導致項目延遲、效果不佳甚至失敗。所以&…

《控制系統實驗與綜合設計》計控第三次(含程序和題目)

實驗七 采樣控制系統的分析 一、實驗完成任務 1、熟悉用 LF398 組成的采樣控制系統; 2、通過本實驗理解采樣定理和零階保持器的原理及其實現方法; 3、觀察系統在階躍作用下的穩態誤差。 4.、研究開環增益 K 和采樣周期 T 的變化對系統動態性能的影響…

Linux基礎之進程-進程狀態

目錄 一、進程狀態 1.1 什么是進程狀態 1.2 運行狀態 1.2 阻塞狀態 1.3 掛起狀態 二、Linux操作系統上具體的進程狀態 2.1 狀態 2.2 R 和 S 狀態的查看 2.3 后臺進程和前臺進程 2.4 休眠狀態和深度休眠狀態 一、進程狀態 1.1 什么是進程狀態 首先我們知道我們的操作系…

分布式光伏監控系統功能模塊詳解

目前,分布式光伏發電系統的總容量比較小,并且光伏電站的功率受外界環境影響容易出現大起大落的現象。這使電壓調整變得很困難。光伏電站運行維護人員不足,長時間不保養維護會影響光伏電站的發電效率。針對上述問題,鷓鴣云基于無線…

天銳綠盾|設計院圖紙透明加密軟件、制造業文件資料防止外泄

#圖紙加密軟件# 天銳綠盾是一家專注于數據安全解決方案的提供商,其產品主要為企業級用戶設計,旨在保護敏感信息和知識產權免遭未經授權的訪問或泄露。"天銳綠盾"的圖紙透明加密軟件和機械制造業文件資料防止外泄系統,是專為設計院…

JS中的宏任務和微任務

JavaScript 引擎是建立在一個事件循環系統之上的,它實時監控事件隊列,如果有事件就執行,如果沒有事件就等待。事件系統是一個典型的生產消費模式,生產者發出事件,接收者監聽事件,在UI 開發中是常見的一個設…

Modbus TCP轉CAN網關在不同行業中的應用以及其使用上的優勢

倍訊科技Modbus TCP轉CAN網關通常被用于工業自動化領域,特別是在需要連接現有Modbus TCP網絡和CAN總線設備的場景中。以下是該網關在不同行業中的應用以及其使用上的優勢: 1. 制造業: - 在制造業中,各種類型的設備和機器通常使用不…

Java項目實現報文數據校驗注解方式(必輸項、值大小)

普通項目 導入校驗依賴 <dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId><version>4.1.0.Final</version></dependency><dependency><groupId>javax.validation</…

Docker安裝Redis,并在 Visual Studio Code 中使用它

Docker安裝Redis 查找Redis docker search Redis完整結果 PS C:\Users\cheng> docker search Redis NAME DESCRIPTION STARS OFFICIAL redis Redis is an open …

System V IPC(進程間通信)機制詳解

文章目錄 一、引言二、System V IPC的基本概念1、IPC結構的引入2、IPC標識符&#xff08;IPC ID&#xff09;3、S ystem V的優缺點 三、共享內存&#xff08;Shared Memory&#xff09;1、共享內存的基本概念2、共享內存的創建&#xff08;shmget&#xff09;3、共享內存的附加…

C++:并發保護

一、前言 本文將會通過保護一個數據討論&#xff1a;互斥鎖、雙重檢查鎖、 std::once_flag 類、 std::call_once() 函數、單例模式、使用局部靜態變量實現單例模式等。 二、保護共享數據 假設我們需要某個共享數據&#xff0c;而它創建起來開銷不菲。因為創建它可能需要建立…

vim中的替換

:[range]s/pattern/replacement/flags 這里各部分的含義是&#xff1a; :[range]&#xff1a;可選的行范圍&#xff0c;用于指定在哪些行之間進行替換。如果省略&#xff0c;則默認為當前行。例如&#xff0c;1,10 表示在第1行到第10行之間替換&#xff0c;% 表示在整個文件中…

python的文件操作及函數式編程介紹

五、文件操作 1、讀取鍵盤輸入 input 獲取標準輸入&#xff0c;數據類型統一為字符串 #!/usr/bin/python # -*- coding: UTF-8 -*- str input("請輸入&#xff1a;") print&#xff08;"你輸入的內容是: ", str&#xff09; 這會產生如下的對應著輸入的…

KeyShot 2023.3 Pro for mac/win:完美融合3D渲染與動畫制作

在當今數字化時代&#xff0c;視覺內容的創作和表現越來越受到重視。無論是產品設計、建筑規劃&#xff0c;還是影視特效&#xff0c;都需要具備出色的3D渲染和動畫制作工具來展現創意和想法。而作為業內領先的3D渲染和動畫制作軟件之一&#xff0c;KeyShot 2023.3 Pro在這個領…

電腦剛開機的時候不卡,用一會就變卡頓了…怎么回事?

前言 昨天咱們聊到舊電腦更換了固態硬盤之后&#xff0c;開機就會變快的事情&#xff0c;這個確實是可行且有效的升級電腦辦法之一。 看完這篇之后&#xff0c;切莫著急升級電腦硬件配置&#xff0c;因為這里的坑比你想象的還要多。 從機械硬盤測試的數據和固態硬盤的測試數…

類與面向對象編程(Object-Oriented Programming, OOP)

類與面向對象編程&#xff08;Object-Oriented Programming, OOP&#xff09;&#xff08;一&#xff09; 對象比較&#xff1a;‘is’ 對比 ‘’ 當我還是個孩子的時候&#xff0c;我們的鄰居擁有一對雙胞胎貓咪。它們看起來幾乎一模一樣—同樣的炭黑色毛發和同樣銳利的綠色…

2024年港口危貨安全管理證報名條件

危化品安全員證報考條件 1、職業道德良好&#xff0c;身體健康&#xff0c;年齡不超過60周歲(法定代表人除外). 2、建筑施工企業的在職人員。 3、學歷及要求: (1)建筑施工企業主要負責人應為大專以上學歷&#xff0c;需大專以上學歷&#xff0c;除企業法人外&#xff0c;其他…

linux進階高級配置,你需要知道的有哪些(11)-YUM倉庫服務與PXE網絡裝機

1、基于RPM包構建的軟件更新機制 可以自動解決依賴關系 所有軟件包由集中的YUM軟件倉庫提供 2、軟件倉庫的提供方式 FTP服務&#xff1a;ftp://...... HTTP服務&#xff1a;http://...... 本地目錄&#xff1a;file:///...... 3、客戶端YUM命令 &#xff08;1&#xff0…

從CSDN搬家到微信公眾號

博主將會在微信公眾號里不斷輸出精品內容&#xff0c;陪伴大家共同成長。 如果你對博主的經歷感興趣&#xff0c;或者對博主的IT技術感興趣&#xff0c;歡迎關注我的微信公眾號&#xff0c;閱讀我的技術文章&#xff0c;免費獲取各種IT資源。也可以加我的微信成為我的好友&…

檔案數字化加工是如何利用檔案的

檔案數字化加工是將紙質檔案轉化為數字形式&#xff0c;并進行后續的加工和利用。通過檔案數字化加工&#xff0c;可以實現以下幾個方面的利用&#xff1a; 1. 存儲和保護&#xff1a;數字化檔案可以將大量的紙質檔案存儲在數字存儲介質中&#xff0c;從而節省空間和維護成本。…