Go-Zero从0到1实现微服务项目开发(二)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 Tair(兼容Redis),内存型 2GB
简介: 继续更新GoZero微服务实战系列文章:上一篇被GoZero作者万总点赞了,本文将继续使用 Go-zero 提供的工具和组件,从零开始逐步构建一个基本的微服务项目。手把手带你完成:项目初始化+需求分析+表结构设计+api+rpc+goctl+apifox调试+细节处理。带你实现一个完整微服务的开发。

前言


书接上回,继续更新GoZero微服务实战系列文章。


上一篇被GoZero作者万总点赞了,更文动力倍增,也建议大家先看巧一篇,欢迎粉丝股东们三连支持一波:Go-zero微服务快速入门和最佳实践(一)


alt

本文将继续使用 Go-zero 提供的工具和组件,从零开始逐步构建一个基本的微服务项目。手把手带你完成:项目初始化+需求分析+表结构设计+api+rpc+goctl+apifox调试+细节处理。带你实现一个完整微服务的开发。


实战前准备


首先需要你在本地安装goctl、protoc、go-zero,goctl安装,按照教程操作即可,非常简单。


下面按顺序和我操作吧,对整体开发流程不清楚的同学务必先看我上一篇文章:GoZero的开发技巧 & 整体开发流程


实战开始


1 | 新建项目(本文使用GoLand)


左上角File-->选择New-->点击Project(如果是第一次使用直接点击New Project即可)


alt

选择新建项目的文件夹以及命名,选择Go的版本(我使用的是Go 1.22.1)


alt

新建文件目录如下


alt

2 | 设计库和表,生成model(本文以文章article表举例,带你实现增删改查基础功能)


数据库表结构设计


alt

快速定位到model目录下执行该命令(右键model-->找到Open In-->点击Terminal)


alt

使用goctl命令生成model(username、passwd、host、port、dbname、tables换成自己的对应的数据)


goctl model mysql datasource -url="${username}:${passwd}@tcp(${host}:${port})/${dbname}" -table="${tables}" -dir="./" -cache=true --style=goZero

一键生成:


alt

3 | 设计api层


在api目录下新建文件:


alt

在article.api中定义文章服务的请求和响应


syntax = "v1"

info (
    title:   "文章服务"
    desc:    "文章服务"
    version: "v1"
)

// 数据库中对应的article表
type Article {
    Id       int64  json:<span class="hljs-string" style="color: #98c379; line-height: 26px;">"id"</span>
    Title    string json:<span class="hljs-string" style="color: #98c379; line-height: 26px;">"title"</span>
    Content  string json:<span class="hljs-string" style="color: #98c379; line-height: 26px;">"content"</span>
}
//---------------------------Req&Resp------------------------------
// 获取文章列表
type (
    GetArticleListReq {
    }

    GetArticleListResp {
        Articles []Article json:<span class="hljs-string" style="color: #98c379; line-height: 26px;">"Articles"</span>
    }
)
// 创建文章
type (
    CreateArticleReq {
        Title   string json:<span class="hljs-string" style="color: #98c379; line-height: 26px;">"title"</span>
        Content string json:<span class="hljs-string" style="color: #98c379; line-height: 26px;">"content"</span>
    }
    CreateArticleResp {
    }
)
// 删除文章
type (
    DeleteArticleReq {
        Id int64 json:<span class="hljs-string" style="color: #98c379; line-height: 26px;">"id"</span>
    }
    DeleteArticleResp {
    }
)
// 修改文章
type (
    UpdateArticleReq {
        Id      int64  json:<span class="hljs-string" style="color: #98c379; line-height: 26px;">"id"</span>
        Title   string json:<span class="hljs-string" style="color: #98c379; line-height: 26px;">"title"</span>
        Content string json:<span class="hljs-string" style="color: #98c379; line-height: 26px;">"content"</span>
    }
    UpdateArticleResp {
    }
)

在main.api中定义文章服务的API


syntax = "v1"

info (
    title:   "文章服务"
    desc:    "文章服务"
    version: "v1"
)

// 导入article.api,直接引用
import (
    "article/article.api"
)

// 服务的相关配置,这里分别是前缀和分组
@server (
    prefix: article/v1
    group:  article
)
service article {
    @doc "获得文章列表"
    @handler getArticles
    post /getArticles (GetArticleListReq) returns (GetArticleListResp)

    @doc "创建文章"
    @handler createArticle
    post /createArticle (CreateArticleReq) returns (CreateArticleResp)

    @doc "删除文章"
    @handler deleteArticle
    post /deleteArticle (DeleteArticleReq) returns (DeleteArticleResp)

    @doc "修改文章"
    @handler updateArticle
    post /updateArticle (UpdateArticleReq) returns (UpdateArticleResp)
}

至此api层已经定义好了,接下来使用goctl代码自动生成


