community-single 介绍

community-single是一个极简版社区的后端服务,首要包含用户的注册、登录、重视等功用,创造内容(文本、图片、视频)的发布、评论、点赞、保藏等功用,这些功用在各个社区渠道、视频渠道、直播渠道等都比较常见,能够作为学习参阅用,点击检查完好的项目代码。

community-single项目一开始设计为单体web服务,整个服务由生成代码东西sponge辅佐完结,sponge生成web服务代码进程中剥离了事务逻辑与非事务逻辑两部分代码,这儿的非事务逻辑代码指的是web服务结构代码,首要包含:

  • 经过封装的gin代码
  • 服务管理(日志、限流、熔断、链路盯梢、服务注册与发现、目标收集、功用剖析、装备中心、资源计算等)
  • 编译构建和布置脚本(二进制、docker、k8s)
  • CI/CD(jenkins)

除了web服务结构代码,其他都归于事务逻辑代码。

把一个完好web服务代码看作一个鸡蛋,蛋壳表明web服务结构代码,蛋白和蛋黄都表明事务逻辑代码,蛋黄是事务逻辑的中心(需求人工编写的代码),例如界说mysql表、界说api接口、编写详细逻辑代码都归于蛋黄部分。蛋白是事务逻辑中心代码与web结构代码衔接的桥梁(主动生成,不需求人工编写),例如依据proto文件生成的注册路由代码、handler办法函数代码、参数校验代码、错误码、swagger文档等都归于蛋白部分。web服务鸡蛋模型剖析图如下图所示:

web-http-pb-anatomy.png

图1 web服务代码的组成结构图

因而开发一个完好web服务项目聚焦在了界说数据表界说api接口在模板代码中编写详细事务逻辑代码这3个节点上,也便是事务逻辑的中心代码(蛋黄),其他代码(蛋壳和蛋白)是由sponge生成,能够协助你少写许多代码,下面介绍从0开始到完结项目的开发进程。

开发进程依靠东西sponge,需求先装置sponge,点击检查装置阐明。

界说数据表和api接口

依据事务需求,首先要界说数据表和api接口,这是事务逻辑代码中心(图1中的蛋黄部分),后边需求依据数据表和api接口(IDL)来生成代码(图1中的蛋壳和蛋白两部分)。

界说数据表

这是现已界说好的mysql表 community.sql

界说api接口

在proto文件界说api接口、输入输出参数、路由等,下面是现已界说好的api接口的proto文件:

  • user.proto
  • relation.proto
  • like.proto
  • comment.proto
  • collect.proto

开发中不大可能一次性就界说好事务所需的mysql表和api接口,添加或更改是很常见的事,修正mysql表和proto文件后,如何同步更新到代码里,鄙人面的编写事务逻辑代码章节中介绍。

生成项目代码

界说了数据表和api接口之后,然后在sponge的界面上依据proto文件生成web服务项目代码。进入sponge的UI界面,点击左面菜单栏【protobuf】–> 【Web类型】–>【创建web项目】,填写相关参数生成web项目代码,如下图所示:

community-single-web.png

解压代码,修正文件夹称号(例如community-single),一个服务只需生成代码一次。 这就完结搭建了一个web服务的根本结构(图1中的蛋壳部分),接着能够在web服务结构内编写事务逻辑代码了。

编写事务逻辑代码

从上面图1中web服务代码鸡蛋模型解剖图看出,经过sponge剥离后的事务逻辑代码包含蛋白和蛋黄两部分,编写事务逻辑代码根本都是围绕这两部分展开。

编写与proto文件相关的事务逻辑代码

进入项目community-single目录,翻开终端,履行指令:

make proto

这个指令是依据api/community/v1目录下的proto文件生成了接口模板代码、注册路由代码、api接口错误码、swagger文档这四个部分代码,也便是图1中的蛋白部分。

