文章目录
- 前言
- 一、有哪些规范我们应该遵循
- 二、项目开发流程
- 三、git的代码分支管理
- 1. 分支管理
- 2. commit规范
- 三、go的代码规范
- 四、go项目目录规范
- 五、微服务该采用multi-repo还是mono-repo?
- 1. 引言
- 2. Repos 是什么?
- 3. 什么是 Mono-repo?
- 4. Mono-repo 的劣势
- 5. 什么是 Multi-repo?
- 6. Multi-repo 的优势?
- 7. Multi-repo 的劣势
- 8. Mono Repo 和 Multi Repo 的区别
- 六、微服务的项目目录规范
- 七、代码检测
- 1. govet进行代码检测
- 2. golangci-lint进行代码检测
- 3. goland配置golangci-lint实现保存时自检
前言
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
一、有哪些规范我们应该遵循
- 开发流程规范
-
- 项目开发规范
-
- go项目开发流程的规范
- 代码分支管理规范 - git开发(不分语言)
-
- 代码分支开发规范
-
- commit规范
- go - 代码规范
-
- uber提供的代码规范
- go - 代码目录规范
-
- 代码风格不统一
-
- 目录杂乱无章 - 维护性很差
-
- 错误码的处理
-
- 常见的项目类型:
-
-
- api服务
-
-
-
- 非api服务
-
-
-
-
- 命令行工具
-
-
-
-
-
- deamonset服务:比如rocketmq消费者服务(一直运行但不对外提供端口服务)
-
-
-
-
- 框架类、组件类: 比较多样,并没有统一的规范
-
-
-
- sdk开发
-
-
- 不同类型的服务目录规范
- go - 微服务代码目录规范
-
- 微服务项目和单体服务的目录不同点
-
- 微服务应该如何管理目录
- 代码发布规范
-
- go项目的发布步骤
-
- 静态代码扫描
-
- 代码自动格式化
-
- 代码自动运行单元测试
-
- go vet检查 竞态
-
- 自动编译
-
- 镜像上传
- 代码结构规范
-
- 代码是否便于写单元测试- 很多项目后期想加单元测试很麻烦,初始化过程太繁琐
-
- code review流程是否满足
-
- 设计模式是否引入了?
-
- 代码分层做好了吗 - 后期想要替换gorm会不会做不到,想要替换gin能否做到?
-
- ioc和aop是否能理解其作用?
-
- 代码自动生成的应用场景?和java的区别在哪里?
-
- 你是否理解到go自带工具的作用?为什么要这样做?
上述是基本保障,实际开发中还有很多其他更具体的工作需要注意,后面开发中逐步理解到
二、项目开发流程
三、git的代码分支管理
1. 分支管理
- git想要使用好,最好是理解git的大概原理,git的规范使用基本上可以出一门课,所以我们只给大家
简单介绍git的规范,各个规范我会给出网上查询到的比较好的文章,请轻喷 - git本身学习很枯燥,所以最好的办法就是强迫自己平时遵循这些规范,养成好习惯
- 项目开发中最常用的是用git进行管理,git对于多人开发中有一套成熟的分支管理
- 现在有5个人
-
- a和c突然要开发一个功能
A
- a和c突然要开发一个功能
-
- b和d这个时候业开发一个功能
D
- b和d这个时候业开发一个功能
-
- e和a也要合作开发一个功能
C
- e和a也要合作开发一个功能
-
- 微服务不适合太多人开发一个微服务 一个微服务10个人开发
- 绝大部分小公司都仍然会采用svn式的管理,无可厚非
- 多人开发的大项目都会采用标准的开发管理,减少代码冲突,大家进入以后直接遵循即可
- 如果一两个人开发一个项目简单化就可以了
- 参考这篇: https://zhuanlan.zhihu.com/p/23478654
2. commit规范
commit规范很重要,不论是多人开发还是自己开发,否则后期你自己看代码都得晕, 而且还能帮我们生成changelog, commit的log生成changelog的步骤可以参考:
https://cloud.tencent.com/developer/article/1755709
很多时候为了方式代码丢失本地会进行进行commit, 要学会merge commitlog
举个例子说明一下:
我们现在开发了一个订单接口服务,但是开发中,我们commit了一个log:feature-order:增加生成订单接口,接下来我们发现了一个bug,然后继续commit:feature-order:修复了bug1,然后又发现了一个bug, 继续commit:feature-order:修复了bug2, 然后又增加了log记录,生成commit:feature-order:增加日志打印,然后就成这样了:
- feature-order:增加生成订单接口
- feature-order:修复了bug1
- feature-order:修复了bug2
- feature-order:增加日志打印
我们发布的版本是V1.2,生成changelog:
- feature-order:增加生成订单接口
- feature-order:修复了bug1
- feature-order:修复了bug2
- feature-order:增加日志打印
其实这就是一个低质量的commit log,这些都可以合并成一个commit,我们并不关心你中途的所有commit log
合并commit log可以参考:https://cloud.tencent.com/developer/article/1690638
commit规范可以参考: https://blog.csdn.net/xmcy001122/article/details/123372325
goland的插件:
三、go的代码规范
uber开源的代码规范: https://github.com/xxjwxc/uber_go_guide_cn
建议有事没事就到uber看看
- 代码规范一下全部接受不容易,经常看,养成习惯即可
- 规范不代表权威,某个规范自己思考一下,不一定就正确,要结合自己的需求具体情况具体分析
- 简单给大家介绍几个
- 零值 Mutex 是有效的
- errors比较重要,后面有专门的章节讲解
- 这里有一些我们后面会有专门的章节讲解,所以建议大家以后再来看一下这里的规范
四、go项目目录规范
很多目录规范是随着某个框架而确定的,并不是语言本身可以决定目录规范,比如python中的django目录, java的spring目录规范,但是go目前还没有出现spring一样一统天下的框架,所以目录规范也并不统一,但是在某种程度上还是有大家的共识的,我们以uber的目录规范来做一下说明。
参考: https://github.com/golang-standards/project-layout/blob/master/README_zh.md
五、微服务该采用multi-repo还是mono-repo?
Multi-repo 和 Mono-epo 是 Git 托管代码的两种策略,我们讨论下两者的策略以及其利弊。
1. 引言
大多数现代项目都是在 Git 上管理和托管的。Git 已经成为来自世界各地的分布式源代码管理、版本控制和协作的标准平台。Git 是快速和高效的,主要有两种方法来托管和管理 Git 代码:
- Mono-repo
- Multi-repo
在深入研究这些方法之前,让我们先了解一下 Repo 是如何工作的。
2. Repos 是什么?
仓库(Repo)包含项目的所有文件夹和文件。它还包含关于用户、人和计算机的信息。
Git 仓库数据受版本控制,Repo 可以由个人或团队成员拥有。
Git 仓库可以是公开的,私人的,或者是内部的。GitHub 是 Git 仓库的一个托管服务,并且有一个用户界面。
Git 提供了版本控制和代码共享功能,Git 的特别之处在于,如果开发人员想对他们的文件做一些修改,他们可以将整个存储库复制到他们的本地系统中。因此,即使开发人员没有对特定项目的写入权限,他们也可以在本地复制内容并修改它们(我们称为 forking)。
此外,如果开发人员希望共享本地所做的更改,他们可以向项目所有者发送一个 pull request
。一个项目可以只有一个服务。如果你的项目有多个工作流,你可以为每个工作流创建多个服务。大多数开发人员喜欢将较大的项目拆分为具有一个或多个功能的较小的独立服务。每个服务都可以解决各种业务问题。随着 serverless
框架的流行,用户可以将功能作为服务访问。
一旦你创建了这些函数——作为服务并部署它们,下一步就是对它们构造和版本控制——你可以将所有的服务放在一个存储库(mono-repo)中,或者为你拥有的每个服务拥有一个单独的存储库(multi-repo) !
3. 什么是 Mono-repo?
在 mono-repo 方法中,你可以将所有服务保存在单一(mono)存储库中。你仍然可以独立地部署和管理每个服务。这些服务可以共享公共库和代码。
像 Facebook、 Google 和 Dropbox 这样的公司都使用 Mono-repo。
如果你把一个代码打包成公共的代码供大家go get
那么你会面临一个问题:
版本维护
假设A项目用了你的1.x 版本
B项目用了你的2.x的版本
c项目用了你的3.x的版本
所以随着项目的反正,你这个库会越来越大,越来越多的无用代码
最好的基本办法是什么:强制升级,有个问题 你很难知道谁用了你的代码, 别人不愿意升级
Mono-repo 的优势
Mon-repo 方式有许多优点:
- 存储所有项目代码的单独位置,团队中的每个人都可以访问。
- 易于重用和共享代码,与团队合作。
-
- 就意味着基本上没有版本管理,只有最新版,我升级了版本,引用的地方就会报错
- 很容易理解你的变更对整个项目的影响。
- 代码重构和代码大变更的最佳选择。
- 团队成员可以获得整个项目的总体视图。
- 易于管理依赖关系。
4. Mono-repo 的劣势
当然,Mono-repo 也有一些缺点,主要表现在性能上。如果你的项目增长,每隔一天都会添加更多的文
件,那么 git checkout、pull 和其他操作可能变得缓慢,以及文件搜索可能需要更长的时间。
此外,如果你为你的项目雇佣了许多独立的承包商,那么让他们访问整个代码库可能不那么安全。
此外,实现持续部署(Continuous deployation,CD)也很困难,因为许多人可以合入他们的更改,而持续
集成(Continuous Integration,CI)系统可能需要进行多次重构。
使用 Mono-repo 的大公司都有自定义工具来处理扩展问题。例如,Facebook 使用自定义文件系统和源
代码控制。
5. 什么是 Multi-repo?
在 Multi-repo 方法中,存在多个存储库,它们承载一个项目的多个库和服务。如果服务发生更改,开发
人员只需重新构建该服务,而不需要构建整个项目。个人和团队可以从事他们特定的服务,他们只能访问
他们有权限的服务。
像 Netflix 和 Amazon 这样的公司使用 Multi-repo。
6. Multi-repo 的优势?
采用 Multi-repo 的公司数量远远多于采用 Mono-repo 的公司,原因如下:
- 每个服务和库都有自己的版本控制。
- 代码 checkout 和 pull 是小型且独立的,因此即使项目规模增大,也不存在性能问题。
- 团队可以独立工作,不需要访问整个代码库。
- 更快的开发和灵活性。
- 每个服务都可以单独发版,并有自己的部署周期,从而使 CI 和 CD 更易于实现。
- 更好的权限访问控制——所有的团队不需要完全访问所有的库——需要的时候,再获得读访问权限。
7. Multi-repo 的劣势
跨服务和项目使用的公共依赖和库必须定期同步以获得最新版本。
某种程度上鼓励孤立文化,导致重复代码和各个团队试图解决相同问题。
每个团队可能遵循不同的一组最佳实践来编写代码,从而导致难以遵循通用的最佳实践。
8. Mono Repo 和 Multi Repo 的区别
让我们来概括 Mono Repo
和 Multi Repo
的区别:
Mono-repo | Multi-repo |
---|---|
一个组织的所有项目的所有代码都驻留在中央存储库中 | 每个服务和项目都有一个单独的存储库 |
团队可以一起协作和工作; 他们可以看到彼此的变化 | 团队可以自主工作; 个人的变更不会影响其他团队或项目的变更 |
每个人都可以访问整个项目结构 | 管理员可以将访问控制限制到开发人员需要访问的项目或服务 |
如果项目规模不断增长,则可能会出现并放大问题 | 良好的性能,因为有限的代码和较小的服务单元 |
难以实现持续部署(CD)和持续集成(CI) | 开发人员可以很容易地实现 CD 和 CI,因为他们可以独立地构建服务 |
开发人员可以轻松地共享库、 api 和其他在中央存储库中更新的公共代码 | 对库和其他常见代码的任何更改都应该定同步,以避免以后出现问题 |
注: 技术栈统一的话建议采用mono-repo
, 不同服务技术栈不统一就采用multirepo
, 目前采用mono-repo
的知名公司: google、bilibili
总结
Mono-repo 和 Multi-repo 同样流行,哪一个更好取决于你的项目大小、项目需求以及你需要的版本控制和访问控制级别。
Mono-repo 侧重一致性,而 Multi-repo 侧重于解耦。在 Mono-repo 中,整个团队可以看到某一个人完成的更改,而 multi-repo 为每个团队创建一个单独的 repo
,这些团队只能访问所需的仓库。
如果你想为你的项目使用 mono-repo 和 multi-repo 的组合,你可以使用 meta
,一个管理多个项目和库的工具。
六、微服务的项目目录规范
至于multi-repo直接采用前面的layout即可,但是对于mono-repo确可以有两种选择
- 将共同部分抽取到根目录下, 不同的代码放在internal中
- 将公共代码比如pkg代码放在根目录下,cmd等都放在不同的服务中
两种都可以,看个人喜好,我采用的是第一种
/api
存放proto文件和生成的代码
/app
存放各个微服务,包括grpc服务和http服务 : 大量逻辑在这里,这里是重点目录,后期各个服务单独重构的时候讲解具体的原因
/goods
存放商品服务
/srv
service服务
/mxshop
/admin
后台管理系统的服务
/apiserver
整个电商项目的服务
/configs
保存各个服务的yaml配置文件
/cmd
保存各个服务的启动main启动源码
/build
保存docker部署文件和shell启动脚本
/logs
保存各个服务的日志文件
/pkg
保存所有微服务的公共代码
/third_party
第三方源码,可以存放第三方的proto源码,克隆下来的第三方源码等
/tools
存放一些工具源码
七、代码检测
1. govet进行代码检测
在 Go 项目发布阶段中,go vet 是一个非常有用的工具,它用于静态分析 Go 代码并查找潜在的错误或问题。
具体而言,go vet 会检查代码中的常见错误,例如未使用的变量、格式化字符串与参数不匹配、导入了但未使用的包等。它还可以检查常见的代码约定,例如命名规范、函数签名等。
除了这些常见的错误和约定之外,go vet 还可以检查一些特定的错误。例如,它可以检查使用 sync.WaitGroup 时是否正确地使用了 Add、Done 和 Wait 方法,以及是否存在可能导致竞争条件的代码。
在发布 Go 项目之前,运行 go vet 可以帮助开发人员及时发现和修复代码中的问题,提高代码质量和稳定性。
示例:
假设我们有以下的 Go 代码,其中包含了一个错误:
package main
import "fmt"
func main() {
fmt.Printf("Hello, world!", 123) // 错误:格式化字符串与参数不匹配
}
在这个例子中,我们使用 fmt.Printf 函数输出字符串 “Hello, world!”,但是在格式化字符串中并没有包含 %s 等占位符,却传递了一个整数 123 作为参数,这样会导致输出不符合预期。
为了发现这个错误,我们可以使用 go vet 工具,命令如下:
go vet main.go
运行上述命令后,go vet 会分析 main.go 文件,并检查其中的潜在问题。在这个例子中,它会输出以下信息:
# command-line-arguments
./main.go:6: printf call has arguments but no formatting directives
这个输出告诉我们,在第 6 行的 fmt.Printf 调用中,传递了参数但是却没有相应的格式化指令。这个错误很明显,因此很容易修复。我们只需要将格式化字符串修改为 “Hello, world!\n”,然后再次运行 go vet,即可确保代码中不再存在这个问题。
2. golangci-lint进行代码检测
golangci-lint 是一个集成工具,它集成了很多静态代码分析工具(静态代码分析是不会运行代码的),我们通过配置这个工具,便可灵活启用需要的代码规范检查。
官方文档:https://golangci-lint.run/usage/install/
安装:
golangci-lint 是 Go 语言编写的,可以从源代码安装它,在终端输入命令:
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
有可能会找不到命令, 可以参考一下官方文档:https://golangci-lint.run/usage/install/
查看支持的linter
golangci-lint help linters
支持的命令和flag
golangci-lint -h
完整的golangci.yml配置
具体配置可以参考:https://golangci-lint.run/usage/linters
3. goland配置golangci-lint实现保存时自检
安装后添加golangci-lint
添加到save
自定义组件:
官方文档:https://golangci-lint.run/usage/linters
示例:
在项目根目录添加新文件:.golangci.yml
run:
timeout: 5m
modules-download-mode: readonly
govet:
disable-all: true
# Enable analyzers by name (in addition to default).
# Run `go tool vet help` to see all analyzers.
# Default: []
enable:
- printf
linters:
disable-all: true
enable:
- errcheck
- goimports
- gofmt
- govet
- staticcheck
issues:
exclude-use-default: false
max-issues-per-linter: 0
max-same-issues: 0