热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

关于前端:开箱即用的微服务框架-Gozero进阶篇

之前咱们简略介绍过Go-zero详见《Go-zero:开箱即用的微服务框架》。这次咱们从入手实现一个Blog我的项目的用户模块登程,具体讲述Go-zero的应用。

之前咱们简略介绍过 Go-zero 详见《Go-zero:开箱即用的微服务框架》。这次咱们从入手实现一个 Blog 我的项目的用户模块登程,具体讲述 Go-zero 的应用。

特地阐明本文波及的所有材料都已上传 Github 仓库 “kougazhang/go-zero-demo”, 感兴趣的同学能够自行下载。

Go-zero 实战我的项目:blog

本文以 blog 的网站后盾为例,着重介绍一下如何应用 Go-zero 开发 blog 的用户模块。

用户模块是后盾管理系统常见的模块,它的性能大家也十分相熟。治理用户波及到前端操作,用户信息长久化又离不开数据库。所以用户模块堪称是 “麻雀虽小五脏俱全”。本文将具体介绍一下如何应用 go-zero 实现用户模块性能,如:用户登录、增加用户、删除用户、批改用户、查问用户 等(残缺的 Api 文档请参考仓库代码)

Blog 整体架构

最下面是 api 网关层。go-zero 须要 api 网关层来代理申请,把 request 通过 gRPC 转发给对应的 rpc 服务去解决。这块把具体申请转发到对应的 rpc 服务的业务逻辑,须要手写。

接下来是 rpc 服务层。上图 rpc 服务中的 user 就是接下来向大家演示的模块。每个 rpc 服务能够独自部署。服务启动后会把相干信息注册到 ETCD,这样 api 网关层就能够通过 ECTD 发现具体服务的地址。rpc 服务解决具体申请的业务逻辑,须要手写。

最初是Model 层。model 层封装的是数据库操作的相干逻辑。如果是查问类的相干操作,会先查问 redis 中是否有对应的缓存。非查问类操作,则会间接操作 MySQL。goctl 能通过 sql 文件生成一般的 CRDU 代码。上文也有提到,目前 goctl 这部分性能只反对 MySQL。

上面演示如何应用 go-zero 开发一个 blog 零碎的用户模块。

api 网关层

编写 blog.api 文件

  • 生成 blog.api 文件

执行命令 goctl api -o blog.api,创立 blog.api 文件。

  • api 文件的作用