(1) 生成的接口模板代码,在internal/handler目录下,文件称号与proto文件名共同,后缀名是_logic.go,称号别离有:

collect_logic.go, comment_logic.go, like_logic.go, post_logic.go, relation_logic.go, user_logic.go

在这些文件里边的办法函数与proto文件界说的rpc办法名一一对应,默许每个办法函数下有简略的运用示例,只需在每个办法函数里边编写详细的逻辑代码,上面那些文件代码是现已编写过详细逻辑之后的代码。

(2) 生成注册路由代码,在internal/routers目录下,文件称号与proto文件名共同,后缀名是_handler.pb.go,称号别离有:

collect_handler.pb.go, comment_handler.pb.go, like_handler.pb.go, post_handler.pb.go, relation_handler.pb.go, user_handler.pb.go

在这些文件里边的设置api接口的中间件,例如jwt鉴权,每个接口都现已存在中间件模板代码,只需求撤销注释代码就能够使中间件生效,支撑路由分组和独自路由来设置中间件。

(3) 生成接口错误码,在internal/ecode目录下,文件称号与proto文件名共同,后缀是_http.go,称号别离有:

collect_http.go, comment_http.go, like_http.go, post_http.go, relation_http.go, user_http.go

在这些文件里边的默许错误码变量与proto文件界说的rpc办法名一一对应,在这儿添加或更改事务相关的错误码,注意错误码不能重复,否则会触发panic。

(4) 生成swagger文档,在docs目录下,称号为apis.swagger.json

假如在proto文件添加或更改了api接口,需求从头再履行一次指令make proto更新代码,会发现在internal/handlerinternal/routersinternal/ecode目录下呈现后缀名为日期时刻的代码文件,翻开文件,把新增或修正部分代码仿制到同名文件代码中即可。仿制完新增代码后,履行指令make clean清除这些日期后缀文件。

make proto指令生成的代码是用来衔接web结构代码和事务逻辑中心代码的桥梁,也便是蛋白部分,这种分层生成代码的优点是削减编写代码。

编写与mysql表相关的事务逻辑代码

前面生成的web服务结构代码和依据proto文件生成的事务逻辑的部分代码,都还没有包含对mysql表的操作,因而需求依据mysql表生成dao(数据访问目标)代码,dao代码包含了对表的增修改查代码、缓存代码、model代码,这些代码归于图1中的蛋白部分。

进入sponge的UI界面,点击左面菜单栏【Public】–> 【生成dao CRUD代码】,填写相关参数生成dao代码,如下图所示:

community-single-dao.png

解压dao代码,把internal目录移动到community-single目录下,这样就完结添加了对mysql表的增修改查操作接口。当有新添加的mysql表时,需求再次指定mysql表生成dao代码。

指定mysql表生成的dao代码包含三个部分。

(1) 生成model代码,在internal/model目录下,文件称号与mysql表名共同,别离有:

comment.go, commentContent.go, commentHot.go, commentLatest.go, post.go, postHot.go, postLatest.go, relationNum.go, user.go, userCollect.go, userComment.go, userFollower.go, userFollowing.go, userLike.go, userPost.go

这是生成的对应gorm的go结构体代码。

(2) 生成缓存代码,在internal/cache目录下文件,文件称号与mysql表名共同,别离有:

comment.go, commentContent.go, commentHot.go, commentLatest.go, post.go, postHot.go, postLatest.go, relationNum.go, user.go, userCollect.go, userComment.go, userFollower.go, userFollowing.go, userLike.go, userPost.go

编写事务代码进程中,为了进步功用,有可能运用到缓存,有时候对表的默许缓存(CRUD)不能满足要求,需求添加缓存代码,sponge支撑一键生成缓存代码,点击左面菜单栏【Public】–> 【生成cache代码】,填写参数生成代码,然后把解压的internal目录移动到community-single目录下,然后在事务逻辑中直接调用缓存接口。

