封装用户服务客户端

打开common项目
修改pkg/container/service.go

  1. package container
  2. import (
  3. userPb "github.com/869413421/micro-service/user/proto/user"
  4. "github.com/micro/go-micro/v2"
  5. "github.com/micro/go-micro/v2/broker"
  6. )
  7. var service micro.Service
  8. var userServiceClient userPb.UserService
  9. // SetService 设置服务实例
  10. func SetService(srv micro.Service) {
  11. service = srv
  12. }
  13. // GetService 返回服务实例
  14. func GetService() micro.Service {
  15. return service
  16. }
  17. // GetServiceBroker 返回服务Broker实例
  18. func GetServiceBroker() broker.Broker {
  19. return service.Options().Broker
  20. }
  21. // SetUserServiceClient 设置客户端实例
  22. func SetUserServiceClient(userService userPb.UserService) {
  23. userServiceClient = userService
  24. }
  25. // GetUserServiceClient 获取客户端实例
  26. func GetUserServiceClient() userPb.UserService {
  27. return userServiceClient
  28. }

打开user-api

  1. package main
  2. import (
  3. "github.com/869413421/micro-service/common/pkg/container"
  4. pb "github.com/869413421/micro-service/user/proto/user"
  5. "github.com/micro/go-micro/v2"
  6. "github.com/micro/go-micro/v2/web"
  7. "log"
  8. "time"
  9. )
  10. func main() {
  11. // 1.初始化web客户端
  12. var serviceName = "micro.api.user"
  13. service := web.NewService(
  14. web.Name(serviceName),
  15. web.Address(":81"),
  16. // 指定服务注册信息在注册中心的有效期。 默认为一分种
  17. web.RegisterTTL(time.Minute*2),
  18. // 指定服务主动向注册中心报告健康状态的时间间隔,默认为 30 秒。
  19. web.RegisterInterval(time.Minute*1),
  20. )
  21. // 2.初始化用户服务客户端
  22. clientService := micro.NewService(
  23. micro.Name("pg.api.user.cli"),
  24. )
  25. client := pb.NewUserService("micro.service.user", clientService.Client())
  26. container.SetUserServiceClient(client)
  27. // 3.启动web客户端
  28. err := service.Init()
  29. if err != nil {
  30. log.Fatal("Init api error:", err)
  31. }
  32. err = service.Run()
  33. if err != nil {
  34. log.Fatal("start api error:", err)
  35. return
  36. }
  37. }

修改commonuser-apigo.mod,锁定grpc版本

  1. replace google.golang.org/grpc => google.golang.org/grpc v1.26.0

执行go mod tidy 下载依赖

封装BaseController

打开common项目

  1. mkdir -p api/http/controller
  2. touch api/http/controller/base_controller.go
  1. package controller
  2. import "github.com/gin-gonic/gin"
  3. // ResponseErrors 异常信息统一格式
  4. type ResponseErrors map[string]interface{}
  5. // ResponseData 统一响应格式
  6. type ResponseData struct {
  7. Code int `json:"code"`
  8. ErrorMsg string `json:"errorMsg"`
  9. Data interface{} `json:"data"`
  10. }
  11. // Pagination 分页统一结构体
  12. type Pagination struct {
  13. Page uint64 `form:"page"`
  14. PerPage uint32 `form:"perPage"`
  15. }
  16. // BaseController base
  17. type BaseController struct {
  18. }
  19. // NewBaseController 初始化控制器
  20. func NewBaseController() *BaseController {
  21. return &BaseController{}
  22. }
  23. // ResponseJson 响应json
  24. func (*BaseController) ResponseJson(ctx *gin.Context, code int, errorMsg string, data interface{}) {
  25. responseData := ResponseData{
  26. Code: code,
  27. ErrorMsg: errorMsg,
  28. Data: data,
  29. }
  30. ctx.JSON(code, responseData)
  31. ctx.Abort()
  32. }

执行go mod tidy 下载依赖