和刚刚一样,右键main.api然后打开Terminal,输入代码


goctl api go -api main.api -dir ../ --style=goZero 

会自动生成etc、internal文件夹以及article.go文件(我习惯把article.go文件改成main.go文件:如果我们后续有多个微服务,执行goctl命令的时候就不用频繁切换文件名称了)


在这里只需要在意几个配置文件(article.yaml, config.go, serviceContext.go)以及需要编写代码的logic目录即可, 暂时先不管, 将rpc层也生成好后一起配置。


4 | 编写rpc层


在rpc下新建pb文件夹, 在pb文件夹里新建article.proto文件


在编写proto文件的时候, 如果是第一次编写的话, 可以使用sql2pb工具自动生成, 一旦我们的proto文件有自定义的修改之后, 就不建议使用这个工具了, 使用方法如下



  1. 安装最新的sql2pb

go install github.com/Mikaelemmmm/sql2pb@latest


  1. 命令示例:

sql2pb -go_package ./pb -host localhost -package pb -password lps123456 -port 3306 -schema zero-demo -service_name article -user root > article.proto

这个命令可以直接将我的zero-demo数据库下所有的表内容都生成到article.proto文件中
这是自动生成的article.proto文件, 你也可以根据自己的需求往里面增加内容


syntax = "proto3";

option go_package ="./pb";

package pb;

// ------------------------------------ 
// Messages
// ------------------------------------ 

//--------------------------------article--------------------------------
message Article {
  int64 id = 1; //id
  string title = 2; //title
  string content = 3; //content
}

message AddArticleReq {
  string title = 1; //title
  string content = 2; //content
}

message AddArticleResp {
}

message UpdateArticleReq {
  int64 id = 1; //id
  string title = 2; //title
  string content = 3; //content
}

message UpdateArticleResp {
}

message DelArticleReq {
  int64 id = 1; //id
}

message DelArticleResp {
}

message GetArticleByIdReq {
  int64 id = 1; //id
}

message GetArticleByIdResp {
  Article article = 1; //article
}

message SearchArticleReq {
  int64 page = 1; //page
  int64 limit = 2; //limit
  int64 id = 3; //id
  string title = 4; //title
  string content = 5; //content
}

message SearchArticleResp {
  repeated Article article = 1; //article
}



// ------------------------------------ 
// Rpc Func
// ------------------------------------ 

service article{ 

  //-----------------------article----------------------- 
  rpc AddArticle(AddArticleReq) returns (AddArticleResp); 
  rpc UpdateArticle(UpdateArticleReq) returns (UpdateArticleResp); 
  rpc DelArticle(DelArticleReq) returns (DelArticleResp); 
  rpc GetArticleById(GetArticleByIdReq) returns (GetArticleByIdResp); 
  rpc SearchArticle(SearchArticleReq) returns (SearchArticleResp); 

}

下一步就是通过proto文件自动生成rpc层其他代码


和之前说的一样,右键article.proto然后打开Terminal,输入代码:


goctl rpc protoc article.proto --go_out=../ --go-grpc_out=../ --zrpc_out=../ --style=goZero

注意: 如果你是Windows电脑,运行后可能会出现一个invalid UTF-8 encoding的问题, 将左下角的文件格式改为UTF-8即可。(更建议你按照 git bash,从根本上解决这个问题。)


alt

运行成功后又会生成好几个文件


alt

这里我同样将article.go改成了main.go


5 | 配置api层和rpc层


OK,现在没有能自动生成的代码了, 需要自己手动敲代码实现业务逻辑了。


配置api层


打开api/etc/article.yaml文件, 写入以下代码:


Name: article-api #服务名称
Host: 127.0.0.1 #监听地址
Port: 1001 #监听端口
Mode: dev #运行模式
# 配置MySQL Redis
DB:
  DataSource: root:lps123456@tcp(127.0.0.1:3306)/zero-demo?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
Cache:
  - Host: 127.0.0.1:6379
    Pass:
# 配置rpc客户端, 后面需要调用rpc中的方法
ArticleRpcConf:
  Endpoints:
    - 127.0.0.1:2001
  NonBlock: true

接下来是api/internal/config/config.go文件


package config

import (
   "github.com/zeromicro/go-zero/core/stores/cache"
   "github.com/zeromicro/go-zero/rest"
   "github.com/zeromicro/go-zero/zrpc"
)
type Config struct {
   rest.RestConf
   DB struct {
      DataSource string
   }
   Cache cache.CacheConf
   ArticleRpcConf zrpc.RpcClientConf
}

最后是api/internal/svc/serviceContext.go文件


package svc

import (
   "GoZeroDemo/app/article/cmd/api/internal/config"
   "GoZeroDemo/app/article/cmd/rpc/article"
   "github.com/zeromicro/go-zero/zrpc"
)