(3) 生成dao代码,在internal/dao目录下,文件称号与mysql表名共同,文件别离有:

comment.go, commentContent.go, commentHot.go, commentLatest.go, post.go, postHot.go, postLatest.go, relationNum.go, user.go, userCollect.go, userComment.go, userFollower.go, userFollowing.go, userLike.go, userPost.go

编写事务代码进程中会涉及到操作mysql表,有时候对表的默许操作(CRUD)不能满足要求,这时需求人工编写自界说操作mysql表的函数办法与完成代码,例如comment.go、post.go等都包含少部分人工界说的操作msyql表的办法函数。

在开发进程中有时会修正或新增mysql表,基于mysql表生成的代码需求同步到项目代码中,分为两种情况处理:

  • 修正mysql表之后更新代码处理方法:只需依据修正后的表生成新model代码,替换旧的model代码。点击左面菜单栏【Public】–> 【生成model代码】,填写参数,挑选更改的mysql表,然后把解压的internal目录移动到community-single目录下,并承认替换。
  • 新增mysql表之后处理方法:只需依据新增的表生成新的dao代码,添加到项目目录下。点击左面菜单栏【Public】–> 【生成dao代码】,填写参数,挑选新增的mysql表,然后把解压的internal目录移动到community-single目录下。

测验api接口

编写了事务逻辑代码后,发动服务测验api接口,在第一次发动服务前,先翻开装备文件(configs/community.yml)设置mysql和redis地址,然后履行指令编译发动服务:

# 编译、运转服务
make run

在浏览器访问 http://localhost:8080/apis/swagger/index.htm ,进入swagger界面,如下图所示:

community-single-swagger.png

从图中看到有些api接口右边有一把锁符号,表明请求头会携带鉴权信息Authorization,服务端接收到请求是否做鉴权,由服务端决议,假如服务端需求做鉴权,能够在各个internal/routers/xxx_handler.pb.go文件中设置,也便是撤销鉴权的注释代码,使api接口的鉴权中间件生效。

服务管理

生成的web服务代码中包含了丰富的服务管理插件,有些服务管理插件默许是封闭的,依据实际需求敞开运用,统一在装备文件configs/community.yml进行设置。

除了web服务供给的服务管理插件,也能够运用自己的服务管理插件,建议在internal/routers/routers.go引入自己的服务管理插件。

日志

日志插件默许是敞开的,默许是输出到终端,默许输出日志格局是console,能够设置输出格局为json,设置日志保存到指定文件,日志文件切割和保存时刻。

在装备文件里的字段logger设置:

# logger 设置
logger:
  level: "info"             # 输出日志级别 debug, info, warn, error,默许是debug
  format: "console"     # 输出格局,console或json,默许是console
  isSave: false           # false:输出到终端,true:输出到文件,默许是false
  logFileConfig:          # isSave=true时有用
    filename: "out.log"            # 文件称号,默许值out.log
    maxSize: 20                     # 最大文件大小(MB),默许值10MB
    maxBackups: 50               # 保存旧文件的最大个数,默许值100个
    maxAge: 15                     # 保存旧文件的最大天数,默许值30天
    isCompression: true          # 是否压缩/归档旧文件,默许值false

限流

限流插件默许是封闭的,自适应限流,不需求设置其他参数。

在装备文件里的字段enableLimit设置:

  enableLimit: false    # 是否敞开限流(自适应),true:敞开, false:封闭

熔断

熔断插件默许是封闭的,自适应熔断,支撑自界说请求返回错误码(默许500和503)进行熔断,在internal/routers/routers.go设置。

在装备文件里的字段enableCircuitBreaker设置:

  enableCircuitBreaker: false    # 是否敞开熔断(自适应),true:敞开, false:封闭

链路盯梢

链路盯梢插件默许是封闭的,链路盯梢依靠jaeger服务。

