go-zero 微服务 快速入门

867 阅读5分钟

快速开发-微服务

由于go-zero的中文文档与最新版本的go-zero的一些部分有冲突,在磕磕绊绊踩了两天坑后终于算是跑起来了快速入门的例子。下面是我在学习过程中对文档不正确的地方修改后的笔记。

开发准备

配置Go Module

Go Module是Golang管理依赖性的方式

这些应该在刚开始go的时候就已经配好了。

查看GO111MODULE开启情况:

go env GO111MODULE

如果为on,则跳过下一步。否则go env -w GO111MODULE="on"

设置GOPROXY:

go env -w GOPROXY=goproxy.cn

设置GOMODCACHE:

go env GOMODCACHE 如果目录不为空或者/dev/null,跳过下一步

go env -w GOMODCACHE=$GOPATH/pkg/mod

安装go-zero:

go get -u github.com/zeromicro/go-zero

安装goctl工具:

goctl是go-zero微服务框架下的代码生成工具。

go install github.com/zeromicro/go-zero/tools/goctl@latest

能不用go get就不用吧,go get下载的goctl可能会出现"goctl : 无法将“goctl”项识别为 cmdlet、函数.."的问题。

安装protoc&protoc-gen-go:

为了实现rpc通信。

protoc是一款用C++编写的工具,其可以将proto文件翻译为指定语言的代码。在go-zero的微服务中,我们采用grpc进行服务间的通信,而grpc的编写就需要用到protoc和翻译成go语言rpc stub代码的插件protoc-gen-go。

goctl env check -i -f --verbose

congratulations! your goctl environment is ready! 说明全部下载成功。

下载失败请尝试挂梯子。

选择性环境

前面的是开发阶段必须要准备的环境。后面的环境可以选择性安装,因为他们一般存在于服务器。

etcd,redis,mysql

api配置

Config

type Config struct{
    rest.RestConf // rest api配置
    Auth struct { // jwt鉴权配置
        AccessSecret string // jwt密钥
        AccessExpire int64 // 有效期,单位:秒
    }
    Mysql struct { // 数据库配置,除mysql外,可能还有mongo等其他数据库
        DataSource string // mysql链接地址,满足 $user:$password@tcp($ip:$port)/$db?$queries 格式即可
    }
    CacheRedis cache.CacheConf // redis缓存
    UserRpc    zrpc.RpcClientConf // rpc client配置
}    

rest.RestConf

api服务基础配置,包含监听地址,监听端口,证书配置,限流,熔断参数,超时参数等控制。

go-zero.dev/cn/docs/con…

rpc配置

rpc配置控制着一个rpc服务的各种功能,包含但不限于监听地址,etcd配置,超时,熔断配置等。

go-zero.dev/cn/docs/con…

快速开始 --微服务

单体服务与微服务区别在于有没有服务间通信。

一个订单服务,一个用户服务

订单是直接面向用户,通过http协议访问数据,而订单内部需要获取用户的一些基础数据,既然我们的服务是采用微服务的架构设计, 那么两个服务(user, order)就必须要进行数据交换,服务间的数据交换即服务间的通讯,到了这里,采用合理的通讯协议也是一个开发人员需要 考虑的事情,可以通过http,rpc等方式来进行通讯,这里我们选择rpc来实现服务间的通讯。

从上文得知,我们需要一个

  • user rpc
  • order api

创建user rpc服务

在项目根目录下创建user/rpc

添加user.proto文件,增加getUser方法

syntax = "proto3";

package user;

// protoc-gen-go 版本大于1.4.0, proto文件需要加上go_package,否则无法生成
option go_package = "./user";

message IdRequest {
    string id = 1;
}

message UserResponse {
    // 用户id
    string id = 1;
    // 用户名称
    string name = 2;
    // 用户性别
    string gender = 3;
}

service User {
    rpc getUser(IdRequest) returns(UserResponse);
}

写好user.proto文件后,在同级目录下(user/rpc)

执行生成代码的指令:

goctl rpc protoc user.proto --go_out=./types --go-grpc_out=./types --zrpc_out=.

如图中的getuserlogic.go就是根据proto自动生成的代码,需要在GetUser部分添加业务逻辑代码。

注意! 在生成代码后,可能需要手动修改etc/user.yaml文件中的部分内容。将在后续提到。 (这是与文档不一致的地方之一)

创建order api服务

在douyinbackend/douyinbackend目录下,创建order文件夹,并创建api子文件夹。api子文件夹中创建order.api:

