How do I use go-zero to implement a Middle Ground System?

[!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR

Author: Jack Luo

Original link:https://www.cnblogs.com/jackluo/p/14148518.html

I recently discovered that a new star microservice framework has emerged in the golang community. It comes from a good future. Just looking at the name, it is very exciting. Before, I only played go-micro. In fact, I have not used it in the project. I think that microservices and grpc are very noble. They have not been in the project yet. I have really played them. I saw that the tools provided by the government are really easy to use. They only need to be defined, and the comfortable file structure is generated. I am concerned about business, and there was a voting activity recently, and in recent years, Middle Ground System have been quite popular, so I decided to give it a try.

SourceCode: https://github.com/jackluo2012/datacenter

Let’s talk about the idea of Middle Ground System architecture first:

Middle Ground System - 图1

The concept of Middle Ground System is probably to unify the apps one by one. I understand it this way anyway.

Let’s talk about user service first. Now a company has a lot of official accounts, small programs, WeChat, Alipay, and xxx xxx, and a lot of platforms. Every time we develop, we always need to provide user login services. Stop copying the code, and then we are thinking about whether we can have a set of independent user services, just tell me you need to send a platform you want to log in (such as WeChat), WeChat login, what is needed is that the client returns one to the server code, and then the server takes this code to WeChat to get user information. Anyway, everyone understands it.

We decided to get all the information into the configuration public service, and then store the appid and appkey of WeChat, Alipay and other platforms, as well as the appid and appkey of payment, and write a set.


Finally, let’s talk about implementation, the whole is a repo:

  • Gateway, we use: go-zero’s Api service
  • Others are services, we use go-zero rpc service

Look at the directory structure

Middle Ground System - 图2

After the whole project was completed, I worked alone and wrote about it for a week, and then I realized the above Middle Ground System.

datacenter-api service

Look at the official document first https://zeromicro.github.io/go-zero/

Let’s set up the gateway first::

  1. blogs mkdir datacenter && cd datacenter
  2. datacenter go mod init datacenter
  3. go: creating new go.mod: module datacenter
  4. datacenter

View the book directory:

  1. datacenter tree
  2. .
  3. └── go.mod
  4. 0 directories, 1 file

Create api file

  1. datacenter goctl api -o datacenter.api
  2. Done.
  3. datacenter tree
  4. .
  5. ├── datacenter.api
  6. └── go.mod

Define api service

Respectively include the above Public Service, User Service, Voting Activity Service

  1. info(
  2. title: "demo"
  3. desc: "demo"
  4. author: "jackluo"
  5. email: "net.webjoy@gmail.com"
  6. )
  7. // Get application information
  8. type Beid struct {
  9. Beid int64 `json:"beid"`
  10. }
  11. type Token struct{
  12. Token string `json:"token"`
  13. }
  14. type WxTicket struct{
  15. Ticket string `json:"ticket"`
  16. }
  17. type Application struct {
  18. Sname string `json:"Sname"`
  19. Logo string `json:"logo"`
  20. Isclose int64 `json:"isclose"`
  21. Fullwebsite string `json:"fullwebsite"`
  22. }
  23. type SnsReq struct{
  24. Beid
  25. Ptyid int64 `json:"ptyid"` // Platform ID
  26. BackUrl string `json:"back_url"` // Return address after login
  27. }
  28. type SnsResp struct{
  29. Beid
  30. Ptyid int64 `json:"ptyid"` // Platform ID
  31. Appid string `json:"appid"` // sns Platform ID
  32. Title string `json:"title"`
  33. LoginUrl string `json:"login_url"` // WeChat login address
  34. }
  35. type WxShareResp struct {
  36. Appid string `json:"appid"`
  37. Timestamp int64 `json:"timestamp"`
  38. Noncestr string `json:"noncestr"`
  39. Signature string `json:"signature"`
  40. }
  41. @server(
  42. group: common
  43. )
  44. service datacenter-api {
  45. @doc(
  46. summary: "Get information about the site"
  47. )
  48. @handler votesVerification
  49. get /MP_verify_NT04cqknJe0em3mT.txt (SnsReq) returns (SnsResp)
  50. @handler appInfo
  51. get /common/appinfo (Beid) returns (Application)
  52. @doc(
  53. summary: "Get social attribute information of the site"
  54. )
  55. @handler snsInfo
  56. post /common/snsinfo (SnsReq) returns (SnsResp)
  57. // Get shared returns
  58. @handler wxTicket
  59. post /common/wx/ticket (SnsReq) returns (WxShareResp)
  60. }
  61. @server(
  62. jwt: Auth
  63. group: common
  64. )
  65. service datacenter-api {
  66. @doc(
  67. summary: "Qiniu upload credentials"
  68. )
  69. @handler qiuniuToken
  70. post /common/qiuniu/token (Beid) returns (Token)
  71. }
  72. // Registration request
  73. type RegisterReq struct {
  74. Mobile string `json:"mobile"`
  75. Password string `json:"password"`
  76. Smscode string `json:"smscode"`
  77. }
  78. // Login request
  79. type LoginReq struct{
  80. Mobile string `json:"mobile"`
  81. Type int64 `json:"type"` // 1. Password login, 2. SMS login
  82. Password string `json:"password"`
  83. }
  84. // WeChat login
  85. type WxLoginReq struct {
  86. Beid int64 `json:"beid"` // Application id
  87. Code string `json:"code"` // WeChat AccesskKey
  88. Ptyid int64 `json:"ptyid"` // Platform ID
  89. }
  90. //Return user information
  91. type UserReply struct {
  92. Auid int64 `json:"auid"`
  93. Uid int64 `json:"uid"`
  94. Beid int64 `json:"beid"` // Platform ID
  95. Ptyid int64 `json:"ptyid"`
  96. Username string `json:"username"`
  97. Mobile string `json:"mobile"`
  98. Nickname string `json:"nickname"`
  99. Openid string `json:"openid"`
  100. Avator string `json:"avator"`
  101. JwtToken
  102. }
  103. type AppUser struct{
  104. Uid int64 `json:"uid"`
  105. Auid int64 `json:"auid"`
  106. Beid int64 `json:"beid"`
  107. Ptyid int64 `json:"ptyid"`
  108. Nickname string `json:"nickname"`
  109. Openid string `json:"openid"`
  110. Avator string `json:"avator"`
  111. }
  112. type LoginAppUser struct{
  113. Uid int64 `json:"uid"`
  114. Auid int64 `json:"auid"`
  115. Beid int64 `json:"beid"`
  116. Ptyid int64 `json:"ptyid"`
  117. Nickname string `json:"nickname"`
  118. Openid string `json:"openid"`
  119. Avator string `json:"avator"`
  120. JwtToken
  121. }
  122. type JwtToken struct {
  123. AccessToken string `json:"access_token,omitempty"`
  124. AccessExpire int64 `json:"access_expire,omitempty"`
  125. RefreshAfter int64 `json:"refresh_after,omitempty"`
  126. }
  127. type UserReq struct{
  128. Auid int64 `json:"auid"`
  129. Uid int64 `json:"uid"`
  130. Beid int64 `json:"beid"`
  131. Ptyid int64 `json:"ptyid"`
  132. }
  133. type Request {
  134. Name string `path:"name,options=you|me"`
  135. }
  136. type Response {
  137. Message string `json:"message"`
  138. }
  139. @server(
  140. group: user
  141. )
  142. service user-api {
  143. @handler ping
  144. post /user/ping ()
  145. @handler register
  146. post /user/register (RegisterReq) returns (UserReply)
  147. @handler login
  148. post /user/login (LoginReq) returns (UserReply)
  149. @handler wxlogin
  150. post /user/wx/login (WxLoginReq) returns (LoginAppUser)
  151. @handler code2Session
  152. get /user/wx/login () returns (LoginAppUser)
  153. }
  154. @server(
  155. jwt: Auth
  156. group: user
  157. middleware: Usercheck
  158. )
  159. service user-api {
  160. @handler userInfo
  161. get /user/dc/info (UserReq) returns (UserReply)
  162. }
  163. type Actid struct {
  164. Actid int64 `json:"actid"`
  165. }
  166. type VoteReq struct {
  167. Aeid int64 `json:"aeid"`
  168. Actid
  169. }
  170. type VoteResp struct {
  171. VoteReq
  172. Votecount int64 `json:"votecount"`
  173. Viewcount int64 `json:"viewcount"`
  174. }
  175. type ActivityResp struct {
  176. Actid int64 `json:"actid"`
  177. Title string `json:"title"`
  178. Descr string `json:"descr"`
  179. StartDate int64 `json:"start_date"`
  180. EnrollDate int64 `json:"enroll_date"`
  181. EndDate int64 `json:"end_date"`
  182. Votecount int64 `json:"votecount"`
  183. Viewcount int64 `json:"viewcount"`
  184. Type int64 `json:"type"`
  185. Num int64 `json:"num"`
  186. }
  187. type EnrollReq struct {
  188. Actid
  189. Name string `json:"name"`
  190. Address string `json:"address"`
  191. Images []string `json:"images"`
  192. Descr string `json:"descr"`
  193. }
  194. type EnrollResp struct {
  195. Actid
  196. Aeid int64 `json:"aeid"`
  197. Name string `json:"name"`
  198. Address string `json:"address"`
  199. Images []string `json:"images"`
  200. Descr string `json:"descr"`
  201. Votecount int64 `json:"votecount"`
  202. Viewcount int64 `json:"viewcount"`
  203. }
  204. @server(
  205. group: votes
  206. )
  207. service votes-api {
  208. @doc(
  209. summary: "Get activity information"
  210. )
  211. @handler activityInfo
  212. get /votes/activity/info (Actid) returns (ActivityResp)
  213. @doc(
  214. summary: "Activity visit +1"
  215. )
  216. @handler activityIcrView
  217. get /votes/activity/view (Actid) returns (ActivityResp)
  218. @doc(
  219. summary: "Get information about registered voting works"
  220. )
  221. @handler enrollInfo
  222. get /votes/enroll/info (VoteReq) returns (EnrollResp)
  223. @doc(
  224. summary: "Get a list of registered works"
  225. )
  226. @handler enrollLists
  227. get /votes/enroll/lists (Actid) returns(EnrollResp)
  228. }
  229. @server(
  230. jwt: Auth
  231. group: votes
  232. middleware: Usercheck
  233. )
  234. service votes-api {
  235. @doc(
  236. summary: "vote"
  237. )
  238. @handler vote
  239. post /votes/vote (VoteReq) returns (VoteResp)
  240. @handler enroll
  241. post /votes/enroll (EnrollReq) returns (EnrollResp)
  242. }

The API and document ideas that are basically written above

Generate datacenter api service

  1. datacenter goctl api go -api datacenter.api -dir .
  2. Done.
  3. datacenter tree
  4. .
  5. ├── datacenter.api
  6. ├── etc
  7. └── datacenter-api.yaml
  8. ├── go.mod
  9. ├── internal
  10. ├── config
  11. └── config.go
  12. ├── handler
  13. ├── common
  14. ├── appinfohandler.go
  15. ├── qiuniutokenhandler.go
  16. ├── snsinfohandler.go
  17. ├── votesverificationhandler.go
  18. └── wxtickethandler.go
  19. ├── routes.go
  20. ├── user
  21. ├── code2sessionhandler.go
  22. ├── loginhandler.go
  23. ├── pinghandler.go
  24. ├── registerhandler.go
  25. ├── userinfohandler.go
  26. └── wxloginhandler.go
  27. └── votes
  28. ├── activityicrviewhandler.go
  29. ├── activityinfohandler.go
  30. ├── enrollhandler.go
  31. ├── enrollinfohandler.go
  32. ├── enrolllistshandler.go
  33. └── votehandler.go
  34. ├── logic
  35. ├── common
  36. ├── appinfologic.go
  37. ├── qiuniutokenlogic.go
  38. ├── snsinfologic.go
  39. ├── votesverificationlogic.go
  40. └── wxticketlogic.go
  41. ├── user
  42. ├── code2sessionlogic.go
  43. ├── loginlogic.go
  44. ├── pinglogic.go
  45. ├── registerlogic.go
  46. ├── userinfologic.go
  47. └── wxloginlogic.go
  48. └── votes
  49. ├── activityicrviewlogic.go
  50. ├── activityinfologic.go
  51. ├── enrollinfologic.go
  52. ├── enrolllistslogic.go
  53. ├── enrolllogic.go
  54. └── votelogic.go
  55. ├── middleware
  56. └── usercheckmiddleware.go
  57. ├── svc
  58. └── servicecontext.go
  59. └── types
  60. └── types.go
  61. └── datacenter.go
  62. 14 directories, 43 files

We open etc/datacenter-api.yaml and add the necessary configuration information

  1. Name: datacenter-api
  2. Log:
  3. Mode: console
  4. Host: 0.0.0.0
  5. Port: 8857
  6. Auth:
  7. AccessSecret: secret
  8. AccessExpire: 86400
  9. CacheRedis:
  10. - Host: 127.0.0.1:6379
  11. Pass: pass
  12. Type: node
  13. UserRpc:
  14. Etcd:
  15. Hosts:
  16. - 127.0.0.1:2379
  17. Key: user.rpc
  18. CommonRpc:
  19. Etcd:
  20. Hosts:
  21. - 127.0.0.1:2379
  22. Key: common.rpc
  23. VotesRpc:
  24. Etcd:
  25. Hosts:
  26. - 127.0.0.1:2379
  27. Key: votes.rpc

I will write the above UserRpc, CommonRpc, and VotesRpc first, and then add them by step.

Let’s write the CommonRpc service first.

CommonRpc service

New project directory

  1. datacenter mkdir -p common/rpc && cd common/rpc

Just create it directly in the datacenter directory, because in common, it may not only provide rpc services in the future, but also api services, so the rpc directory is added

goctl create template

  1. rpc goctl rpc template -o=common.proto
  2. rpc ls
  3. common.proto

Fill in the content:

  1. rpc cat common.proto
  2. syntax = "proto3";
  3. package common;
  4. message BaseAppReq{
  5. int64 beid=1;
  6. }
  7. message BaseAppResp{
  8. int64 beid=1;
  9. string logo=2;
  10. string sname=3;
  11. int64 isclose=4;
  12. string fullwebsite=5;
  13. }
  14. message AppConfigReq {
  15. int64 beid=1;
  16. int64 ptyid=2;
  17. }
  18. message AppConfigResp {
  19. int64 id=1;
  20. int64 beid=2;
  21. int64 ptyid=3;
  22. string appid=4;
  23. string appsecret=5;
  24. string title=6;
  25. }
  26. service Common {
  27. rpc GetAppConfig(AppConfigReq) returns(AppConfigResp);
  28. rpc GetBaseApp(BaseAppReq) returns(BaseAppResp);
  29. }

gotcl generates rpc service

  1. rpc goctl rpc proto -src common.proto -dir .
  2. protoc -I=/Users/jackluo/works/blogs/datacenter/common/rpc common.proto --go_out=plugins=grpc:/Users/jackluo/works/blogs/datacenter/common/rpc/common
  3. Done.
  1. rpc tree
  2. .
  3. ├── common
  4. └── common.pb.go
  5. ├── common.go
  6. ├── common.proto
  7. ├── commonclient
  8. └── common.go
  9. ├── etc
  10. └── common.yaml
  11. └── internal
  12. ├── config
  13. └── config.go
  14. ├── logic
  15. ├── getappconfiglogic.go
  16. └── getbaseapplogic.go
  17. ├── server
  18. └── commonserver.go
  19. └── svc
  20. └── servicecontext.go
  21. 8 directories, 10 files

Basically, all the catalog specifications and structure are generated, so there is no need to worry about the project catalog, how to put it, and how to organize it.

Take a look at the configuration information, which can write mysql and other redis information:

  1. Name: common.rpc
  2. ListenOn: 127.0.0.1:8081
  3. Mysql:
  4. DataSource: root:admin@tcp(127.0.0.1:3306)/datacenter?charset=utf8&parseTime=true&loc=Asia%2FShanghai
  5. CacheRedis:
  6. - Host: 127.0.0.1:6379
  7. Pass:
  8. Type: node
  9. Etcd:
  10. Hosts:
  11. - 127.0.0.1:2379
  12. Key: common.rpc

Let’s add database services:

  1. rpc cd ..
  2. common ls
  3. rpc
  4. common pwd
  5. /Users/jackluo/works/blogs/datacenter/common
  6. common goctl model mysql datasource -url="root:admin@tcp(127.0.0.1:3306)/datacenter" -table="base_app" -dir ./model -c
  7. Done.
  8. common tree
  9. .
  10. ├── model
  11. ├── baseappmodel.go
  12. └── vars.go
  13. └── rpc
  14. ├── common
  15. └── common.pb.go
  16. ├── common.go
  17. ├── common.proto
  18. ├── commonclient
  19. └── common.go
  20. ├── etc
  21. └── common.yaml
  22. └── internal
  23. ├── config
  24. └── config.go
  25. ├── logic
  26. ├── getappconfiglogic.go
  27. └── getbaseapplogic.go
  28. ├── server
  29. └── commonserver.go
  30. └── svc
  31. └── servicecontext.go
  32. 10 directories, 12 files

So the basic rpc is finished, and then we connect the rpc with the model and the api. This official document is already very detailed, here is just the code:

  1. common cat rpc/internal/config/config.go
  2. package config
  3. import (
  4. "github.com/tal-tech/go-zero/core/stores/cache"
  5. "github.com/tal-tech/go-zero/zrpc"
  6. )
  7. type Config struct {
  8. zrpc.RpcServerConf
  9. Mysql struct {
  10. DataSource string
  11. }
  12. CacheRedis cache.ClusterConf
  13. }

Modify in svc:

  1. common cat rpc/internal/svc/servicecontext.go
  2. package svc
  3. import (
  4. "datacenter/common/model"
  5. "datacenter/common/rpc/internal/config"
  6. "github.com/tal-tech/go-zero/core/stores/sqlx"
  7. )
  8. type ServiceContext struct {
  9. c config.Config
  10. AppConfigModel model.AppConfigModel
  11. BaseAppModel model.BaseAppModel
  12. }
  13. func NewServiceContext(c config.Config) *ServiceContext {
  14. conn := sqlx.NewMysql(c.Mysql.DataSource)
  15. apm := model.NewAppConfigModel(conn, c.CacheRedis)
  16. bam := model.NewBaseAppModel(conn, c.CacheRedis)
  17. return &ServiceContext{
  18. c: c,
  19. AppConfigModel: apm,
  20. BaseAppModel: bam,
  21. }
  22. }

The above code has already associated rpc with the model database, we will now associate rpc with api:

  1. datacenter cat internal/config/config.go
  2. package config
  3. import (
  4. "github.com/tal-tech/go-zero/core/stores/cache"
  5. "github.com/tal-tech/go-zero/rest"
  6. "github.com/tal-tech/go-zero/zrpc"
  7. )
  8. type Config struct {
  9. rest.RestConf
  10. Auth struct {
  11. AccessSecret string
  12. AccessExpire int64
  13. }
  14. UserRpc zrpc.RpcClientConf
  15. CommonRpc zrpc.RpcClientConf
  16. VotesRpc zrpc.RpcClientConf
  17. CacheRedis cache.ClusterConf
  18. }

Join the svc service:

  1. datacenter cat internal/svc/servicecontext.go
  2. package svc
  3. import (
  4. "context"
  5. "datacenter/common/rpc/commonclient"
  6. "datacenter/internal/config"
  7. "datacenter/internal/middleware"
  8. "datacenter/shared"
  9. "datacenter/user/rpc/userclient"
  10. "datacenter/votes/rpc/votesclient"
  11. "fmt"
  12. "net/http"
  13. "time"
  14. "github.com/tal-tech/go-zero/core/logx"
  15. "github.com/tal-tech/go-zero/core/stores/cache"
  16. "github.com/tal-tech/go-zero/core/stores/redis"
  17. "github.com/tal-tech/go-zero/core/syncx"
  18. "github.com/tal-tech/go-zero/rest"
  19. "github.com/tal-tech/go-zero/zrpc"
  20. "google.golang.org/grpc"
  21. )
  22. type ServiceContext struct {
  23. Config config.Config
  24. GreetMiddleware1 rest.Middleware
  25. GreetMiddleware2 rest.Middleware
  26. Usercheck rest.Middleware
  27. UserRpc userclient.User //用户
  28. CommonRpc commonclient.Common
  29. VotesRpc votesclient.Votes
  30. Cache cache.Cache
  31. RedisConn *redis.Redis
  32. }
  33. func timeInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
  34. stime := time.Now()
  35. err := invoker(ctx, method, req, reply, cc, opts...)
  36. if err != nil {
  37. return err
  38. }
  39. fmt.Printf("timeout %s: %v\n", method, time.Now().Sub(stime))
  40. return nil
  41. }
  42. func NewServiceContext(c config.Config) *ServiceContext {
  43. ur := userclient.NewUser(zrpc.MustNewClient(c.UserRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor)))
  44. cr := commonclient.NewCommon(zrpc.MustNewClient(c.CommonRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor)))
  45. vr := votesclient.NewVotes(zrpc.MustNewClient(c.VotesRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor)))
  46. //缓存
  47. ca := cache.NewCache(c.CacheRedis, syncx.NewSharedCalls(), cache.NewCacheStat("dc"), shared.ErrNotFound)
  48. rcon := redis.NewRedis(c.CacheRedis[0].Host, c.CacheRedis[0].Type, c.CacheRedis[0].Pass)
  49. return &ServiceContext{
  50. Config: c,
  51. GreetMiddleware1: greetMiddleware1,
  52. GreetMiddleware2: greetMiddleware2,
  53. Usercheck: middleware.NewUserCheckMiddleware().Handle,
  54. UserRpc: ur,
  55. CommonRpc: cr,
  56. VotesRpc: vr,
  57. Cache: ca,
  58. RedisConn: rcon,
  59. }
  60. }