在装备文件里的字段enableTrace设置:

  enableTrace: false    # 是否敞开追寻,true:启用,false:封闭,假如是true,必须设置jaeger装备。
  tracingSamplingRate: 1.0      # 链路盯梢采样率, 范围0~1.0浮点数, 0表明不采样, 1.0表明采样所有链路
# jaeger 设置
jaeger:
  agentHost: "192.168.3.37"
  agentPort: 6831

在jaeger界面上检查链路盯梢信息文档阐明。

服务注册与发现

服务注册与发现插件默许是封闭的,支撑consul、etcd、nacos三种类型。

在装备文件里的字段registryDiscoveryType设置:

  registryDiscoveryType: ""    # 注册和发现类型:consul、etcd、nacos,假如为空表明封闭服务注册与发现。
# 依据字段registryDiscoveryType值来设置参数,例如运用consul作为服务发现,只需设置consul。
# consul 设置
consul:
  addr: "192.168.3.37:8500"
# etcd 设置
etcd:
  addrs: ["192.168.3.37:2379"]
# nacos 设置
nacosRd:
  ipAddr: "192.168.3.37"
  port: 8848
  namespaceID: "3454d2b5-2455-4d0e-bf6d-e033b086bb4c" # namespace id

目标收集

目标收集功用默许是敞开的,供给给prometheus收集数据,默许路由是/metrics

在装备文件里的字段enableMetrics设置:

  enableMetrics: true    # 是否敞开目标收集,true:启用,false:封闭

运用prometheus和grafana收集目标和监控服务的文档阐明。

功用剖析

功用剖析插件默许是封闭的,收集profile的默许路由是/debug/pprof,除了支撑go语言自身供给默许的profile剖析,还支撑io剖析,路由是/debug/pprof/profile-io

在装备文件里的字段enableHTTPProfile设置:

  enableHTTPProfile: false    # 是否敞开功用剖析,true:启用,false:封闭

经过路由收集profile进行功用剖析方法,通常在开发或测验时运用,假如线上敞开会有一点点功用损耗,由于程序后台一直守时记录profile相关信息。sponge生成的web服务对此做了一些改进,平时中止收集profile,用户主动触发体系信号时才敞开和封闭收集profile,收集profile保存到/tmp/服务称号_profile目录,默许收集为60秒,60秒后主动中止收集profile,假如只想收集30秒,发送第一次信号开始收集,大约30秒后发送第2次信号表明中止收集profile,类似开关相同。

这是收集profile操作步骤:

# 经过称号检查服务pid
ps aux | grep 服务称号
# 发送信号给服务
kill -trap pid值

注:只支撑linux、darwin体系。

资源计算

资源计算插件默许是敞开的,默许每分钟计算一次并输出到日志,资源计算了包含体系和服务自身这两部分的cpu和内存相关的数据,资源计算包含了主动触发收集profile功用,当连续3次计算本服务的CPU或内存均匀值,CPU或内存均匀值占用体系资源超过80%时,主动触发收集profile,默许收集为60秒,收集profile保存到/tmp/服务称号_profile目录,然后完成自适应收集profile,比经过人工发送体系信号来收集profile又改进了一步。

在装备文件里的字段enableHTTPProfile设置:

  enableStat: true    # 是否敞开资源计算,true:启用,false:封闭

装备中心

现在支撑nacos作为装备中心,装备中心文件configs/community_cc.yml,装备内容如下:

# nacos 设置
nacos:
  ipAddr: "192.168.3.37"    # 服务地址
  port: 8848                      # 监听端口
  scheme: "http"                # 支撑http和https
  contextPath: "/nacos"       # 途径
  namespaceID: "3454d2b5-2455-4d0e-bf6d-e033b086bb4c" # namespace id
  group: "dev"                    # 组称号: dev, prod, test
  dataID: "community.yml"  # 装备文件id
  format: "yaml"                 # 装备文件类型: json,yaml,toml

