这个模板主要使用 echo 提供 restful api, 目标是实现微服务体系中的一个业务服务,它 可以随意扩展。
一些基础设施的代码在 x
这里主要是业务代码
关于项目结构,在 golang 的世界中争议颇大,有很多流派。
本人不喜欢按层管理结构,确切的说是在微服务中再对代码横向按层分隔,本来微服务的一 个服务中业务内容就不多,因为已经被纵向分隔过了。这时尽量把相关的业务代码集中才更 容易维护。
在单体项目中,按层分隔代码是没有问题的。
这个主要是使用 golang-standards/project-layout 的建议。
去他的 Issues 区看看吧,很多人有和我一样的观点,你发布一个 layout 没问题,但是自 己把它叫做 Standard 就太无耻了,带偏了很多初入 golang 的新人。
我在任何时候都不建议这个结构
把所有文件都放在根目录下,适合小项目,这也是 golang 社区最初的建议。
我的 restdemo 就是这种结构的演示。
在单体小项目的情况下,推荐这个结构,可以让你把精力都集中在业务代码上。
在 reddit 和 maillist 等地方其实经常讨论项目目录结构,我也在 3 年中尝试过四五种 不同的结构,它们各有优缺点,很难有一个尽善尽美的。
下面我会说一下本项目的文件结构考虑。
结构不是万能的,这个结构在以下条件下才更有优势:
- 微服务中的业务模块,单个服务中管理的实体不多。
- 倾向于使用 rest api 对外服务
- 倾向于使用事件驱动的设计模式,让服务间通讯最优先选择 pub/sub 模式的异步通讯
- 服务间同步通讯可以用 rest api,也可以用 grpc,但不作为主要通讯方式。这条很重要 ,如果主要选择 rpc 去通讯,可以去用一些成熟的微服务框架。
- 使用了 vscode 或 goland ,最好启用了 gopls
- 使用了 go module
- 参与的程序员要认可使用 go 的模式去写 golang 代码。如果主要语言是别的,golang 项目只是参与以下,可能来回切换会很不习惯。
我倾向于把实体模型放在根目录,但是如果根目录不是本来的项目名称,或者强迫症嫌根目
录有别的文件,那单独用这个项目名称做一个文件夹,里边放实体模型的定义。在这个项目
中是 demo
文件夹。
实体附带的一些方法当然也可以放在一起,如果有 client
或者 sdk
之类的东西,其
定义以及 New 的方法最好也放在这个文件夹。
这个文件夹是要被别的服务引用的,被引用的一般只有 模型 和 Client 。
把模型外的业务代码全部放在 server
文件夹中。代码量不大可以每个实体就用一个 go
文件。代码量如果大,可以像这个例子一样,把同一个实体相关的代码,按照层分在不同的
文件中。但是它们在目录中还是会被排列到一起的,很方便查看业务代码。
这个例子中只写了 park 的 service 和对普通用户/管理员的 rest handler 。如果要添加 服务间调用的 rpc,可以继续再来一个 park_rpc.go 就好。如果有处理队列消息的订阅者 ,可以放在 park_worker.go 中,诸如此类。
总之最高宗旨就是把业务代码集中在一起,让协作的人和将来的自己更好阅读代码。
我们服务端一般只有一个可执行程序,按照管理把它放在 cmd 中,如果将来有别的服务端 程序要启动,也预留了地方,在 cmd 中再来个文件夹就好了。
- endpoint 可以分为四组
- 默认 通过 gateway 暴露,公开
- user 通过 gateway 暴露,需要用户登录认证
- admin 通过 gateway 暴露,需要管理员登录认证
- sys 只能内部服务间访问,内部公开
- 认证的接口必定能在 context 中取到相关字段
- jwt 方案解析 token 写入 context
- oauth 方案配合 session 读出常用的权限写入 context 即可
部署方面,中小项目可以按照下边的极简方案来,需要学习的东西最少。大项目上各种微服 务工具都是支持的。
- 服务发现和负载均衡使用 docker swarm 即可,内部直接用服务名访问别的服务。
- API gateway 可以选用 nginx 或者 caddy
- 使用事件驱动思想,能异步的交互全部用异步,采用 nsq 等 pub/sub 模型的消息队列实 现。
- 使用两段构建,先 build ,再把二级制文件复制到生产镜像
- 生产镜像使用了我略微修改的
debian
- 增加证书,用以在镜像内访问别的 https 资源,否则会报 x509
- 指定了中国时区