Basically, we can call it in the file directory of logic:

  1. cat internal/logic/common/appinfologic.go
  2. package logic
  3. import (
  4. "context"
  5. "datacenter/internal/svc"
  6. "datacenter/internal/types"
  7. "datacenter/shared"
  8. "datacenter/common/model"
  9. "datacenter/common/rpc/common"
  10. "github.com/tal-tech/go-zero/core/logx"
  11. )
  12. type AppInfoLogic struct {
  13. logx.Logger
  14. ctx context.Context
  15. svcCtx *svc.ServiceContext
  16. }
  17. func NewAppInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) AppInfoLogic {
  18. return AppInfoLogic{
  19. Logger: logx.WithContext(ctx),
  20. ctx: ctx,
  21. svcCtx: svcCtx,
  22. }
  23. }
  24. func (l *AppInfoLogic) AppInfo(req types.Beid) (appconfig *common.BaseAppResp, err error) {
  25. err = l.svcCtx.Cache.GetCache(model.GetcacheBaseAppIdPrefix(req.Beid), appconfig)
  26. if err != nil && err == shared.ErrNotFound {
  27. appconfig, err = l.svcCtx.CommonRpc.GetBaseApp(l.ctx, &common.BaseAppReq{
  28. Beid: req.Beid,
  29. })
  30. if err != nil {
  31. return
  32. }
  33. err = l.svcCtx.Cache.SetCache(model.GetcacheBaseAppIdPrefix(req.Beid), appconfig)
  34. }
  35. return
  36. }