而服务的装备文件configs/community.yml仿制到nacos界面上装备。运用nacos装备中心,发动服务指令需求指定装备中心文件,指令如下:

./community -c configs/community_cc.yml -enable-cc

运用nacos作为装备中心的文档阐明。

继续集成与布置

sponge生成的web服务包含了编译和布置脚本,编译支撑二进制编译和docker镜像构建,布置支撑二进制布置、docker布置、k8s布置三种方法,这些功用都统一集成在Makefile文件里,运用make指令就能够很方便的履行指定编译或布置服务。

除了运用make指令编译和布置,还支撑主动化布置东西Jenkins,默许的Jenkins设置在文件Jenkinsfile,支撑主动化布置到k8s,假如需求二进制或docker布置,需求对Jenkinsfile进行修正。

运用Jenkins继续集成和布置的文档阐明。

服务压测

压测服务时运用的一些东西:

  • http压测东西wrk或go-stress-testing。
  • 服务敞开目标收集功用,运用prometheus收集服务目标和体系目标进行监控。
  • 服务自身的自适应收集profile功用。

压测目标:

  • 并发度: 逐渐添加并发用户数,找到服务的最大并发度,确认服务能支撑的最大用户量。
  • 呼应时刻: 重视并发用户数添加时,服务的均匀呼应时刻和呼应时刻散布情况。确保即使在高并发下,呼应时刻也在可接受范围内。
  • 错误率: 调查并发添加时,服务呈现错误或异常的概率。运用压测东西进行长时刻并发测验,计算各并发级别下的错误数量和类型。
  • 吞吐量: 找到服务的最大吞吐量,确认服务在高并发下能够支撑的最大请求量。这需求不断添加并发,直到找到吞吐量饱和点。
  • 资源利用率: 重视并发添加时,CPU、内存、磁盘I/O、网络等资源的利用率,找到服务的资源瓶颈。
  • 瓶颈检测: 经过调查高并发情况下服务的功用目标和资源利用率,找到体系和服务的硬件或软件瓶颈,以便进行优化。
  • 稳定性: 长时刻高并发运转能够检测到服务存在的潜在问题,如内存走漏、衔接走漏等,确保服务稳定运转。这需求较长时刻的并发压测,调查服务运转目标。

对服务进行压测,首要是为了评价其功用,确认能支撑的最大并发和吞吐量,发现当时的瓶颈,并检测服务运转的稳定性,以便进行优化或容量规划。

总结

这是运用东西sponge从开发到布置的实战项目示例,详细流程如下:

  1. 界说mysql表
  2. 在proto文件界说api接口
  3. 生成web结构代码
  4. 依据proto文件生成事务逻辑相关代码
  5. 依据mysql表生成dao代码
  6. 在指定模板文件中编写详细逻辑代码
  7. 在swagger测验验证api接口
  8. 按需启用服务管理功用
  9. 继续集成与布置
  10. 服务压测

看起来流程许多,真实需求人工编写代码的只有1、2、6这三个中心事务流程,其他流程涉及到的代码或脚本由sponge生成,运用sponge剥离非事务逻辑代码和事务逻辑代码,让开发项目时只需求聚焦在事务逻辑的中心代码上,同时也使得项目代码变得规范统一,不同的程序员都能够敏捷上手。再结合人工智能编程辅佐东西Copilot或Codeium编写代码,开发变得更高效、轻松。

community-single是单体web服务,跟着需求添加,功用越来越复杂,使得代码保护和开发变得困难,能够拆分成多个微服务,web单体服务拆分成微服务进程,只换了蛋壳(web结构换成rpc结构)和蛋白(http handler相关代码换成rpc service相关代码),蛋黄(中心事务逻辑代码)不变,中心事务逻辑代码能够无缝的移植到微服务代码中。鄙人一篇文章介绍运用东西sponge辅佐完结把community-single拆分为微服务集群,点击检查community-cluster。