前言

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

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

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

实战前准备

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

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

实战开始

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

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

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

新建文件目录如下

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

数据库表结构设计

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

使用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

一键生成:

3 | 设计api层

在api目录下新建文件:

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

syntax = "v1"

info (
title: "文章服务"
desc: "文章服务"
version: "v1"
) // 数据库中对应的article表
type Article {
Id int64 `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
}
//---------------------------Req&Resp------------------------------
// 获取文章列表
type (
GetArticleListReq {
} GetArticleListResp {
Articles []Article `json:"Articles"`
}
)
// 创建文章
type (
CreateArticleReq {
Title string `json:"title"`
Content string `json:"content"`
}
CreateArticleResp {
}
)
// 删除文章
type (
DeleteArticleReq {
Id int64 `json:"id"`
}
DeleteArticleResp {
}
)
// 修改文章
type (
UpdateArticleReq {
Id int64 `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
}
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,从根本上解决这个问题。)

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

这里我同样将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代码

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

首先编写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文件

  1. 将json文件导入到Apifox中

    打开Apifox, 新建接口项目, 点击这里导入json文件

将json文件拖进去即可

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

这里的地址就是我对应的api服务的地址(yaml文件中配置)

3. 启动程序后, 进行测试

还记得刚刚我改名的main.go文件吗,一个在api层,一个在rpc层,现在分别运行它们

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

可以看到它们监听了两个端口,一个1001一个2001

接下来就可以在Apifox中进行测试了

从左至右分别点击这三个地方(数据可以自动生成也可以自己写)

然后发现返回null和200(因为我没有定义规范的返回给前端的字段,所以返回null代表正常)

4. 查询数据库,确保数据生成

最后查看数据库中是否出现了这条记录

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

总结

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

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

Go-Zero从0到1实现微服务项目开发(二)的更多相关文章

  1. 微服务项目开发学成在线_day02 CMS前端开发

    1 Vue.js与Webpack研究 开发版的浏览器:https://www.google.cn/intl/zh-CN/chrome/dev/ 前端的开发框架:微服务项目开发学成在线_Vue.js与W ...

  2. 微服务项目开发学成在线_day01_CMS服务端开发

    05-CMS需求分析-什么是CMS 什么是CMS?CMS (Content Management System)即内容管理系统,不同的项目对CMS的定位不同.CMS有哪些类型? 每个公司对每个项目的C ...

  3. 微服务项目开发学成在线_day03 CMS页面管理开发

    springboot引入mangodb依赖坐标:在spring-boot集成条件下,使用mongodb的DAO层开发. swagger查看接口文档,请求地址:http://localhost:3100 ...

  4. 微服务项目开发学成在线_Vue.js与Webpack

    Vue.js 1.Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架.自底向上逐层应用:作为渐进式框架要实现的目标就是方便项目增量开发. 渐进式框架:Progress ...

  5. springboot的maven多模块项目架构微服务搭建——依赖方式的多模块演化为微服务项目

    在上一篇依赖方式多模块的基础上对项目进行改造.主要改造user-service项目,service要配置mapper.mybatis及数据库相关的东西,后面的接口消费方user就不再需要了 注意:以下 ...

  6. (3)go-micro微服务项目搭建

    目录 一 微服务项目介绍 二 go-micro安装 1.拉取micro镜像 2.生成项目目录 三 项目搭建 使用DDD模式开发项目: 四 最后 一 微服务项目介绍 账户功能是每一个系统都绕不开的一部分 ...

  7. 【SFA官方翻译】使用 Kubernetes、Spring Boot 2.0 和 Docker 的微服务快速指南

    [SFA官方翻译]使用 Kubernetes.Spring Boot 2.0 和 Docker 的微服务快速指南 原创: Darren Luo SpringForAll社区 今天 原文链接:https ...

  8. 【spring colud】spring cloud微服务项目搭建【spring boot2.0】

    spring cloud微服务项目搭建 =================================== 示例版本: 1.spring boot 2.0版本 2.开发工具 IntellJ IDE ...

  9. 【版本号公布】Jeecg-P3 1.0 公布,J2EE微服务框架(插件开发)

    JEECG-P3 1.0 公布了! JEECG-P3 1.0是一个J2EE微服务框架(插件开发). 特点:业务组件以JAR方式提供,插件模式.松耦合.可插拔.支持独立部署,也能够无缝集成Jeecg平台 ...

  10. 「 从0到1学习微服务SpringCloud 」10 服务网关Zuul

    系列文章(更新ing): 「 从0到1学习微服务SpringCloud 」06 统一配置中心Spring Cloud Config 「 从0到1学习微服务SpringCloud 」07 RabbitM ...

随机推荐

  1. 一秒变身艺术家!U2Net 跨界肖像画,让你的头像瞬间细节完美复刻,打造个性化头像新风潮!

    效果 测试图片来自网络,如有侵权,联系删除. 项目 关注微信公众号,回复关键字:"一秒变身艺术家",获取程序! 模型信息 Inputs ---------------------- ...

  2. 摄像头网页预览,不需安装插件,支持Chrome

    背景 实在是不想折腾ActiveX控件 1.麻烦(开发麻烦.使用时设置也麻烦) 2.非IE浏览器不兼容 解决方案 写一个摄像头服务,提供http服务,返回摄像头当前画面的Base64字符串,前端页面调 ...

  3. KingbaseES V8R6 集群运维系列 -- 命令行部署repmgr管理集群+switchover测试

    本次部署未使用securecmd/kbha工具,无需普通用户到root用户的互信. 一.环境准备 1.创建OS用户 建立系统数据库安装用户组及用户,在所有的节点执行. root用户登陆服务器,创建用户 ...

  4. MemfireCloud让静态托管页面动起来!

    静态托管 我们最常接触到的静态托管是github pages,它的常见工作模式是在github上创建一个仓库,使用hexo类的工具初始化仓库,编写markdown文件,生成静态页面,推送到github ...

  5. Python字典遍历

    1 def dict_test(): 2 #初始化字典 3 dict= {"a1":"1","a2":"2"," ...

  6. Make It Equal 题解

    Problem Link 简要题意 翻译很清楚. 思路 提供一种简单直接的思路. 可以发现最多会操作 \(n\) 次. 那么就可以每次直接枚举切的高度 \(h\),检查更改是否超过 \(k\),之后暴 ...

  7. https安全性 带给im 消息加密的启发

    大家好,我是蓝胖子,在之前# MYSQL 是如何保证binlog 和redo log同时提交的?这篇文章里,我们可以从mysql的设计中学会如何让两个服务的调用逻辑达到最终一致性,这也是分布式事务实现 ...

  8. 如何拿到接口返回的消耗token

    SemanticKernel 以下引用自官方案例 Text模型 使用Kernel FunctionResult functionResult = await kernel.InvokePromptAs ...

  9. #根号分治,动态规划#洛谷 5616 [MtOI2019]恶魔之树

    题目传送门 分析 最小公倍数最终一定会被表示成若干个质数指数幂的情况(1的情况就直接乘上二的次幂) 然后每个数的加入相当于对每个质数的指数取最大值,但是如果将每个质数的次数都表示出来状态数很多, 考虑 ...

  10. #凸包,闵可夫斯基和#CF87E Mogohu-Rea Idol

    题目 按逆时针顺序给出三个凸包点集 \(\mathbb{A,B,C}\),每次查询给出点 \(D\), 问是否存在点 \(A\in\mathbb{A},B\in\mathbb{B},C\in\math ...