In this way, it is basically connected, and basically there is no need to change the others. UserRPC and VotesRPC are similar, so I won’t write them here.

Reviews

go-zero is really fragrant, because it has a goctl tool, which can automatically generate all the code structure, we will no longer worry about the directory structure, how to organize it, and there is no architectural ability for several years It’s not easy to implement. What are the norms, concurrency, circuit breaker, no use at all, test and filter other, concentrate on realizing the business, like microservices, but also service discovery, a series of things, don’t care, because go-zero has been implemented internally.

I have written code for more than 10 years. The php I have been using before, the more famous ones are laravel and thinkphp, which are basically modular. Realizations like microservices are really costly, but you use go-zero. , You develop as simple as tune api interface, other service discovery, those do not need to pay attention at all, only need to pay attention to the business.

A good language, framework, and their underlying thinking are always high-efficiency and no overtime thinking. I believe that go-zero will improve the efficiency of you and your team or company. The author of go-zero said that they have a team dedicated to organizing the go-zero framework, and the purpose should be obvious, that is, to improve their own development efficiency, process flow, and standardization, which are the criteria for improving work efficiency, as we usually encounter When it comes to a problem or encounter a bug, the first thing I think of is not how to solve my bug, but whether there is a problem with my process, which of my process will cause the bug, and finally I believe in go-zeroCan become the preferred framework for microservice development.

