接着上一篇文章 一天开发完结一个极简版社区后端服务 ,接下来运用东西sponge实战一个微服务集群项目community-cluster,点击检查community-cluster的完好项目代码。


单体服务community-single拆分为微服务详细过程

社区后端服务community-single选用单体web应用架构,为了应对需求添加,形成功用越来越复杂,代码保护和开发变得困难的问题,把community-single拆分成多个微服务,下面是拆分微服务详细过程:

第一步是进行体系剖析和规划。首先确认哪些功用模块合适独立作为微服务,需求对单体服务community-single进行了仔细的功用分解,将其划分为几个关键的范畴,分为用户服务(user)、联系服务(relation)和内容创造服务(creation)三个独立的服务。用户服务担任用户注册登录等功用,联系服务担任老友联系办理等,内容创造服务担任帖子创立、谈论、点赞、保藏等功用。这些范畴代表了体系的中心功用,并且在不同的范畴之间存在较强的逻辑阻隔。

第二步是界说服务接口。每个微服务需求界说清晰的RPC接口供外部调用,接口需求指定输入输出的数据结构。每个服务开发团队需求依据事务规划自己的接口并完结接口文档。

第三步是规划集群架构。引入了一个rpc网关服务(community_gw),rpc网关服务作为一切微服务的进口,担任路由恳求和负载均衡,它能够依据恳求的路由信息将恳求转发给相应的微服务。此外,rpc网关服务还供给了身份验证、授权和安全性等同享功用,以保证体系的安全性和共同性,这种架构能够进步整体的扩展性。

第四步是数据搬迁。单体服务community-single运用的单一数据库,现在需求将数据按服务拆分,搬迁至每个微服务自己的数据存储中。这里用户服务、联系服务、创造服务运用独立的MySQL,完结各自的数据阻隔。

第五步是开发、测验、布置微服务。在拆分后的微服务集群中,每个微服务都能够独立进行。团队成员能够专注于自己范畴的开发作业,并且能够依据需求对各个微服务进行水平扩展,以满足不同的功用需求。此外,微服务架构还供给了更好的可扩展性,供给继续集成和继续交给(CI/CD),以快速布置和发布新的功用和更新。微服务上线后,需求全面测验各个微服务的功用,保证拆分后的服务能够正常运转、RPC调用正常、满足预期功用。

最后是流量搬迁。当微服务架构正常运转后,将外部流量逐渐搬迁至新的RPC网关层,中止对community-single的访问,完结从单体架构到微服务架构的过渡。后端服务的扩展和晋级将首要在微服务层进行。

经过上述过程,将单体服务community-single拆分为微服务是一个复杂而耗时的过程,经过体系剖析、功用拆分、技术选型、API规划和搬迁策略等过程,完结体系的微服务化,并提高体系的可扩展性、可靠性和功用。微服务架构也带来了分布式事务、运维本钱添加等新的挑战,需求归纳考虑多个因素。经过继续的评价和优化,能够不断提高体系的灵活性和可保护性,以适应不断变化的事务需求。

community-cluster 介绍

community-cluster是由 gRPC服务rpc网关服务 这两种服务类型组成,gRPC服务是各个功用的完结模块,rpc网关服务首要作用是转发恳求给gRPC服务和组装数据。community-cluster服务集群由东西sponge建立,sponge生成gRPC服务和rpc网关服务代码时都会主动剥离事务逻辑与非事务逻辑两部分代码,剥离事务逻辑与非事务逻辑的好处是让开发者聚集在中心事务逻辑代码中,极大的减小建立微服务集群的难度,削减人工编写很多代码。结构图如下图所示:

community-cluster-frame.png

gRPC服务代码组成结构依据grpc封装,包含了丰厚的服务管理插件、构建、布置脚本,gRPC服务代码组成结构如下图所示:

micro-rpc-pb-anatomy.png

图1 gRPC服务代码结构图

