如何快速构建微服务项目(Phanes )
Phanes 快速构建微服务项目
目录
项目介绍
Phanes 的初衷为快速构建微服务项目而生,基于go-micro
, 提供了一个 CMD 命令行工具用于生成脚手架,后续还会有生成代码功能,类似 PHP Laravel 框架的命令行生成工具。帮助快速构建一个微服务项目。 项目本身提供了大部分微服务必须的组件,让你只需要关注业务开发。
项目基本功能
- 支持自动服务注册和服务发现
- 支持负载均衡,服务熔断等
- 支持Open-Telemetry可检测日志/链路追踪/指标监控
- 支持事件总线
- 支持各类型数据库
- 支持灵活的Http Middleware和gRpc middleware
- 支持配置中心配置热加载
快速体验
安装 cmd 工具
go install github.com/phanes-o/phanes@v0.1.0
使用 cmd 工具创建项目脚手架
phanes new helloworld
执行tree helloworld
你将看到如下结构
helloworld
├── Dockerfile
├── README.md
├── bll // 业务罗基础
│ ├── init.go
│ └── user.go
├── client // 客户端抽象层
│ ├── broker // 代理客户端
│ │ ├── init.go
│ │ ├── nats.go
│ │ └── rabbit.go
│ ├── grpc // grpc 客户端
│ │ └── init.go
│ ├── httpc // http 客户端
│ │ └── init.go
│ ├── init.go
│ └── websocket // websocket 客户端
│ └── init.go
├── collector // 系统监控
│ ├── init.go
│ ├── logger // 日志
│ │ ├── file_output.go
│ │ ├── init.go
│ │ ├── log.go
│ │ ├── otlp.go
│ │ ├── redis_output.go
│ │ └── zap.go
│ ├── metrics // 指标监控
│ │ ├── init.go
│ │ └── prometheus.go
│ └── trace // 链路追踪
│ └── init.go
├── config // 系统配置
│ ├── conf.go
│ ├── init.go
│ └── redis.go
├── docker-compose.yaml // 基础服务
├── errors // 错误处理
│ ├── error.go
│ └── error_type.go
├── event // 事件总线
│ ├── data.go
│ └── event.go
├── go.mod
├── go.sum
├── lib // 库
│ ├── example_server // 自定义服务
│ │ └── init.go
│ ├── trace // 链路追踪封装实现
│ │ └── trace.go
│ └── traefik // traefik 自动注册
│ ├── etcd.go
│ ├── traefik.go
│ └── traefik_test.go
├── main.go
├── makefile
├── model // 模型
│ ├── entity // 实体映射
│ │ └── user.go
│ ├── mapping // 映射处理
│ │ └── mapping.go
│ ├── model.go
│ └── user.go
├── script // 脚本文件
│ ├── config.json
│ ├── down.sql
│ ├── index.sql
│ └── up.sql
├── server // 服务
│ ├── example_server // 自定义示例服务
│ │ └── init.go
│ ├── grpc // grpc 服务
│ │ ├── init.go
│ │ ├── middleware // grpc 中间件
│ │ │ ├── log.go
│ │ │ └── trace.go
│ │ └── v1 // v1版本接口
│ │ └── user.go
│ ├── init.go
│ └── web // http 服务
│ ├── init.go
│ ├── middleware // http 中间件
│ │ └── log.go
│ └── v1 // v1 版本接口
│ ├── init.go
│ └── user.go
├── store // 数据存储抽象
│ ├── init.go
│ ├── mysql // mysql 实现
│ │ └── init.go
│ ├── postgres // postgres 实现
│ │ ├── init.go
│ │ └── user.go
│ ├── redis // redis 实现
│ │ └── init.go
│ └── user.go
└── utils // 通用工具集
└── utils.go
metric 目前暂未实现, 后续添加, 目前也只支持 etcd 作为注册中心和配置中心
修改配置文件
{
"name": "phanes",
"env": "develop",
"version": "0.1.0",
"http_listen": ":7771",
"collect": {
"log": {
"file_name": "admin",
"redis_key": "log_list"
},
"trace": {
"addr": ""
},
"metric": {
"addr": ""
}
},
"db": [
{
"type": "postgres",
"addr": "host=127.0.0.1 user=root password=root dbname=phanes port=5432 sslmode=disable TimeZone=Asia/Shanghai",
"user": "root",
"pwd": "root"
},
{
"type": "redis",
"addr": "127.0.0.1:6379",
"user": "",
"pwd": ""
}
],
"proxy": {
"domain": "http://127.0.0.1",
"prefix": "/phanes"
},
"broker": {
"type": "rabbitmq",
"addr": "amqp://coco:kk123123123@127.0.0.1:5672/",
"user": "",
"pwd": ""
},
"traefik": {
"enabled": false,
"domain": "test.com",
"prefix": "/phanes"
}
}
启动项目基础服务
项目依赖:Mysql
或者 Postgres
, Nats
或者RabbitMQ
, Redis
,Etcd
, Jaeger
(测试时非必需),Otel-Collector
(测试时非必需)
可使用脚手架下提供的 docker-compose.yaml
启动以上依赖服务, 启动命令: docker-compose up -d
, 基于已安装docker
的前提下
项目使用Etcd
作为注册中心和配置中心,目前只支持Etcd
作为注册中心和配置中心,后续会添加consul
支持
写入配置文件到配置中心
在项目根目录执行 make config
将修改好的配置文件写入配置中心 etcd
, 基于已安装 etcdctl
项目介绍
1. CMD 工具
提供方便的项目生成工具,目前支持以下命令:
- phanes new project_name
其他更方便使用的工具命令正在开发中…
2. 项目基础脚手架
基础脚手架提供了微服务的基础代码架构,在执行 phanes new project_name
你就将得到一个全新的微服务基础架构代码
3. proto
提供一个仓库供多个微服务项目引入
Example:
用户管理
用户请求流程可以是 gRpc,也可以是 Http代码逻辑流程如下:
gRpc→bll→store(Mysql)
http→bll→store(Mysql)
- bll(业务逻辑) 层代码, /bll/user.go
package bll
import (
"context"
"github.com/phanes-o/proto/base"
"github.com/phanes-o/proto/dto"
log "go-micro.dev/v4/logger"
"phanes/errors"
"phanes/event"
"phanes/model/entity"
"phanes/store"
"phanes/store/postgres"
)
var User = &user{}
type user struct {
// bll 层需要使用存储层提供数据存储
user store.IUser
}
// 事件监听
func (a *user) onEvent(ed* event.Data) {
}
// 初始化 user
func (a* user) init() func() {
a.user = postgres.NewUser()
return func() {}
}
// 创建用户
func (a* user) Create(ctx context.Context, in* dto.CreateUserRequest) (err error) {
u := &entity.User{
Username: in.Username,
Password: in.Password,
}
_, err = a.user.Create(u)
if err != nil {
log.Error(err)
return errors.Wrap(err, "user create failed")
}
return nil
}
// 删除用户
func (a *user) Delete(ctx context.Context, p* base.Int64) error {
return a.user.Delete(p.Value)
}
- store(存储抽象层) 接口代码 /store/user.go
package store
import (
"phanes/model/entity"
)
// User 存储层抽象
type IUser interface {
Create(u* entity.User) (id int64, err error)
Find(id int64) (user* entity.User, err error)
Update(id int64, updates map[string]interface{}) (err error)
Delete(id int64) (err error)
List(opts map[string]interface{}) (users []*entity.User, err error)
}
- mysql(存储接口实现)代码 /store/mysql/user.go
package mysql
import (
"phanes/model/entity"
)
type user struct{}
func NewUser() *user {
return &user{}
}
func (a* user) Create(u* entity.User) (id int64, err error) {
err = db.Model(&entity.User{}).Create(u).Error
return u.ID, err
}
func (a* user) Find(id int64) (user* entity.User, err error) {
u := new(entity.User)
err = db.Model(&entity.User{}).Find(u, id).Error
return
}
func (a* user) Update(id int64, updates map[string]interface{}) (err error) {
return db.Model(&entity.User{}).Where("id = ?", id).Updates(updates).Error
}
func (a* user) Delete(id int64) (err error) {
return db.Delete(&entity.User{}, id).Error
}
func (a* user) List(opts map[string]interface{}) (users []*entity.User, err error) {
// todo: implement
return nil, nil
}
- gRpc Api 接口层代码 /server/grpc/v1/user.go
package v1
import (
"context"
"github.com/phanes-o/proto/base"
"github.com/phanes-o/proto/dto"
"phanes/bll"
)
type User struct{}
func (u *User) Create(ctx context.Context, request* dto.CreateUserRequest, empty* base.Empty) error {
return bll.User.Create(ctx, request)
}
func (u* User) Delete(ctx context.Context, p* base.Int64, empty* base.Empty) error {
return bll.User.Delete(ctx, p)
}
- HttpApi 接口层代码, /server/web/v1/user.go
package v1
import (
"github.com/gin-gonic/gin"
"github.com/phanes-o/proto/base"
"github.com/phanes-o/proto/dto"
"phanes/bll"
"phanes/errors"
)
var User = &user{}
type user struct{}
func (a* user) Init(r* gin.RouterGroup) {
u := r.Group("user")
{
u.POST("register", a.register)
u.DELETE("/:value", a.delete)
}
}
func (a* user) register(c* gin.Context) {
var u = &dto.CreateUserRequest{}
if err := c.ShouldBindJSON(&u); err != nil {
c.Error(err)
return
}
// 此处处理参数确实错误并返回,错误由拦截器拦截处理,并根据错误码返回对应的 Http StatusCode
if u.Username == "" {
c.Error(errors.BadRequest.New("username is required"))
return
}
if u.Password == "" {
c.Error(errors.BadRequest.New("password is required"))
return
}
if err := bll.User.Create(c.Request.Context(), u); err != nil {
c.Error(err)
return
}
c.JSON(200, gin.H{
"code": 0,
"msg": "success",
})
}
func (a* user) delete(c* gin.Context) {
d := &base.Int64{}
if err := c.ShouldBindJSON(&d); err != nil {
c.Error(err)
return
}
if err := bll.User.Delete(c.Request.Context(), d); err != nil {
c.Error(err)
return
}
c.JSON(200, gin.H{
"code": 0,
"msg": "success",
})
}
以上均为示例代码,所以部分未实现,只做展示使用。
错误处理
错误处理封装在 /errors
下
errors.BadRequest.New("password is required")
日志/链路追踪/指标监控
目前实现了 日志/链路追踪,并添加了对应的中间件和拦截器,指标监控目前还未完成
根据以上代码示例可以发现,我们只需要专注三个层面的开发: 接口->业务逻辑->数据存储。可能也会涉及事件,MQ的使用,后续会有专门的文字来介绍
有好想法和建议可以给我们提 issues, 项目日志如下:
脚手架项目: github.com/phanes-o/phanes-layout
命令行工具项目:github.com/phanes-o/phanes
本作品采用《CC 协议》,转载必须注明作者和本文链接