新手,用一个月左右看了看语法 demo 啥的各种小 demo 看了看觉得自己行了。
然后想写个项目( ssh 跳板机)练练手,在项目组织上又迷糊了,现在这么组织的
├── go.mod
├── go.sum
├── internal
│ ├── admin
│ │ ├── resources
│ │ │ └── assets
│ │ │ └── FS.go < FS=embed *.js *.css **/*.js **/*.css
│ │ └── server.go
│ ├── bootstrap.go
│ ├── config
│ │ └── config.go
│ ├── model
│ │ ├── init.go
│ │ └── models.go
│ └── ssh
│ ├── clients.go
│ ├── error.go
│ ├── forward.go
│ ├── handler.go
│ └── server.go
├── main.go
想实现 在 main.go
里 调用 bootstrap.setupadmin() bootstrap.setupsshserver()
同时也是为了方便的以后能把 admin 和 sshserver 分离开。
那我在什么时候初始化数据库连接呢?放在 model.init 里? init 方法的话,我在哪传入配置信息呢?(数据库地址啊、密码啊啥的)。现在是 model.init.go 里是 Initialize(cfg *internal.Config.Global)
所以完整的 main.go 是这样的
func main() {
cfgfilepath = // parseflag
if cfg, err = ioutil.ReadAll(cfgfilepath) err!=nil {panic
config.Global.LoadConfig(cfg)
model.Initialize(config.Global)
// 支持信号退出用
context = context.withcancel(...
// 这两个里是 go func..
model.SetupAdmin(config.Global, context)
model.SetupSSHServer(config.Global, context)
<- 等待信号
}
这样组织代码有问题吗?怎么做能让 admin 和 sshserver 两个 net.Listen 任意一个有问题就整个退出呢
1
hteen 2022-05-30 10:00:02 +08:00 2
|
2
iseki 2022-05-30 10:04:30 +08:00
我喜欢在外卖把数据库连接初始化好,然后丢进 model 包里。这样将来如果测试时需要对数据库对象 mock ,可以很容易做到
|
3
wheeler 2022-05-30 10:05:50 +08:00 via iPhone
数据库初始化放 main 不能吗? init 塞数据库初始化是坏实践。
|
4
wheeler 2022-05-30 10:08:13 +08:00 via iPhone
“怎么做能让 admin 和 sshserver 两个 net.Listen 任意一个有问题就整个退出呢”
用 channel 不能吗? |
7
saltbo 2022-05-30 10:12:58 +08:00
/internal/app/ssh
/internal/app/admin /internal/pkg/config /internal/pkg/model /internal/pkg/dao 1. 不要在 model 里做数据库操作,数据库操作应该放到 dao 里。model 里只做赋值及数据结构转换之类的操作。在 model 里做数据库操作,盲猜 php 的习惯。 2. config 你都 Global 了,还传啥传,直接在对应的包里使用就好了 3. dao.init 放到 main 里也行。还有人喜欢放 init 里,但我个人比较反感这种,因为不是显式调用,很容易忽略。更好的方式是封装个 Engine 或者 App 之类的 struct ,然后再 main 里调用它,这样 main 里就比较干净 |
8
securityCoding 2022-05-30 10:24:09 +08:00
@wheeler init 做外部资源类初始化就是埋雷,哪天下游炸了就有意思了。
|
10
weiwenhao 2022-05-30 10:43:59 +08:00
github.com/spf13/cobra // 做启动入口
github.com/spf13/viper // 做配置 github.com/robfig/cron // 做定时任务 github.com/gin-gonic/gin // 做 http 服务的 mysql/redis/es 等等数据库都是用连接池的方式初始化,也就是入口处初始化一次就好啦。确实不推荐用原生的 init, 自己再创建 Init ,需要的时候手动调用就好了。不然你 ./test version 就调用了一堆 init 方法。 相关结构推荐扁平化,比如你这个 internal 感觉没啥必要呀,你这就一个编译入口 main 直接把 admin,config,model,ssh 摆出来不就好了。 |
11
sophos 2022-05-30 10:53:26 +08:00
这个可以参考一下: https://github.com/douyu/jupiter-layout
|
12
lxz6597863 2022-05-30 10:59:25 +08:00
任意一个有问题就整个退出 golang.org/x/sync/errgroup
|
13
sciel 2022-05-30 11:20:32 +08:00
goframe 工程目录结构可以参考一下 https://goframe.org/pages/viewpage.action?pageId=30740166
|
14
Gota 2022-05-30 12:00:24 +08:00
我之前写过一篇和微服务组织相关的博客,也有配套的 DEMO 代码,可以参考看看: https://blog.igota.net/posts/20220422/
|
15
dzdh OP @lxz6597863 #12 支持主动退出吗?比如 ctrl-c
|
16
Macolor21 2022-05-30 13:21:04 +08:00 3
搞来搞去,又变成了 Java 那样的工程化分层。
这种帖子下,又没人吐槽这种分层了。 |
17
zackkson1991 2022-05-30 13:38:59 +08:00 2
我实践告诉我, "Java 那套"DAO ,Service ,Controller 的分层还是很清晰的,而且拓展性还是很不错的。虽然我的项目不大。我觉得很多人说这是属于“Java 那套分层”。但是我认为不是输入 java 的, 而是属于大多数软件的工程项目的。
然后, 我想不到有比这个更好的文件结构了。 |
18
yuancoder 2022-05-30 13:59:10 +08:00
我是根据模块功能划分目录,然后在 main 方法里依次调用模块相关的初始化方法,全局的 config ,logger 之类的都是直接使用的
|
19
dzdh OP @wheeler #9
bootstrap.setupadmin(config, context, cacnel) 是 func setup.. srv := ssh.NewServer() go func { select { <-context.Done: srv.shutdown srv.ListenAndServe() 这样总感觉有点问题,感觉不伦不类的 |
20
dzdh OP |
21
Biwood 2022-05-30 19:30:19 +08:00
个人非常讨厌过多的文件夹嵌套,超过三层的文件夹我基本就要疯了,最好是文件夹都在顶层,实在不得已再开第二层,Go 语言的哲学就是简单直接、灵活、可扩展,千万别过度设计
|
22
Macolor21 2022-05-30 22:53:34 +08:00
@zackkson1991 #17 https://www.v2ex.com/t/851925 这可不是我说的
|
23
lesismal 2022-05-31 09:51:40 +08:00
对于应用服务,internal 可能不是好点子,一旦多个仓库想共用,没法 import 它
|
24
LoNeFong 2022-05-31 10:17:42 +08:00
工程化实践可以参考一下 bilibili 毛剑的课
|
25
ShiJh 2022-05-31 13:19:15 +08:00 via iPhone
go clean template GitHub 上面的了解一下
|