从图1能够看出,开发一个完好的微服务聚集在界说数据表界说api接口在模板代码中编写详细事务逻辑代码这3个节点上,而这3个节点代码在单体web服务community-single现已存在,不需求从头编写,直接把这些代码移植过来即可,也便是蛋黄(中心事务逻辑代码)保持不变,只需换蛋壳(web结构换成gRPC结构)和蛋白(http handler相关代码换成rpc service相关代码),运用东西sponge,很简略完结web服务到gRPC服务的转化。

rpc网关服务代码依据gin封装,包含了丰厚的服务管理插件、构建、布置脚本,rpc网关服务代码组成结构如下图所示:

micro-rpc-gw-pb-anatomy.png

图2 rpc网关服务代码结构图

从图2能够看出,开发一个完好rpc网关服务聚集在界说api接口在模板代码中编写详细事务逻辑代码这2个节点上,其中界说api接口在单体web服务community-single现已存在,不需求从头编写,仿制proto文件过来就能够运用。

community-cluster-gen.png

这是单体web服务和微服务集群依靠的proto文件对比图,左边是单体web服务依靠的proto文件,一切proto文件都在同一个服务中。右边是微服务依靠的proto文件,依据各个gRPC服务依靠自己的proto文件。

在rpc网关服务中,假如需求从多个微服务中获取的数据组装成一个新的api接口,把这个组装的新api接口描述信息填写到community_gw.proto文件中。

下面运用东西sponge从0开端到完结微服务集群过程,开发过程依靠东西sponge,需求先装置sponge,点击检查装置阐明。

创立一个目录community-cluster,把各个独立微服务代码移动到这个目录下。

gRPC服务

创立user、relation、creation服务

进入sponge的UI界面,点击左边菜单栏【Protobuf】–> 【RPC类型】–>【创立rpc项目】,填写参数,别离生成三个微服务代码。

创立user服务

这是从单体服务community-single仿制过来的proto文件user.proto,用来快速生成用户(user)服务代码,如下图所示:

community-rpc-pb-user.png

解压代码,把目录称号改为user,然后把user目录移动community-cluster目录下。

创立relation服务

这是从单体服务community-single仿制过来的proto文件relation.proto,用来快速生成联系(relation)服务,如下图所示:

community-rpc-pb-relation.png

解压代码,把目录称号改为relation,然后把relation目录移动community-cluster目录下。

创立creation服务

这是从单体服务community-single仿制过来的proto文件post.proto、comment.proto、like.proto、collect.proto,快速生成创造(creation)服务,如下图所示:

community-rpc-pb-creation.png

解压代码,把目录称号改为creation,然后把creation目录移动community-cluster目录下。

经过简略的界面操作就创立了三个gRPC服务(user、relation、creation),也便是完结了各个gRPC服务各自的图1中蛋壳部分,接下来完结图1中蛋白和蛋黄两部分代码。

编写user、relation、creation服务的事务逻辑代码

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

编写user服务事务逻辑代码

分三个过程编写user服务事务逻辑代码。

第一步 生成模板代码,进入项目user目录,翻开终端,执行指令:

make proto

这个指令生成了service模板代码、过错码、rpc客户端测验代码,这些代码对应图1中蛋白部分。

  • service模板代码,在internal/service目录下,文件称号与proto文件名共同,后缀名是_login.go,文件里边的办法函数与proto文件界说的rpc办法名一一对应,默许每个办法函数下有简略的运用示例,只需在每个办法函数里边编写详细的逻辑代码。
  • 接口过错码,在internal/ecode目录下,文件称号与proto文件名共同,后缀是_rpc.go,文件里边的默许过错码变量与proto文件界说的rpc办法名一一对应,在这里添加或更改事务相关的过错码,留意过错码不能重复,否则会触发panic。
  • rpc客户端测验代码,在internal/service目录下,文件称号与proto文件名共同,后缀是_client_test.go,文件里边的办法函数与proto文件界说的rpc办法名一一对应,填写参数,就能够每个rpc办法。

第二步 搬迁dao代码,把单体web服务community-single目录中的internal/modelinternal/cacheinternal/dao、,internal/ecode这四个目录下user开头的代码文件,仿制到user服务目录下,仿制后的目录和文件称号不变。仿制的这些代码对应图1中蛋白部分。