编写用户控制器

  1. mkdir -p app/http/controllers
  2. touch app/http/controllers/user_controller.go
  1. package controllers
  2. import (
  3. base "github.com/869413421/micro-service/common/api/http/controller"
  4. "github.com/869413421/micro-service/common/pkg/container"
  5. "github.com/869413421/micro-service/common/pkg/types"
  6. pb "github.com/869413421/micro-service/user/proto/user"
  7. "github.com/gin-gonic/gin"
  8. "net/http"
  9. )
  10. // UserController 用户控制器
  11. type UserController struct {
  12. base.BaseController
  13. }
  14. // NewUserController 初始化用户控制器
  15. func NewUserController() *UserController {
  16. return &UserController{}
  17. }
  18. // Index 用户分页
  19. func (controller *UserController) Index(context *gin.Context) {
  20. // 1.构建发起请求参数
  21. pagination := &base.Pagination{}
  22. err := context.BindQuery(pagination)
  23. if err != nil {
  24. controller.ResponseJson(context, http.StatusForbidden, "pagination params required", []string{})
  25. }
  26. // 2.请求用户服务
  27. req := &pb.PaginationRequest{
  28. Page: pagination.Page,
  29. PerPage: pagination.PerPage,
  30. }
  31. client := container.GetUserServiceClient()
  32. rsp, err := client.Pagination(context, req)
  33. if err != nil {
  34. controller.ResponseJson(context, http.StatusInternalServerError, err.Error(), []string{})
  35. return
  36. }
  37. //3.响应用户信息
  38. controller.ResponseJson(context, http.StatusOK, "", rsp)
  39. }
  40. // Store 创建用户
  41. func (controller *UserController) Store(context *gin.Context) {
  42. // 1.构建微服务请求体
  43. req := &pb.CreateRequest{}
  44. client := container.GetUserServiceClient()
  45. err := context.BindJSON(req)
  46. if err != nil {
  47. controller.ResponseJson(context, http.StatusForbidden, "json params error", []string{})
  48. return
  49. }
  50. // 2.发起创建请求
  51. rsp, err := client.Create(context, req)
  52. if err != nil {
  53. controller.ResponseJson(context, http.StatusInternalServerError, err.Error(), []string{})
  54. return
  55. }
  56. // 3.响应用户信息
  57. controller.ResponseJson(context, http.StatusOK, "", rsp.User)
  58. }
  59. // Update 更新用户
  60. func (controller *UserController) Update(context *gin.Context) {
  61. // 1.获取路由中的ID
  62. idStr := context.Param("id")
  63. if idStr == "" {
  64. controller.ResponseJson(context, http.StatusForbidden, "route id required", []string{})
  65. return
  66. }
  67. // 2.构建微服务请求体
  68. req := &pb.UpdateRequest{}
  69. err := context.BindJSON(req)
  70. if err != nil {
  71. controller.ResponseJson(context, http.StatusForbidden, "json params error", []string{})
  72. return
  73. }
  74. id, _ := types.StringToInt(idStr)
  75. req.Id = uint64(id)
  76. // 3.调用服务请求
  77. client := container.GetUserServiceClient()
  78. rsp, err := client.Update(context, req)
  79. if err != nil {
  80. controller.ResponseJson(context, http.StatusInternalServerError, err.Error(), []string{})
  81. return
  82. }
  83. //5.响应用户信息
  84. controller.ResponseJson(context, http.StatusOK, "", rsp.User)
  85. }
  86. // Delete 删除用户
  87. func (controller *UserController) Delete(context *gin.Context) {
  88. //1.获取路由中的ID
  89. idStr := context.Param("id")
  90. if idStr == "" {
  91. controller.ResponseJson(context, http.StatusForbidden, "route id required", []string{})
  92. return
  93. }
  94. //2.构建微服务请求体发起请求
  95. id, _ := types.StringToInt(idStr)
  96. req := &pb.DeleteRequest{Id: uint64(id)}
  97. client := container.GetUserServiceClient()
  98. rsp, err := client.Delete(context, req)
  99. if err != nil {
  100. controller.ResponseJson(context, http.StatusInternalServerError, err.Error(), []string{})
  101. return
  102. }
  103. //3.响应用户信息
  104. controller.ResponseJson(context, http.StatusOK, "", rsp.User)
  105. }
  106. // Show 展示单个用户
  107. func (controller *UserController) Show(context *gin.Context) {
  108. // 1.获取路由中的ID
  109. idStr := context.Param("id")
  110. if idStr == "" {
  111. controller.ResponseJson(context, http.StatusForbidden, "route id required", []string{})
  112. return
  113. }
  114. // 2.构建微服务请求体发起请求
  115. id, _ := types.StringToInt(idStr)
  116. req := &pb.GetRequest{Id: uint64(id)}
  117. client := container.GetUserServiceClient()
  118. rsp, err := client.Get(context, req)
  119. if err != nil {
  120. controller.ResponseJson(context, http.StatusInternalServerError, err.Error(), []string{})
  121. return
  122. }
  123. // 3.响应用户信息
  124. controller.ResponseJson(context, http.StatusOK, "", rsp.User)
  125. }
  126. // Auth 认证
  127. func (controller *UserController) Auth(context *gin.Context) {
  128. //1.构建微服务请求体
  129. req := &pb.AuthRequest{}
  130. err := context.BindJSON(req)
  131. if err != nil {
  132. controller.ResponseJson(context, http.StatusForbidden, "json params error", []string{})
  133. return
  134. }
  135. //2.发起请求
  136. client := container.GetUserServiceClient()
  137. rsp, err := client.Auth(context, req)
  138. if err != nil {
  139. controller.ResponseJson(context, http.StatusInternalServerError, err.Error(), []string{})
  140. return
  141. }
  142. //3.响应用户信息
  143. controller.ResponseJson(context, http.StatusOK, "", rsp)
  144. }

