去掉热更
This commit is contained in:
parent
11e0ec2f75
commit
a7a9ed08ff
2
Makefile
2
Makefile
@ -43,7 +43,7 @@ build:
|
|||||||
go build ./...
|
go build ./...
|
||||||
go build ./server/admin/...
|
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` 只跑子集。
|
# 可用 `make run rs`、`make run SERVICE=room-service` 或 `make run DEV_RUN_SERVICES=user-service,gateway-service` 只跑子集。
|
||||||
run:
|
run:
|
||||||
./scripts/resolve-compose-container-conflicts.sh
|
./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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -20,19 +20,14 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const stopTimeout = 8 * time.Second
|
||||||
scanInterval = 800 * time.Millisecond
|
|
||||||
restartDebounce = 350 * time.Millisecond
|
|
||||||
stopTimeout = 8 * time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
type serviceSpec struct {
|
type serviceSpec struct {
|
||||||
Name string
|
Name string
|
||||||
Dir string
|
Dir string
|
||||||
Args []string
|
Args []string
|
||||||
WatchRoot []string
|
Ports []int
|
||||||
Ports []int
|
Color string
|
||||||
Color string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type serviceRunner struct {
|
type serviceRunner struct {
|
||||||
@ -66,7 +61,6 @@ func main() {
|
|||||||
defer stop()
|
defer stop()
|
||||||
|
|
||||||
runners := make(map[string]*serviceRunner, len(specs))
|
runners := make(map[string]*serviceRunner, len(specs))
|
||||||
watchers := make([]*watchState, 0, len(specs))
|
|
||||||
for _, spec := range specs {
|
for _, spec := range specs {
|
||||||
runner := &serviceRunner{spec: spec}
|
runner := &serviceRunner{spec: spec}
|
||||||
runners[spec.Name] = runner
|
runners[spec.Name] = runner
|
||||||
@ -74,43 +68,13 @@ func main() {
|
|||||||
if err := runner.start(root); err != nil {
|
if err := runner.start(root); err != nil {
|
||||||
logf(os.Stderr, spec, "start failed: %v", err)
|
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")
|
printPlain(os.Stdout, "press Ctrl-C to stop all services")
|
||||||
|
|
||||||
ticker := time.NewTicker(scanInterval)
|
<-ctx.Done()
|
||||||
defer ticker.Stop()
|
stopAll(runners)
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func allServices() []serviceSpec {
|
func allServices() []serviceSpec {
|
||||||
@ -126,13 +90,9 @@ func allServices() []serviceSpec {
|
|||||||
appService("gateway-service", []int{13000}, "92"),
|
appService("gateway-service", []int{13000}, "92"),
|
||||||
appService("statistics-service", []int{13010, 13110}, "94"),
|
appService("statistics-service", []int{13010, 13110}, "94"),
|
||||||
{
|
{
|
||||||
Name: "admin",
|
Name: "admin",
|
||||||
Dir: "server/admin",
|
Dir: "server/admin",
|
||||||
Args: []string{"run", "./cmd/server", "-config", "configs/config.yaml"},
|
Args: []string{"run", "./cmd/server", "-config", "configs/config.yaml"},
|
||||||
WatchRoot: []string{
|
|
||||||
"server/admin",
|
|
||||||
"api",
|
|
||||||
},
|
|
||||||
Ports: []int{13100},
|
Ports: []int{13100},
|
||||||
Color: "91",
|
Color: "91",
|
||||||
},
|
},
|
||||||
@ -141,14 +101,9 @@ func allServices() []serviceSpec {
|
|||||||
|
|
||||||
func appService(name string, ports []int, color string) serviceSpec {
|
func appService(name string, ports []int, color string) serviceSpec {
|
||||||
return serviceSpec{
|
return serviceSpec{
|
||||||
Name: name,
|
Name: name,
|
||||||
Dir: ".",
|
Dir: ".",
|
||||||
Args: []string{"run", "./services/" + name + "/cmd/server", "-config", "services/" + name + "/configs/config.yaml"},
|
Args: []string{"run", "./services/" + name + "/cmd/server", "-config", "services/" + name + "/configs/config.yaml"},
|
||||||
WatchRoot: []string{
|
|
||||||
"services/" + name,
|
|
||||||
"pkg",
|
|
||||||
"api",
|
|
||||||
},
|
|
||||||
Ports: ports,
|
Ports: ports,
|
||||||
Color: color,
|
Color: color,
|
||||||
}
|
}
|
||||||
@ -238,14 +193,6 @@ func (r *serviceRunner) start(repoRoot string) error {
|
|||||||
return nil
|
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() {
|
func (r *serviceRunner) stop() {
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
cmd := r.cmd
|
cmd := r.cmd
|
||||||
@ -306,114 +253,6 @@ func stopAll(runners map[string]*serviceRunner) {
|
|||||||
wg.Wait()
|
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) {
|
func killPortOwners(spec serviceSpec) {
|
||||||
seen := map[int]bool{}
|
seen := map[int]bool{}
|
||||||
for _, port := range spec.Ports {
|
for _, port := range spec.Ports {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user