经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Go语言 » 查看文章
golang API开发过程的中的自动重启(基于gin框架)
来源:cnblogs  作者:wang_yb  时间:2020/12/14 17:25:21  对本文有异议

概要

基于 golang Gin 框架开发 web 服务时, 需要时不时的 go build , 然后重启服务查看运行结果.
go build 的过程集成在编辑器中(emacs), 可以通过快捷键迅速完成, 但是每次重启服务都切换到命令行中操作.
因此, 希望能够编译通过之后自动重启服务.

这里并不是部署阶段的服务重启, 所以不用过多考虑是否正常退出其中的协程.

实现方式

在开源的 illuminant 项目中, 已经将相应的代码集成到 gin 的 debug mode 中.

代码文件: https://gitee.com/wangyubin/illuminant/blob/dev/server_cmd.go

  1. 1 func setupWatcher() (chan struct{}, error) {
  2. 2 file, err := osext.Executable()
  3. 3 if err != nil {
  4. 4 return nil, err
  5. 5 }
  6. 6 log.Printf("watching %q\n", file)
  7. 7 w, err := fsnotify.NewWatcher()
  8. 8 if err != nil {
  9. 9 return nil, err
  10. 10 }
  11. 11 done := make(chan struct{})
  12. 12 go func() {
  13. 13 select {
  14. 14 case e := <-w.Events:
  15. 15 log.Printf("watcher received: %+v", e)
  16. 16 err := syscall.Exec(file, os.Args, os.Environ())
  17. 17 if err != nil {
  18. 18 log.Fatal(err)
  19. 19 }
  20. 20 case err := <-w.Errors:
  21. 21 log.Printf("watcher error: %+v", err)
  22. 22 case <-done:
  23. 23 log.Print("watcher shutting down")
  24. 24 return
  25. 25 }
  26. 26 }()
  27. 27 err = w.Add(file)
  28. 28 if err != nil {
  29. 29 return nil, err
  30. 30 }
  31. 31 return done, nil
  32. 32 }

在 gin debug mode 下, 使用此方法自动重启服务

  1. 1 if c.Bool("prod") {
  2. 2 gin.SetMode(gin.ReleaseMode)
  3. 3 // start route
  4. 4 return routes.Routes(cnf.Server.Port)
  5. 5 } else {
  6. 6 gin.SetMode(gin.DebugMode)
  7. 7 watcher, err := setupWatcher()
  8. 8 if err != nil {
  9. 9 // do something sensible
  10. 10 log.Fatal(err)
  11. 11 }
  12. 12 defer close(watcher)
  13. 13 return routes.Routes(cnf.Server.Port)
  14. 14 }

补充

上面函数的核心有以下两点:

  1. w, err := fsnotify.NewWatcher(): 创建监控文件变化的 watcher, err = w.Add(file) 并将当前二进制文件加入到监控文件列表中
  2. err := syscall.Exec(file, os.Args, os.Environ()) 接受到文件变化的事件时, 重新调用一次自己, 使用上次一样的参数和环境变量

syscall.Exec

对于这个函数, 一般可能用的比较少, 这里稍微介绍下. 它有 3 个参数:

  1. args[0]: 可执行文件的路径(相对路径, 绝对路径或者 PATH 中的路径都可以)
  2. args[1]: 命令的参数
  3. args[2]: 命令的执行的环境变量, os.Environ() 表示继承 caller 的环境变量

syscall.Exec 执行时, 在它之前的所有未执行完的程序都会被中止(包括在 go routine 中执行的程序),
然后执行 syscall.Exec 调用的命令, 该命令还保持在之前程序的 PID 下执行.

syscall.Exec 是最后一条执行的代码, 重启时在它之后可以有代码, 但是都不会被执行到, 包括 defer 中的代码.

下面是个小例子(通过这个例子可以验证上面的结论):

  1. 1 package main
  2. 2
  3. 3 import (
  4. 4 "fmt"
  5. 5 "log"
  6. 6 "os"
  7. 7 "syscall"
  8. 8 "time"
  9. 9
  10. 10 "github.com/fsnotify/fsnotify"
  11. 11 "github.com/kardianos/osext"
  12. 12 )
  13. 13
  14. 14 func syscallExec() {
  15. 15 watcher, err := setupWatcher()
  16. 16 if err != nil {
  17. 17 log.Fatal(err)
  18. 18 }
  19. 19 defer finally(watcher)
  20. 20
  21. 21 fmt.Printf("current pid: %d\n", os.Getpid())
  22. 22 var count = 0
  23. 23
  24. 24 go func(count int) {
  25. 25 for {
  26. 26 fmt.Printf(">>> count in GO ROUTINE: %d\n", count)
  27. 27 count++
  28. 28 time.Sleep(1 * time.Second)
  29. 29 }
  30. 30 }(count)
  31. 31
  32. 32 for {
  33. 33 fmt.Printf(">>> count in MAIN: %d\n", count)
  34. 34 count++
  35. 35 time.Sleep(1 * time.Second)
  36. 36 }
  37. 37 }
  38. 38
  39. 39 func finally(watcher chan struct{}) {
  40. 40 // 重启时没有执行此函数
  41. 41 fmt.Println("exit original exec")
  42. 42 close(watcher)
  43. 43 }
  44. 44
  45. 45 func setupWatcher() (chan struct{}, error) {
  46. 46 file, err := osext.Executable()
  47. 47 if err != nil {
  48. 48 return nil, err
  49. 49 }
  50. 50 log.Printf("watching %q\n", file)
  51. 51 w, err := fsnotify.NewWatcher()
  52. 52 if err != nil {
  53. 53 return nil, err
  54. 54 }
  55. 55 done := make(chan struct{})
  56. 56 go func() {
  57. 57 select {
  58. 58 case e := <-w.Events:
  59. 59 log.Printf("watcher received: %v", e)
  60. 60 err := syscall.Exec(file, os.Args, os.Environ())
  61. 61 if err != nil {
  62. 62 log.Fatal(err)
  63. 63 }
  64. 64 case err := <-w.Errors:
  65. 65 log.Printf("watcher error: %+v", err)
  66. 66 case <-done:
  67. 67 log.Print("watcher shutting down")
  68. 68 return
  69. 69 }
  70. 70 }()
  71. 71 err = w.Add(file)
  72. 72 if err != nil {
  73. 73 return nil, err
  74. 74 }
  75. 75 return done, nil
  76. 76 }

原文链接:http://www.cnblogs.com/wang_yb/p/14132498.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号