编写重置密码控制器

  1. touch app/http/controllers/password_controller.go
  1. package controllers
  2. import (
  3. "github.com/869413421/micro-service/common/api/http/controller"
  4. "github.com/869413421/micro-service/common/pkg/container"
  5. pb "github.com/869413421/micro-service/user/proto/user"
  6. "github.com/gin-gonic/gin"
  7. "net/http"
  8. )
  9. // PasswordResetController 密码控制器
  10. type PasswordResetController struct {
  11. controller.BaseController
  12. }
  13. // NewPasswordResetController 初始化密码控制器
  14. func NewPasswordResetController() *PasswordResetController {
  15. return &PasswordResetController{}
  16. }
  17. // Store 创建
  18. func (controller *PasswordResetController) Store(context *gin.Context) {
  19. // 1.构建微服务请求体
  20. req := &pb.CreatePasswordResetRequest{}
  21. client := container.GetUserServiceClient()
  22. err := context.BindJSON(req)
  23. if err != nil {
  24. controller.ResponseJson(context, http.StatusForbidden, "json params error", []string{})
  25. return
  26. }
  27. // 2.发起创建请求
  28. rsp, err := client.CreatePasswordReset(context, req)
  29. if err != nil {
  30. controller.ResponseJson(context, http.StatusInternalServerError, err.Error(), []string{})
  31. return
  32. }
  33. // 3.响应信息
  34. controller.ResponseJson(context, http.StatusOK, "", rsp)
  35. }
  36. // ResetPassword 重置密码
  37. func (controller *PasswordResetController) ResetPassword(context *gin.Context) {
  38. // 1.构建微服务请求体
  39. req := &pb.ResetPasswordRequest{}
  40. client := container.GetUserServiceClient()
  41. err := context.BindJSON(req)
  42. if err != nil {
  43. controller.ResponseJson(context, http.StatusForbidden, "json params error", []string{})
  44. return
  45. }
  46. // 2.发起创建请求
  47. rsp, err := client.ResetPassword(context, req)
  48. if err != nil {
  49. controller.ResponseJson(context, http.StatusInternalServerError, err.Error(), []string{})
  50. return
  51. }
  52. // 3.响应信息
  53. controller.ResponseJson(context, http.StatusOK, "", rsp)
  54. }

至此我们完成了控制器的编写,其中基本的业务逻辑就是读取请求参数,然后向用户服务发起请求,再讲服务端返回的信息以更友好的格式返回给客户端。

封装路由

  1. mkdir -p bootstarp
  2. touch bootstarp/route.go
  1. package bootstarp
  2. import (
  3. "github.com/869413421/micro-service/user-api/routes"
  4. "github.com/gin-gonic/gin"
  5. "sync"
  6. )
  7. var Router *gin.Engine
  8. var once sync.Once
  9. func SetupRoute() *gin.Engine {
  10. once.Do(func() {
  11. Router = gin.New()
  12. routes.RegisterWebRoutes(Router)
  13. })
  14. return Router
  15. }
  1. mkdir routes
  2. touch routes/api.go
  1. package routes
  2. import (
  3. . "github.com/869413421/micro-service/user-api/app/http/controllers"
  4. "github.com/gin-gonic/gin"
  5. )
  6. var userController = NewUserController()
  7. var passwordController = NewPasswordResetController()
  8. // RegisterWebRoutes 注册路由
  9. func RegisterWebRoutes(router *gin.Engine) {
  10. // 用户管理路由,所有路由必须包含user,因为micro网关只会映射路径中包含user的路由
  11. api := router.Group("/")
  12. {
  13. // 登录
  14. api.POST("/user/token", userController.Auth)
  15. // 注册
  16. api.POST("/user", userController.Store)
  17. }
  18. {
  19. // 发起重置密码
  20. api.POST("user/password", passwordController.Store)
  21. // 重置密码
  22. api.PUT("user/password", passwordController.ResetPassword)
  23. }
  24. userApi := api.Group("user")
  25. {
  26. // 用户列表
  27. userApi.GET("", userController.Index)
  28. // 获取单个用户
  29. userApi.GET("/:id", userController.Show)
  30. // 更新用户
  31. userApi.PUT("/:id", userController.Update)
  32. // 删除用户
  33. userApi.DELETE("/:id", userController.Delete)
  34. }
  35. }

修改main.go

  1. ....
  2. // 1.初始化web客户端
  3. g := bootstarp.SetupRoute()
  4. var serviceName = "micro.api.user"
  5. service := web.NewService(
  6. web.Name(serviceName),
  7. web.Address(":81"),
  8. web.Handler(g),
  9. // 指定服务注册信息在注册中心的有效期。 默认为一分种
  10. web.RegisterTTL(time.Minute*2),
  11. // 指定服务主动向注册中心报告健康状态的时间间隔,默认为 30 秒。
  12. web.RegisterInterval(time.Minute*1),
  13. )
  14. ...

使用micro工具集成统一网关

修改docker-compose.yaml

  1. ...
  2. micro-api:
  3. container_name: micro-api
  4. image: micro/micro:v2.9.3
  5. ports:
  6. - 8080:8080
  7. environment:
  8. MICRO_REGISTRY: "etcd"
  9. MICRO_REGISTRY_ADDRESS: "etcd1:2379,etcd2:2379,etcd3:2379"
  10. command: api --handler=http --namespace=micro.api
  11. networks:
  12. - micro-network
  13. ...

启动网关后,网关会扫描注册中心的所有路由并统一对外暴露,不需要我们编写额外的nginx对请求进行转发。
执行docker-compose up -d