Finally, talk about the pits encountered:

  • grpc

I used grpc for the first time, and then I encountered the problem that the field value is not displayed when some characters are empty:

It is realized by jsonpb in the official library of grpc. The official setting has a structure to realize the conversion of protoc buffer to JSON structure, and can configure the conversion requirements according to the fields.

  • Cross-domain issues

It is set in go-zero, and it feels no effect. The big guy said that it was set through nginx, but later found that it still didn’t work. Recently, I forcibly got a domain name, and I have time to solve it later.

  • sqlx

The sqlx problem of go-zero, this really took a long time:

time.Time is a data structure. Timestamp is used in the database. For example, my field is delete_at. The default database setting is null. When the result is inserted, it reports Incorrect datetime value: '0000-00-00 'for column'deleted_at' at row 1"} This error, the query time reported deleted_at\": unsupported Scan, storing driver.Value type \u003cnil\u003e into type *time.Time"

I removed this field decisively and added the label .omitempty above the field, which seems to be useful too, db:".omitempty"

The second is this Conversion from collation utf8_general_ci into utf8mb4_unicode_ci. The probable reason for this is that I like to use emj expressions now, and my mysql data cannot be recognized.

  • data links

mysql still follows the original way, modify the encoding format of the configuration file, re-create the database, and set the database encoding to utf8mb4, and the sorting rule is utf8mb4_unicode_ci.

In this case, all tables and string fields are in this encoding format. If you don’t want all of them, you can set them separately. This is not the point. Because it is easy to set on Navicat, just click manually

Here comes the important point: Golang uses the github.com/go-sql-driver/mysql driver, which will connect to the dsn of mysql (because I am using gorm, dsn may be different from the native format. Too the same, but it’s okay, just pay attention to charset and collation) root:password@/name?parseTime=True&loc=Local&charset=utf8 is modified to: root:password@/name?parseTime=True&loc=Local&charset=utf8mb4&collation=utf8mb4_unicode_ci