type ServiceContext struct {
   Config  config.Config
   ArticleRpc article.ArticleZrpcClient
}

func NewServiceContext(c config.Config) ServiceContext {
   return &ServiceContext{
      Config:  c,
      ArticleRpc: article.NewArticleZrpcClient(zrpc.MustNewClient(c.ArticleRpcConf)),
   }
}

api层的服务就配置好了


配置rpc层


打开rpc/etc/article.yaml文件, 写入以下代码:


Name: article-rpc #服务名称
ListenOn: 127.0.0.1:2001 #监听地址
Mode: dev #运行模式
# 配置Redis
Redis:
  Host: 127.0.0.1:6379
  Type: node
  Pass:
  Key: article-rpc
# 配置MySQL
DB:
  DataSource: root:lps123456@tcp(127.0.0.1:3306)/zero-demo?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
Cache:
  - Host: 127.0.0.1:6379
    Pass:

接下来是rpc/internal/config/config.go文件


package config

import (
   "github.com/zeromicro/go-zero/core/stores/cache"
   "github.com/zeromicro/go-zero/zrpc"
)

type Config struct {
   zrpc.RpcServerConf
   DB struct {
      DataSource string
   }
   Cache cache.CacheConf
}

最后是rpc/internal/svc/serviceContext.go文件


package svc

import (
   "GoZeroDemo/app/article/cmd/rpc/internal/config"
   "GoZeroDemo/app/article/model"
   "github.com/zeromicro/go-zero/core/stores/redis"
   "github.com/zeromicro/go-zero/core/stores/sqlx"
)

type ServiceContext struct {
   Config       config.Config
   RedisClient  
redis.Redis
   ArticleModel model.ArticleModel
}

func NewServiceContext(c config.Config) ServiceContext {
   return &ServiceContext{
      Config:       c,
      ArticleModel: model.NewArticleModel(sqlx.NewMysql(c.DB.DataSource), c.Cache),
   }
}

至此都配置好了, 接下来就是编写业务逻辑代码


6 | 编写api层和rpc层下logic代码


alt

这里我就拿增加文章做示例


首先编写api下的logic中的createArticleLogic.go文件:


package article

import (
   "GoZeroDemo/app/article/cmd/rpc/article"
   "context"

   "GoZeroDemo/app/article/cmd/api/internal/svc"
   "GoZeroDemo/app/article/cmd/api/internal/types"

   "github.com/zeromicro/go-zero/core/logx"
)

type CreateArticleLogic struct {
   logx.Logger
   ctx    context.Context
   svcCtx 
svc.ServiceContext
}

func NewCreateArticleLogic(ctx context.Context, svcCtx svc.ServiceContext) CreateArticleLogic {
   return &CreateArticleLogic{
      Logger: logx.WithContext(ctx),
      ctx:    ctx,
      svcCtx: svcCtx,
   }
}

func (l CreateArticleLogic) CreateArticle(req types.CreateArticleReq) (resp types.CreateArticleResp, err error) {
   // 这里就是调用rpc下的AddArticle方法 
   _, err = l.svcCtx.ArticleRpc.AddArticle(l.ctx, &article.AddArticleReq{
      Title:   req.Title,
      Content: req.Content,
   })
   if err != nil {
      return nil, err
   }
   return

   return
}

接下来完成rpc中的AddArticle方法, 编写addArticleLogic.go文件:


package logic

import (
   "GoZeroDemo/app/article/cmd/rpc/internal/svc"
   "GoZeroDemo/app/article/cmd/rpc/pb"
   "GoZeroDemo/app/article/model"
   "context"

   "github.com/zeromicro/go-zero/core/logx"
)

type AddArticleLogic struct {
   ctx    context.Context
   svcCtx 
svc.ServiceContext
   logx.Logger
}

func NewAddArticleLogic(ctx context.Context, svcCtx svc.ServiceContext) AddArticleLogic {
   return &AddArticleLogic{
      ctx:    ctx,
      svcCtx: svcCtx,
      Logger: logx.WithContext(ctx),
   }
}

// -----------------------article-----------------------
func (l AddArticleLogic) AddArticle(in pb.AddArticleReq) (*pb.AddArticleResp, error) {
   article := new(model.Article)
   article.Title = in.Title
   article.Content = in.Content
   // 调用model层的方法
   _, err := l.svcCtx.ArticleModel.Insert(l.ctx, article)
   if err != nil {
      return nil, err
   }
   return &pb.AddArticleResp{}, nil
}

接下来就可以进行测试了


7 | 自动生成接口文档并测试(使用Apifox)



  1. 使用swagg生成json文件
    下载goctl-swagger, 确认安装是否成功:

go get -u github.com/zeromicro/goctl-swagger
goctl-swagger -v

在main.api下打开Terminal, 输入以下代码:


goctl api plugin -plugin goctl-swagger="swagger -filename main.json" -api main.api -dir .

不出意外就会生成一个main.json文件


alt

  1. 将json文件导入到Apifox中
    打开Apifox, 新建接口项目, 点击这里导入json文件

alt

将json文件拖进去即可


完成之后-->点击这里-->进入选择开发环境-->进行一个端口的配置


alt
alt

这里的地址就是我对应的api服务的地址(yaml文件中配置)
3. 启动程序后, 进行测试
还记得刚刚我改名的main.go文件吗,一个在api层,一个在rpc层,现在分别运行它们


alt

将它们同时在控制台Terminal打开,分别运行命令go run main.go


alt
alt

可以看到它们监听了两个端口,一个1001一个2001
接下来就可以在Apifox中进行测试了
从左至右分别点击这三个地方(数据可以自动生成也可以自己写)


alt

然后发现返回null和200(因为我没有定义规范的返回给前端的字段,所以返回null代表正常)
4. 查询数据库,确保数据生成
最后查看数据库中是否出现了这条记录


alt

可以看到记录新增成功, 圆满完成!


总结


这篇文章分享了如何使用gozero开发文章服务的增加功能,强烈建议你跟着我的步骤操练一遍,也可以尝试用相同的思路实现删除、查询功能。


我将继续更新Go-Zero系列文章,如果你对Go语言或者微服务感兴趣,欢迎关注我的公众号,也欢迎直接私信我。


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
29天前
|
JavaScript Java Go
探索Go语言在微服务架构中的优势
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出。本文将深入探讨Go语言在构建微服务时的性能优势,包括其在内存管理、网络编程、并发模型以及工具链支持方面的特点。通过对比其他流行语言,我们将揭示Go语言如何成为微服务架构中的一股清流。
118 53
|
3月前
|
Go API Docker
热门go与微服务15
热门go与微服务15
33 2
|
26天前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
27天前
|
Go 数据处理 API
Go语言在微服务架构中的应用与优势
本文摘要采用问答形式,以期提供更直接的信息获取方式。 Q1: 为什么选择Go语言进行微服务开发? A1: Go语言的并发模型、简洁的语法和高效的编译速度使其成为微服务架构的理想选择。 Q2: Go语言在微服务架构中有哪些优势? A2: 主要优势包括高性能、高并发处理能力、简洁的代码和强大的标准库。 Q3: 文章将如何展示Go语言在微服务中的应用? A3: 通过对比其他语言和展示Go语言在实际项目中的应用案例,来说明其在微服务架构中的优势。
|
2月前
|
Cloud Native Go API
Go语言在微服务架构中的创新应用与实践
本文深入探讨了Go语言在构建高效、可扩展的微服务架构中的应用。Go语言以其轻量级协程(goroutine)和强大的并发处理能力,成为微服务开发的首选语言之一。通过实际案例分析,本文展示了如何利用Go语言的特性优化微服务的设计与实现,提高系统的响应速度和稳定性。文章还讨论了Go语言在微服务生态中的角色,以及面临的挑战和未来发展趋势。
|
2月前
|
运维 Go 开发者
Go语言在微服务架构中的应用与优势
本文深入探讨了Go语言在构建微服务架构中的独特优势和实际应用。通过分析Go语言的核心特性,如简洁的语法、高效的并发处理能力以及强大的标准库支持,我们揭示了为何Go成为开发高性能微服务的首选语言。文章还详细介绍了Go语言在微服务架构中的几个关键应用场景,包括服务间通信、容器化部署和自动化运维等,旨在为读者提供实用的技术指导和启发。
|
2月前
|
负载均衡 Go API
探索Go语言在微服务架构中的应用与优势
在这篇技术性文章中,我们将深入探讨Go语言(又称为Golang)在构建微服务架构时的独特优势。文章将通过对比分析Go语言与其他主流编程语言,展示Go在并发处理、性能优化、以及开发效率上的优势。同时,我们将通过一个实际的微服务案例,详细说明如何利用Go语言构建高效、可扩展的微服务系统。
|
2月前
|
安全 Go 云计算
探索Go语言在微服务架构中的应用与优势
在本文中,我们将深入探讨Go语言(又称为Golang)在构建微服务架构中的独特优势。文章将分析Go语言的并发模型、简洁的语法以及高效的编译速度,以及这些特性如何使其成为微服务架构的理想选择。我们将通过一个简单的微服务示例,展示Go语言在实际开发中的表现,并讨论其在性能和可维护性方面的优势。
|
2月前
|
消息中间件 监控 Go
Go语言在微服务架构中的优势与实践
【10月更文挑战第10天】Go语言在微服务架构中的优势与实践
|
2月前
|
Java Go 数据库
Go语言在微服务架构中的优势与实践
【10月更文挑战第10天】Go语言在微服务架构中的优势与实践