type(
    OrderReq {
        Id string `path:"id"`
    }
  
    OrderReply {
        Id string `json:"id"`
        Name string `json:"name"`
    }
)
service order {
    @handler getOrder
    get /api/order/get/:id (OrderReq) returns (OrderReply)
}

然后生成order服务

goctl api go -api order.api -dir .

添加user rpc配置,注意,这一步仍然是在order目录下进行的

在上面生成的order下的internal/config/config.go中:

package config

import (
    "github.com/zeromicro/go-zero/rest"
    "github.com/zeromicro/go-zero/zrpc"
)

type Config struct {
    rest.RestConf
    UserRpc zrpc.RpcClientConf
}

添加yaml配置:

在order目录下的etc/order.yaml中,写入:

Name: order
Host: 0.0.0.0
Port: 8081
UserRpc:
  Etcd:
    Hosts:
    - 127.0.0.1:2379
    Key: user.rpc

要注意第七行 - 与 ip地址之间有一空格!!否则在启动order时,会报错:

2023/01/24 23:57:07 error: config file etc/order.yaml, string: `-127.0.0.1:2379`, error: 
`json: cannot unmarshal number into Go value of type []interface {}`
exit status 1

注意! 这里的Host与Port就是rpc服务的host和端口号,一定要保持一致!

完善服务依赖: 在order目录下的internal/svc/servicecontext.go中:

其中第6行,第11行需要手动添加 (好像) 注意! 这里导入的是user/rpc/userclient,而不是中文文档中的user/rpc/user

package svc

import (
    "douyinbackend/douyinbackend/order/api/internal/config"
    "douyinbackend/douyinbackend/user/rpc/userclient"
    "github.com/zeromicro/go-zero/zrpc"
)

type ServiceContext struct {
    Config  config.Config
    UserRpc userclient.User
}

func NewServiceContext(c config.Config) *ServiceContext {
    return &ServiceContext{
       Config:  c,
       UserRpc: userclient.NewUser(zrpc.MustNewClient(c.UserRpc)),
   }
}

添加order业务逻辑,同样在order目录下的internal/logic/getorderlogic.go.

package logic

import (
    "context"
    "errors"

    "go-zero-demo/mall/order/api/internal/svc"
    "go-zero-demo/mall/order/api/internal/types"
    "go-zero-demo/mall/user/rpc/types/user"

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

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

func NewGetOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) GetOrderLogic {
    return GetOrderLogic{
        Logger: logx.WithContext(ctx),
        ctx:    ctx,
        svcCtx: svcCtx,
    }
}

func (l *GetOrderLogic) GetOrder(req *types.OrderReq) (*types.OrderReply, error) {
    user, err := l.svcCtx.UserRpc.GetUser(l.ctx, &user.IdRequest{
        Id: "1",
    })
    if err != nil {
        return nil, err
    }

    if user.Name != "test" {
        return nil, errors.New("用户不存在")
    }

    return &types.OrderReply{
        Id:   req.Id,
        Name: "test order",
    }, nil
}

启动服务并验证

etcd

windows下etcd下载:

github.com/etcd-io/etc…

选择etcd-v3.5.7-windows-amd64.zip

下载解压后,添加到系统环境变量:在高级系统设置中打开编辑环境变量的窗口,找到Path,新建,添加解压后的地址。如:D:\etcd-v3.5.7-windows-amd64\。 记得添加后所有窗口全部确认。使用etcd -version 检查是否成功。
终端 etcd 启动etcd

在douyinbackend目录下(项目根目录) 下载依赖: go mod tidy

启动user rpc:

在user/rpc目录下, go run user.go -f etc/user.yaml

启动order api:

在order/api目录下,go run order.go -f etc/order.yaml

如果启动etcd有问题,检查是否加入到了环境变量中。

如果启动user rpc有问题,检查端口是否被占用。示例中的端口为8080,可以在命令行中使用netstat -aon|findstr "8080",检查是否被占用。

如果被占用,可以在user/rpc/etc/user.yaml中,修改ListeOn如下:

修改后重新开启rpc服务。

如果启动order api有问题,如果报错:

`json: cannot unmarshal number into Go value of type []interface {}`
exit status 1

则检查order的order.yaml文件中 - 是否与Ip之间有空格。

如果报错:

error: context deadline exceeded, make sure rpc service "add.rpc" is alread started

则表示没有找到rpc服务,检查order.yaml的Host和Port是否与user.yaml的

ListenOn一致。

访问order api:

可以使用接口测试工具调试。

推荐文档:go-zero.dev/cn/docs 这个要新一点点