cmdr 04 - simple micro-service
based oncmdr
v0.2.21
My ado is too much.html
因此此次直入主題,謝絕吐槽。不知道 cmdr
幹嗎用的,無妨看看前文git
那麼,golang適合作後端開發,不管是 gRPC 仍是 RESTful 都是它的強項。github
一旦咱們想要開發一個微服務時,拋開核心邏輯不談,也不論 DevOps 方面到底是 K8s,仍是 Docker,仍是裸機,總要面對一個啓動、調試、測試的平常問題。golang
cmdr
除了提供命令行參數的解釋能力以外,也額外提供了一個daemon插件,它能夠幫助你簡化平常開發工做,也令你沒必要關心 pid 文件、日誌、退出信號等等問題,也無需重複編排 daemon 相關的命令行指令。docker
下面介紹怎麼使用 daemon 插件,怎麼編寫實際的業務邏輯。咱們以 demo 爲例編寫一個簡單的示例性微服務,並解釋具體的作法。數據庫
啓用 Daemon 插件只需一行代碼:ubuntu
// Entry is app main entry func Entry() { logrus.SetLevel(logrus.DebugLevel) logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true}) daemon.Enable(svr.NewDaemon(), nil, nil, nil) if err := cmdr.Exec(rootCmd); err != nil { logrus.Errorf("Error: %v", err) } }
daemon.Daemon
接口啓用 daemon 插件,須要你實現 daemon.Daemon
接口,並編寫必定的包裝代碼來鏈接 cmdr
, daemon
以及你的業務邏輯。segmentfault
daemon.Daemon
接口daemon.Daemon
接口是這樣的:後端
// Daemon interface should be implemented when you are using `daemon.Enable()`. type Daemon interface { OnRun(cmd *cmdr.Command, args []string, stopCh, doneCh chan struct{}) (err error) OnStop(cmd *cmdr.Command, args []string) (err error) OnReload() OnStatus(cxt *Context, cmd *cmdr.Command, p *os.Process) (err error) OnInstall(cxt *Context, cmd *cmdr.Command, args []string) (err error) OnUninstall(cxt *Context, cmd *cmdr.Command, args []string) (err error) }
對於一個微服務來講,你必定要編寫的是 OnRun
,OnStop
兩個方法。其餘的幾個方法,一般是可選的,daemon插件針對 RESTful http2 完成了默認的邏輯來支持 reload,status。此外,daemon插件還針對 systemd 實現了默認的 install 和 uninstall 邏輯。centos
因此下面咱們首先完成必須的部分:
OnRun
type daemonImpl struct {} func (*daemonImpl) OnRun(cmd *cmdr.Command, args []string, stopCh, doneCh chan struct{}) (err error) { logrus.Debugf("demo daemon OnRun, pid = %v, ppid = %v", os.Getpid(), os.Getppid()) go worker(stopCh, doneCh) return } func worker(stopCh, doneCh chan struct{}) { LOOP: for { time.Sleep(time.Second) // this is work to be done by worker. select { case <-stopCh: break LOOP default: } } doneCh <- struct{}{} }
daemon 提供兩個 channels,stopCh
應該促使你的代碼結束無限循環,當你的代碼退出循環以後應該觸發 doneCh
事件。這樣的邏輯保證了整個微服務的 graceful shutdown。
至於核心的邏輯,咱們的 worker
,如今僅僅是個無限循環而已,你能夠根據實際業務須要對其完成替換。
下一次咱們也許提供一個 RESTful micro-service 的樣本。
OnStop
以及其餘func (*daemonImpl) OnStop(cmd *cmdr.Command, args []string) (err error) { logrus.Debugf("demo daemon OnStop") return } func (*daemonImpl) OnReload() { logrus.Debugf("demo daemon OnReload") } func (*daemonImpl) OnStatus(cxt *daemon.Context, cmd *cmdr.Command, p *os.Process) (err error) { fmt.Printf("%v v%v\n", cmd.GetRoot().AppName, cmd.GetRoot().Version) fmt.Printf("PID=%v\nLOG=%v\n", cxt.PidFileName, cxt.LogFileName) return } func (*daemonImpl) OnInstall(cxt *daemon.Context, cmd *cmdr.Command, args []string) (err error) { logrus.Debugf("demo daemon OnInstall") return } func (*daemonImpl) OnUninstall(cxt *daemon.Context, cmd *cmdr.Command, args []string) (err error) { logrus.Debugf("demo daemon OnUninstall") return }
其它的接口方法基本上什麼也不作,由於對於咱們的worker來講,不須要在 OnStop
時清理數據庫鏈接、釋放其它資源,也不須要在 OnReload
時加載新的配置文件。
demo
如今咱們能夠將 demo
跑起來看看了。首先研究下有什麼命令行指令可供使用,咱們採用 --tree
來看看:
能夠注意到 s, server, serve, svr, daemon
命令是新增的,它包含一組子命令以提供 daemon 相關的操做。
其中 server start
子命令的解說是這樣的:
也就是說,start
子命令的兩個變形容許你在前臺運行微服務,這是爲了便於 docker 集成,以及在 IDE 中調試微服務的目的:
# 在前臺運行微服務,而不是進入 daemon 模式 demo run demo start --foreground
對於 daemon 模式,沒有標準的規範定義來要求必定具有哪些要素,不過大致上仍是有約定俗成的東西。daemon 在中文中經常被稱做
守護進程
。daemon 模式通常來講包含這些要素:
- 進程啓動後,fork本身的一份副本在操做系統中運行,這樣副本和 tty 的關聯就被detach了,此外子進程也具備獨立的環境和進程空間,甚至是身份,不會收到其它服務、其它 ttys 的干擾。
- 子進程在 /var/run 中保持一個 pid 文件,這指示了子進程的基本狀態
- 子進程經過 syscall signals 來與前臺交互,通常地說,SIGHUP信號使得子進程 reload 配置信息完成重啓動、卻不被關閉進程和從新啓動進程;SIGTERM等信號通知子進程結束服務。等等。
- 子進程將日誌輸出爲 /var/log/ 下的日誌文件
因此,咱們運行下demo在前臺:
而後按下 CTRL-C 終止它,能夠看到這個」微服務「可以正常地跑起來,也能正常地自行銷燬。
而若是咱們要運行 demo 爲守護進程的話,首先你須要將它編譯成可執行文件,而後才能啓動爲守護進程模式。
經過 vagrant 環境,咱們能夠看到守護進程啓動了,而後被咱們的 stop 指令正確地關閉了。
在支持 systemd 的 Linux 發行版中,咱們能夠測試將微服務安裝爲 systemd 服務。
其中,sudo /vagrant/demo server install
完成安裝動做,成功以後demo服務就安裝就緒了,而且已經被預設爲隨系統啓動而自動啓動的模式。
而後咱們依照 systemd 的規範啓動它:sudo systemctl start demo@$USER.service
。
值得注意的是,咱們將 demo 安裝爲了通配模式,所以 demo 是能夠在不一樣用戶身份下被啓動的。若是你想用專用的微服務帳戶啓動它,你可使用:sudo systemctl start demo@msuser.service
。
而後咱們經過 sudo systemctl status demo@vagrant.service
查看到 demo 已經啓動成功了,其中有三個錯誤,然而他們是能夠被忽略的,它們都是爲了嘗試創建幾個相關文件夾的目的,因此只是預防性的指令。而 demo 的正主,也就是 ExecStart 行表示啓動時成功的,並且 Active 的狀態也是 running 狀態。
此時,log/logrus 等日誌輸出也被轉發到了日誌文件 /var/log/demo/demo.log 中。
那麼咱們也能夠經過 sudo systemctl stop demo@$USER.service
來中止服務。
因爲 systemd 在 macOS 中並不被直接支持,因此對於這個部分的測試是放在 vagrant 中完成的。
對於 Windows 來講,你只能使用 server run
前臺運行的方式,咱們也暫無支持 NT Service 的計劃。但你能夠經過前臺運行的方式完成平常開發調試工做。
實際的生產環境中,你能夠選擇 centos,ubuntu 等發行版,部署須要的只是 sudo demo server install
一條指令。
對於容器的環境,你應該使用 demo server run
這種前臺運行模式來啓動。