第三步 搬迁详细逻辑代码,把单体web服务community-single代码文件internal/handler/user_logic.go各个办法函数下的详细逻辑代码,仿制到user服务代码文件internal/service/user_logic.go同名的函数下。这些代码是图1中蛋黄的编写事务逻辑代码部分。

编写relation服务事务逻辑代码

分三个过程编写relation服务事务逻辑代码,参考上面user服务的三个过程。

编写creation服务事务逻辑代码

分三个过程编写creation服务事务逻辑代码,参考上面user服务的三个过程。

测验user、relation、creation服务的rpc办法

测验user服务的rpc办法

编写了事务逻辑代码后,发动服务来测验rpc办法,在第一次发动服务前,先翻开装备文件(user/configs/user.yml)设置mysql和redis地址、设置grpc和grpcClient相关参数,然后执行指令编译发动服务:

# 编译、运转服务
make run

在goland IDE翻开user服务代码,进入user/internal/service目录,找到后缀为_client_test.go的代码文件,在各个rpc办法填写参数后进行测验。

测验relation服务的rpc办法

测验relation服务的rpc办法,请参考上面user服务的测验rpc办法。

测验creation服务的rpc办法

测验creation服务的rpc办法,请参考上面user服务的测验rpc办法。

rpc网关服务

完结了user、relation、creation这三个服务后,接着需求完结rpc网关服务community_gw,community_gw作为user、relation、creation服务的共同进口。

创立community_gw服务

进入sponge的UI界面,点击左边菜单栏【Protobuf】–> 【Web类型】–>【创立rpc网关项目】,填写一些参数生成rpc网关服务代码。

这是从单体服务community-single仿制过来的proto文件user_gw.proto、relation_gw.proto、post_gw.proto、comment_gw.proto、like_gw.proto、collect_gw.proto,快速创立rpc网关服务community_gw,如下图所示:

community-rpc-gw-pb.png

解压代码,把目录称号改为community_gw。

因为community_gw服务作为恳求进口,运用rpc办法与user、relation、creation通信,因而需求生成衔接user、relation、creation服务的代码。进入sponge的UI界面,点击左边菜单栏【Public】–>【生成rpc服务衔接代码】,填写一些参数生成rpc服务衔接代码,如下图所示:

community-rpc-conn.png

解压代码,把目录internal移动到community_gw服务目录下。

同时把user、relation、creation三个服务的proto文件仿制到community_gw的api目录下,如下列表所示。其中community_gw的v1目录下的proto文件是界说http的api接口信息,主张共同约好后缀名_gw.proto

.
├── community_gw
│   └── v1
│       ├── collect_gw.proto
│       ├── comment_gw.proto
│       ├── like_gw.proto
│       ├── post_gw.proto
│       ├── relation_gw.proto
│       └── user_gw.proto
├── creation
│   └── v1
│       ├── collect.proto
│       ├── comment.proto
│       ├── like.proto
│       └── post.proto
├── relation
│   └── v1
│       └── relation.proto
└── user
    └── v1
        └── user.proto

经过简略的操作就完结创立了rpc网关服务community_gw。

编写community_gw服务的事务逻辑代码

从上面图2中rpc网关代码鸡蛋模型解剖图看出,经过sponge剥离后的事务逻辑代码只包含蛋白和蛋黄两部分,编写事务逻辑代码都是环绕这两部分展开。

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

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

make proto

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

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

collect_gw_logic.go, comment_gw_logic.go, like_gw_logic.go, post_gw_logic.go, relation_gw_logic.go, user_gw_logic.go

在这些文件里边的办法函数与proto文件界说的rpc办法名一一对应,每个办法函数下有默许的运用示例,只需求简略调整就能够调用user、relation、creation服务端的rpc办法。上面那些文件代码是现已编写详细逻辑之后的代码。

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

collect_gw_service.pb.go, comment_gw_service.pb.go, like_gw_service.pb.go, post_gw_service.pb.go, relation_gw_service.pb.go, user_gw_service.pb.go

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

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

