去掉热更
This commit is contained in:
parent
11e0ec2f75
commit
a7a9ed08ff
2
Makefile
2
Makefile
@ -43,7 +43,7 @@ build:
|
||||
go build ./...
|
||||
go build ./server/admin/...
|
||||
|
||||
# `run` 拉起 MySQL/Redis 后在本机用 go run 启动所有 Go 服务,并在源码或配置变更时热重启。
|
||||
# `run` 拉起 MySQL/Redis 后在本机用 go run 启动所有 Go 服务,不做源码监听或热重启。
|
||||
# 可用 `make run rs`、`make run SERVICE=room-service` 或 `make run DEV_RUN_SERVICES=user-service,gateway-service` 只跑子集。
|
||||
run:
|
||||
./scripts/resolve-compose-container-conflicts.sh
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Package main provides the repository-local hot reload runner behind `make run`.
|
||||
// Package main provides the repository-local go run launcher behind `make run`.
|
||||
package main
|
||||
|
||||
import (
|
||||
@ -20,19 +20,14 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
scanInterval = 800 * time.Millisecond
|
||||
restartDebounce = 350 * time.Millisecond
|
||||
stopTimeout = 8 * time.Second
|
||||
)
|
||||
const stopTimeout = 8 * time.Second
|
||||
|
||||
type serviceSpec struct {
|
||||
Name string
|
||||
Dir string
|
||||
Args []string
|
||||
WatchRoot []string
|
||||
Ports []int
|
||||
Color string
|
||||
Name string
|
||||
Dir string
|
||||
Args []string
|
||||
Ports []int
|
||||
Color string
|
||||
}
|
||||
|
||||
type serviceRunner struct {
|
||||
@ -66,7 +61,6 @@ func main() {
|
||||
defer stop()
|
||||
|
||||
runners := make(map[string]*serviceRunner, len(specs))
|
||||
watchers := make([]*watchState, 0, len(specs))
|
||||
for _, spec := range specs {
|
||||
runner := &serviceRunner{spec: spec}
|
||||
runners[spec.Name] = runner
|
||||
@ -74,43 +68,13 @@ func main() {
|
||||
if err := runner.start(root); err != nil {
|
||||
logf(os.Stderr, spec, "start failed: %v", err)
|
||||
}
|
||||
watchers = append(watchers, newWatchState(root, spec))
|
||||
}
|
||||
|
||||
printPlain(os.Stdout, "dev-run watching %d service(s): %s", len(specs), serviceNames(specs))
|
||||
printPlain(os.Stdout, "dev-run started %d service(s): %s", len(specs), serviceNames(specs))
|
||||
printPlain(os.Stdout, "press Ctrl-C to stop all services")
|
||||
|
||||
ticker := time.NewTicker(scanInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
pending := map[string]time.Time{}
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
stopAll(runners)
|
||||
return
|
||||
case now := <-ticker.C:
|
||||
for _, watcher := range watchers {
|
||||
changed, err := watcher.changed()
|
||||
if err != nil {
|
||||
logf(os.Stderr, watcher.spec, "watch failed: %v", err)
|
||||
continue
|
||||
}
|
||||
if changed {
|
||||
pending[watcher.spec.Name] = now
|
||||
}
|
||||
}
|
||||
for name, changedAt := range pending {
|
||||
if now.Sub(changedAt) < restartDebounce {
|
||||
continue
|
||||
}
|
||||
delete(pending, name)
|
||||
runner := runners[name]
|
||||
logf(os.Stdout, runner.spec, "change detected, restarting")
|
||||
runner.restart(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
<-ctx.Done()
|
||||
stopAll(runners)
|
||||
}
|
||||
|
||||
func allServices() []serviceSpec {
|
||||
@ -126,13 +90,9 @@ func allServices() []serviceSpec {
|
||||
appService("gateway-service", []int{13000}, "92"),
|
||||
appService("statistics-service", []int{13010, 13110}, "94"),
|
||||
{
|
||||
Name: "admin",
|
||||
Dir: "server/admin",
|
||||
Args: []string{"run", "./cmd/server", "-config", "configs/config.yaml"},
|
||||
WatchRoot: []string{
|
||||
"server/admin",
|
||||
"api",
|
||||
},
|
||||
Name: "admin",
|
||||
Dir: "server/admin",
|
||||
Args: []string{"run", "./cmd/server", "-config", "configs/config.yaml"},
|
||||
Ports: []int{13100},
|
||||
Color: "91",
|
||||
},
|
||||
@ -141,14 +101,9 @@ func allServices() []serviceSpec {
|
||||
|
||||
func appService(name string, ports []int, color string) serviceSpec {
|
||||
return serviceSpec{
|
||||
Name: name,
|
||||
Dir: ".",
|
||||
Args: []string{"run", "./services/" + name + "/cmd/server", "-config", "services/" + name + "/configs/config.yaml"},
|
||||
WatchRoot: []string{
|
||||
"services/" + name,
|
||||
"pkg",
|
||||
"api",
|
||||
},
|
||||
Name: name,
|
||||
Dir: ".",
|
||||
Args: []string{"run", "./services/" + name + "/cmd/server", "-config", "services/" + name + "/configs/config.yaml"},
|
||||
Ports: ports,
|
||||
Color: color,
|
||||
}
|
||||
@ -238,14 +193,6 @@ func (r *serviceRunner) start(repoRoot string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *serviceRunner) restart(repoRoot string) {
|
||||
r.stop()
|
||||
killPortOwners(r.spec)
|
||||
if err := r.start(repoRoot); err != nil {
|
||||
logf(os.Stderr, r.spec, "restart failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *serviceRunner) stop() {
|
||||
r.mu.Lock()
|
||||
cmd := r.cmd
|
||||
@ -306,114 +253,6 @@ func stopAll(runners map[string]*serviceRunner) {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
type watchState struct {
|
||||
root string
|
||||
spec serviceSpec
|
||||
snapshot map[string]fileStamp
|
||||
}
|
||||
|
||||
type fileStamp struct {
|
||||
modTime time.Time
|
||||
size int64
|
||||
}
|
||||
|
||||
func newWatchState(root string, spec serviceSpec) *watchState {
|
||||
w := &watchState{root: root, spec: spec}
|
||||
w.snapshot = w.scan()
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *watchState) changed() (bool, error) {
|
||||
next := w.scan()
|
||||
changed := !sameSnapshot(w.snapshot, next)
|
||||
w.snapshot = next
|
||||
return changed, nil
|
||||
}
|
||||
|
||||
func (w *watchState) scan() map[string]fileStamp {
|
||||
out := make(map[string]fileStamp)
|
||||
for _, rel := range w.spec.WatchRoot {
|
||||
w.scanPath(out, rel)
|
||||
}
|
||||
// 根 module 变更会影响所有 app 服务;admin 还有自己的独立 module 文件。
|
||||
w.scanPath(out, "go.mod")
|
||||
w.scanPath(out, "go.sum")
|
||||
if w.spec.Name == "admin" {
|
||||
w.scanPath(out, filepath.Join("server", "admin", "go.mod"))
|
||||
w.scanPath(out, filepath.Join("server", "admin", "go.sum"))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (w *watchState) scanPath(out map[string]fileStamp, rel string) {
|
||||
path := filepath.Join(w.root, rel)
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !info.IsDir() {
|
||||
if watchableFile(path) {
|
||||
out[rel] = fileStamp{modTime: info.ModTime(), size: info.Size()}
|
||||
}
|
||||
return
|
||||
}
|
||||
_ = filepath.WalkDir(path, func(path string, d os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if d.IsDir() {
|
||||
if ignoredDir(d.Name()) {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if !watchableFile(path) {
|
||||
return nil
|
||||
}
|
||||
info, err := d.Info()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
relPath, err := filepath.Rel(w.root, path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
out[relPath] = fileStamp{modTime: info.ModTime(), size: info.Size()}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func ignoredDir(name string) bool {
|
||||
switch name {
|
||||
case ".git", ".idea", ".vscode", "bin", "dist", "node_modules", "storage", "tmp", "vendor":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func watchableFile(path string) bool {
|
||||
switch filepath.Ext(path) {
|
||||
case ".go", ".yaml", ".yml", ".proto", ".mod", ".sum":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func sameSnapshot(a, b map[string]fileStamp) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for path, stampA := range a {
|
||||
stampB, ok := b[path]
|
||||
if !ok || !stampA.modTime.Equal(stampB.modTime) || stampA.size != stampB.size {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func killPortOwners(spec serviceSpec) {
|
||||
seen := map[int]bool{}
|
||||
for _, port := range spec.Ports {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user