api 文件的具体语法请参阅文档[https://go-zero.dev/cn/api-gr…],本文依照集体了解谈一谈 api 文件的作用和根底语法。

api 文件是用来生成 api 网关层的相干代码的。

  • api 文件的语法

api 文件的语法和 Golang 语言十分相似,type 关键字用来定义构造体,service 局部用来定义 api 服务。

type 定义的构造体,次要是用来申明申请的入参和返回值的,即 request 和 response.

service 定义的 api 服务,则声明了路由,handler,request 和 response.

具体内容请联合上面的默认的生成的 api 文件进行了解。

// 申明版本,可疏忽
syntax = "v1"

// 申明一些我的项目信息,可疏忽
info(
   title: // TODO: add title
   desc: // TODO: add description
   author: "zhao.zhang"
   email: "zhao.zhang@upai.com"
)

// 重要配置
// request 是构造体的名称,能够应用 type 关键词定义新的构造体
type request {
   // TODO: add members here and delete this comment
   // 与 golang 语言统一,这里申明构造体的成员
}

// 语法同上,只是业务含意不同。response 个别用来申明返回值。
type response {
   // TODO: add members here and delete this comment
}

// 重要配置
// blog-api 是 service 的名称.
service blog-api {
   // GetUser 是解决申请的视图函数
   @handler GetUser // TODO: set handler name and delete this comment
   // get 申明了该申请应用 GET 办法
   // /users/id/:userId 是 url,:userId 表明是一个变量
   // request 就是下面 type 定义的那个 request, 是该申请的入参
   // response 就是下面 type 定义的那个 response, 是该申请的返回值。
   get /users/id/:userId(request) returns(response)

   @handler CreateUser // TODO: set handler name and delete this comment
   post /users/create(request)
}
  • 编写 blog.api 文件

鉴于文章篇幅思考残缺的 blog.api 文件请参考 gitee 上的仓库。上面生成的代码是依照仓库上的 blog.api 文件生成的。

api 相干代码

  • 生成相干的代码

执行命令 goctl api go -api blog.api -dir . ,生成 api 相干代码。

  • 目录介绍

├── blog.api # api 文件
├── blog.go # 程序入口文件
├── etc
│   └── blog-api.yaml # api 网关层配置文件
├── go.mod
├── go.sum
└── internal
    ├── config
    │   └── config.go # 配置文件
    ├── handler # 视图函数层, handler 文件与上面的 logic 文件一一对应
    │   ├── adduserhandler.go
    │   ├── deleteuserhandler.go
    │   ├── getusershandler.go
    │   ├── loginhandler.go
    │   ├── routes.go
    │   └── updateuserhandler.go
    ├── logic # 须要手动填充代码的中央
    │   ├── adduserlogic.go
    │   ├── deleteuserlogic.go
    │   ├── getuserslogic.go
    │   ├── loginlogic.go
    │   └── updateuserlogic.go
    ├── svc # 封装 rpc 对象的中央,前面会将
    │   └── servicecontext.go
    └── types # 把 blog.api 中定义的构造体映射为真正的 golang 构造体
        └── types.go
  • 文件间的调用关系

因为到此时还没波及到 rpc 服务,所以 api 内各模块的调用关系就是非常简单的单体利用间的调用关系。routers.go 是路由,依据 request Method 和 url 把申请散发到对应到的 handler 上,handler 外部会去调用对应的 logic. logic 文件内是咱们注入代码逻辑的中央。

小结

Api 层相干命令:

  • 执行命令 goctl api -o blog.api, 创立 blog.api 文件。
  • 执行命令 goctl api go -api blog.api -dir . ,生成 api 相干代码。
  • 加参数 goctl 也能够生成其余语言的 api 层的文件,比方 java、ts 等,尝试之后发现很难用,所以不开展了。

rpc 服务

编写 proto 文件

  • 生成 user.proto 文件

应用命令 goctl rpc template -o user.proto, 生成 user.proto 文件

  • user.proto 文件的作用

user.proto 的作用是用来生成 rpc 服务的相干代码。

protobuf 的语法曾经超出了 go-zero 的领域了,这里就不具体开展了。

  • 编写 user.proto 文件

鉴于文章篇幅思考残缺的 user.proto 文件请参考 gitee 上的仓库。

生成 rpc 相干代码

  • 生成 user rpc 服务相干代码

应用命令 goctl rpc proto -src user.proto -dir . 生成 user rpc 服务的代码。

小结

rpc 服务相干命令:

  • 应用命令 goctl rpc template -o user.proto, 生成 user.proto 文件
  • 应用命令 goctl rpc proto -src user.proto -dir . 生成 user rpc 服务的代码。

api 服务调用 rpc 服务

A:为什么本节要安顿在 rpc 服务的前面?

Q:因为 logic 局部的内容主体就是调用对应的 user rpc 服务,所以咱们必须要在 user rpc 的代码曾经生成后能力开始这部分的内容。

A:api 网关层调用 rpc 服务的步骤

Q:对这部分目录构造不分明的,能够参考前文 “api 网关层-api 相干代码-目录介绍”。

  • 编辑配置文件 etc/blog-api.yaml,配置 rpc 服务的相干信息。

Name: blog-api
Host: 0.0.0.0
Port: 8888
# 新增 user rpc 服务.
User:
  Etcd:
#  Hosts 是 user.rpc 服务在 etcd 中的 value 值  
    Hosts:
      - localhost:2379
# Key 是 user.rpc 服务在 etcd 中的 key 值
    Key: user.rpc
  • 编辑文件 config/config.go

type Config struct {
   rest.RestConf
   // 手动增加
   // RpcClientConf 是 rpc 客户端的配置, 用来解析在 blog-api.yaml 中的配置
   User zrpc.RpcClientConf
}
  • 编辑文件 internal/svc/servicecontext.go
type ServiceContext struct {
   Config config.Config
   // 手动增加
   // users.Users 是 user rpc 服务对外裸露的接口
   User   users.Users
}

func NewServiceContext(c config.Config) *ServiceContext {
   return &ServiceContext{
      Config: c,
      // 手动增加
      //  zrpc.MustNewClient(c.User) 创立了一个 grpc 客户端
      User:   users.NewUsers(zrpc.MustNewClient(c.User)),
   }
}
  • 编辑各个 logic 文件,这里以 internal/logic/loginlogic.go 为例
func (l *LoginLogic) Login(req types.ReqUser) (*types.RespLogin, error) {
   // 调用 user rpc 的 login 办法
   resp, err := l.svcCtx.User.Login(l.ctx, &users.ReqUser{Username: req.Username, Password: req.Password})
   if err != nil {
      return nil, err
   }
   return &types.RespLogin{Token: resp.Token}, nil
}

model 层

编写 sql 文件

编写创立表的 SQL 文件 user.sql, 并在数据库中执行。

CREATE TABLE `user`
(
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'id',
  `username` varchar(255) NOT NULL UNIQUE COMMENT 'username',
  `password` varchar(255) NOT NULL COMMENT 'password',
  PRIMARY KEY(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

生成 model 相干代码

运行命令 goctl model mysql ddl -c -src user.sql -dir ., 会生成操作数据库的 CRDU 的代码。

此时的 model 目录:

├── user.sql # 手写
├── usermodel.go # 主动生成
└── vars.go # 主动生成

model 生成的代码留神点

  • model 这块代码应用的是拼接 SQL 语句,可能会存在 SQL 注入的危险。
  • 生成 CRUD 的代码比拟高级,须要咱们手动编辑 usermodel.go 文件,本人拼接业务须要的 SQL。参见 usermdel.go 中的 FindByName 办法。

rpc 调用 model 层的代码

rpc 目录构造

rpc 服务咱们只须要关注上面加正文的文件或目录即可。


├── etc
│   └── user.yaml # 配置文件,数据库的配置写在这
├── internal
│   ├── config
│   │   └── config.go # config.go 是 yaml 对应的构造体
│   ├── logic # 填充业务逻辑的中央
│   │   ├── createlogic.go
│   │   ├── deletelogic.go
│   │   ├── getalllogic.go
│   │   ├── getlogic.go
│   │   ├── loginlogic.go
│   │   └── updatelogic.go
│   ├── server
│   │   └── usersserver.go
│   └── svc
│       └── servicecontext.go # 封装各种依赖
├── user
│   └── user.pb.go
├── user.go
├── user.proto
└── users
    └── users.go

rpc 调用 model 层代码的步骤

  • 编辑 etc/user.yaml 文件
Name: user.rpc
ListenOn: 127.0.0.1:8080
Etcd:
  Hosts:
  - 127.0.0.1:2379
  Key: user.rpc
# 以下为手动增加的配置
# mysql 配置
DataSource: root:1234@tcp(localhost:3306)/gozero
# 对应的表
Table: user
# redis 作为换存储
Cache:
  - Host: localhost:6379
  • 编辑 internal/config/config.go 文件
type Config struct {
// zrpc.RpcServerConf 表明继承了 rpc 服务端的配置
   zrpc.RpcServerConf
   DataSource string          // 手动代码
   Cache      cache.CacheConf // 手动代码
}
  • 编辑 internal/svc/servicecontext.go, 把 model 等依赖封装起来。

type ServiceContext struct {
   Config config.Config
   Model  model.UserModel // 手动代码
}

func NewServiceContext(c config.Config) *ServiceContext {
   return &ServiceContext{
      Config: c,
      Model:  model.NewUserModel(sqlx.NewMysql(c.DataSource), c.Cache), // 手动代码
   }
}
  • 编辑对应的 logic 文件,这里以 internal/logic/loginlogic.go 为例:
func (l *LoginLogic) Login(in *user.ReqUser) (*user.RespLogin, error) {
   // todo: add your logic here and delete this line
   one, err := l.svcCtx.Model.FindByName(in.Username)
   if err != nil {
      return nil, errors.Wrapf(err, "FindUser %s", in.Username)
   }

   if one.Password != in.Password {
      return nil, fmt.Errorf("user or password is invalid")
   }

   token := GenTokenByHmac(one.Username, secretKey)
   return &user.RespLogin{Token: token}, nil
}

微服务演示运行

咱们是在单机环境下运行整个微服务,须要启动以下服务:

  • Redis
  • Mysql
  • Etcd
  • go run blog.go -f etc/blog-api.yaml
  • go run user.go -f etc/user.yaml

在上述服务中,rpc 服务要先启动,而后网关层再启动。

在仓库中我封装了 start.sh 和 stop.sh 脚本来别离在单机环境下运行和进行微服务。

好了,通过上述六个步骤,blog 用户模块的常见性能就实现了。

最初再帮大家强调下重点,除了 goctl 罕用的命令须要熟练掌握,go-zero 文件命名也是有法则可循的。配置文件是放在 etc 目录下的 yaml 文件,该 yaml 文件对应的构造体在 interval/config/config.go 中。依赖治理个别会在 interval/svc/xxcontext.go 中进行封装。须要咱们填充业务逻辑的中央是 interval/logic 目录下的文件。

举荐浏览

实操笔记:为 NSQ 配置监控服务的心路历程

go-zero:开箱即用的微服务框架


推荐阅读
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文详细介绍了MysqlDump和mysqldump进行全库备份的相关知识,包括备份命令的使用方法、my.cnf配置文件的设置、binlog日志的位置指定、增量恢复的方式以及适用于innodb引擎和myisam引擎的备份方法。对于需要进行数据库备份的用户来说,本文提供了一些有价值的参考内容。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
author-avatar
mobiledu2502885517
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有