collect_gw_rpc.go, comment_gw_rpc.go, like_gw_rpc.go, post_gw_rpc.go, relation_gw_rpc.go, user_gw_rpc.go

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

注: 假如调用的rpc办法自身包含了过错码,能够直接返回该过错码。

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

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

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

测验api接口

编写了事务逻辑代码后,发动服务测验api接口,在第一次发动服务前,先翻开装备文件(community_gw/configs/community_gw.yml)设置衔接rpc服务装备信息,如下所示:

# grpc client settings, support for setting up multiple rpc clients
grpcClient:
  - name: "user"
    host: "127.0.0.1"
    port: 18282
    registryDiscoveryType: ""
    enableLoadBalance: false
  - name: "relation"
    host: "127.0.0.1"
    port: 28282
    registryDiscoveryType: ""
    enableLoadBalance: false
  - name: "creation"
    host: "127.0.0.1"
    port: 38282
    registryDiscoveryType: ""
    enableLoadBalance: false

执行指令编译发动服务:

# 编译、运转服务
make run

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

community-gw-swagger.png

从图中看到有些api接口右边有一把锁标记,表明恳求头会携带鉴权信息Authorization,服务端接收到恳求是否做鉴权,由服务端决议,假如服务端需求做鉴权,能够在community_gw/internal/routers目录下后缀文件为_service.pb.go文件中设置,也便是撤销鉴权的注释代码,使api接口的鉴权中间件收效。

服务管理

gRPC服务(user、relation、creation)和rpc网关服务(community-gw)都包含了丰厚的服务管理插件(日志、限流、熔断、链路盯梢、服务注册与发现、目标收集、功用剖析、资源计算、装备中心),有些服务管理插件默许是封闭的,依据实际需求敞开运用。

除了服务自身供给的管理插件,也能够运用自己的服务管理插件,添加自己的服务管理插件阐明:

  • 关于gRPC服务(user、relation、creation),在代码文件服务称号/internal/server/grpc.go里添加自己的插件,假如你的服务管理插件(拦截器)归于unary类型,添加到unaryServerOptions函数里边。假如你的服务管理插件(拦截器)归于stream类型,添加到streamServerOptions函数里边。
  • 关于rpc网关服务community-gw,在代码文件community-gw/internal/routers/routers.go里添加自己的插件(gin中间件)。

下面是默许的服务管理插件敞开和设置阐明,共同在各自服务装备文件服务称号/configs/服务称号.yml进行设置。

日志

日志插件(zap)默许是敞开的,默许是输出到终端,默许输出日志格局是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生成的服务自身对此做了一些改善,平常中止收集profile,用户主动触发体系信号时才敞开和封闭收集profile,收集profile保存到/tmp/服务称号_profile目录,默许收集为60秒,60秒后主动中止收集profile,假如只想收集30秒,发送第一次信号开端收集,大概30秒后发送第二次信号表明中止收集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/user_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/user.yml仿制到nacos界面上装备。运用nacos装备中心,发动服务指令需求指定装备中心文件,指令如下:

./user -c configs/user_cc.yml -enable-cc

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

服务压测

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

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

压测目标:

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

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

总结

本文介绍了把单体服务community-single拆分为微服务集群community-cluster的详细实践过程,微服务集群包含了用户服务(user)、联系服务(relation)和内容创造服务(creation)三个独立的服务,一个微服务进口的网关服务(community_gw),这些服务代码(图1和图2中的蛋壳和蛋白部分)都是由东西sponge生成,中心事务逻辑代码是直接手动无缝移植,根本不需求重复编写代码。

运用东西sponge很简略建立出一个的微服务集群,微服务集群长处:

  • 高功用:依据 Protobuf 的高功用通信协议,同时具备高并发处理和低推迟的特点。
  • 可扩展性:丰厚的插件和组件机制,开发者能够依据实际需求定制和扩展结构功用。
  • 高可靠性:供给了服务注册和发现、限流、熔断、链路、监控告警等功用,提高了微服务的可靠性。