本地同時啟停多箇中介軟體的優雅方案

2022-12-14 12:01:45

"I don’t care if it works on your machine! We are not shipping your machine!" - Vidiu Platon

「我才不管它能不能在你的機器上執行捏!我們又不會給你提供機器!」 —— 韋都·柏拉圖

0x00 大綱

0x01 前言

隨著微服務架構風格的推廣應用,開發人員的本地開發和偵錯成本大大提高,甚至不堪重負。動不動就要依賴一攬子東西,註冊中心、Redis、MQ、基礎服務ABC……等等。

開發人員如果手工在本地啟停多個基礎服務和中介軟體,將會浪費大量時間,降低開發效率。

0x02 大公司的解決方案

DockerKubernetes,不缺錢也不缺人的首選。什麼雙活、負載均衡統統來個四五套,把寒氣也傳給運維人員。

0x03 小作坊的解決方案

為了節約成本,當然是要在本地啟動一整套系統節點啦,畢竟記憶體成本比人力成本低得多。但是手工啟停太浪費時間了,這樣會導致本來就不多的摸魚時間所剩無幾。人生苦短,我用指令碼。

確定了方案,我就開始著手編排了,以一個Zookeeper + Redis(一主二從三哨兵)的啟停指令碼為目標,設定過程就略過了,著重解決啟停的問題。

試作一號

啟動指令碼

@echo off
color 5f
title fake-docker
echo ^>^>^>^>^>^>^>^>bootstrapping redis...
start "redis-master" "c:\dev\redis-x64-3.2.100\redis-server.exe" "c:/dev/redis-x64-3.2.100/redis.master-1.conf"
start "redis-slaver-1" "c:\dev\redis-x64-3.2.100\redis-server.exe" "c:/dev/redis-x64-3.2.100/redis.slaver-1.conf"
start "redis-slaver-2" "c:\dev\redis-x64-3.2.100\redis-server.exe" "c:/dev/redis-x64-3.2.100/redis.slaver-2.conf"
start "redis-sentinel-1" "c:\dev\redis-x64-3.2.100\redis-server.exe" "c:/dev/redis-x64-3.2.100/redis.sentinel-1.conf" --sentinel
start "redis-sentinel-2" "c:\dev\redis-x64-3.2.100\redis-server.exe" "c:/dev/redis-x64-3.2.100/redis.sentinel-2.conf" --sentinel
start "redis-sentinel-3" "c:\dev\redis-x64-3.2.100\redis-server.exe" "c:/dev/redis-x64-3.2.100/redis.sentinel-3.conf" --sentinel
echo ^>^>^>^>^>^>^>^>done!
echo ^>^>^>^>^>^>^>^>bootstrapping zookeeper...
start "zookeeper-dev" "c:\dev\apache-zookeeper-3.6.3-bin\bin\zkServer.cmd"
echo ^>^>^>^>^>^>^>^>system is hot!

停止指令碼

@echo off
color 5f
echo ^>^>^>^>^>^>^>^>shutdowning...
taskkill /t /f /fi "imagename eq redis-server.exe" >nul
taskkill /t /f /fi "windowtitle eq zookeeper-dev" >nul
echo ^>^>^>^>^>^>^>^>system is down!
pause>nul

第一個版本,解決了啟動和停止的問題,但是是手動檔的,重啟中介軟體的話要執行兩個指令碼。待改進的問題有兩個:

  • 重啟不方便,儘量做到一鍵啟停
  • 彈出視窗太多,體驗不佳

第一個問題容易解決,先停後起,先執行停止指令,再把應用拉起來。

第二個問題有點麻煩,一開始想嘗試無視窗啟動,反覆嘗試未果,後來採用了折中方案,在CMDstart命令幫助中有如下描述:

C:\Users\Master>help start

啟動一個單獨的視窗以執行指定的程式或命令。

START ["title"] [/D path] [/I] [/MIN] [/MAX] [/SEPARATE | /SHARED]
[/LOW | /NORMAL | /HIGH | /REALTIME | /ABOVENORMAL | /BELOWNORMAL]
[/NODE ] [/AFFINITY ] [/WAIT] [/B]
[command/program] [parameters]

"title"     在視窗標題列中顯示的標題。
path        啟動目錄。
B           啟動應用程式,但不建立新視窗。
            應用程式已忽略 ^C 處理。除非應用程式
            啟用 ^C 處理,否則 ^Break 是唯一可以中斷
            該應用程式的方式。
I           新的環境將是傳遞
            給 cmd.exe 的原始環境,而不是當前環境。
MIN         以最小化方式啟動視窗。
MAX         以最大化方式啟動視窗。
SEPARATE    在單獨的記憶體空間中啟動 16 位 Windows 程式。
SHARED      在共用記憶體空間中啟動 16 位 Windows 程式。
LOW         在 IDLE 優先順序類中啟動應用程式。
NORMAL      在 NORMAL 優先順序類中啟動應用程式。
HIGH        在 HIGH 優先順序類中啟動應用程式。
REALTIME    在 REALTIME 優先順序類中啟動應用程式。
ABOVENORMAL 在 ABOVENORMAL 優先順序類中啟動應用程式。
BELOWNORMAL 在 BELOWNORMAL 優先順序類中啟動應用程式。
NODE        將首選非一致性記憶體結構(NUMA)節點指定為
            十進位制整數。
AFFINITY    將處理器關聯掩碼指定為十六進位制數位。

根據描述,如果start時帶上/b引數,就能讓多個程式在一個視窗中寄宿。修改後得到最終版本:

最終版本

@echo off
color 5f
title %date%
echo ^>^>^>^>^>^>^>^>cleaning up context...
echo ^>^>^>^>^>^>^>^>killing previous runner...
taskkill /t /f /fi "imagename eq redis-server.exe" >nul
taskkill /t /f /fi "windowtitle eq fake-docker*" >nul
timeout /t 3 /nobreak >nul
rd /s /q "c:\tmp\zookeeper">nul
echo ^>^>^>^>^>^>^>^>clean up context done!
title fake-docker
echo ^>^>^>^>^>^>^>^>bootstrapping redis...
start /b "redis-master" "c:\dev\redis-x64-3.2.100\redis-server.exe" "c:/dev/redis-x64-3.2.100/redis.master-1.conf"
start /b "redis-slaver-1" "c:\dev\redis-x64-3.2.100\redis-server.exe" "c:/dev/redis-x64-3.2.100/redis.slaver-1.conf"
start /b "redis-slaver-2" "c:\dev\redis-x64-3.2.100\redis-server.exe" "c:/dev/redis-x64-3.2.100/redis.slaver-2.conf"
start /b "redis-sentinel-1" "c:\dev\redis-x64-3.2.100\redis-server.exe" "c:/dev/redis-x64-3.2.100/redis.sentinel-1.conf" --sentinel
start /b "redis-sentinel-2" "c:\dev\redis-x64-3.2.100\redis-server.exe" "c:/dev/redis-x64-3.2.100/redis.sentinel-2.conf" --sentinel
start /b "redis-sentinel-3" "c:\dev\redis-x64-3.2.100\redis-server.exe" "c:/dev/redis-x64-3.2.100/redis.sentinel-3.conf" --sentinel
echo ^>^>^>^>^>^>^>^>done!
echo ^>^>^>^>^>^>^>^>bootstrapping zookeeper...
start /b "zookeeper-dev" "c:\dev\apache-zookeeper-3.6.3-bin\bin\zkServer.cmd"
echo ^>^>^>^>^>^>^>^>system is hot!

最終版本,實現了一鍵啟停,只會產生一個命令列視窗,屬於能用的範疇了。在此基礎上,可以根據專案情況,自行新增其他中介軟體或基礎服務的啟停命令。

0x04 小結

分散式應用偵錯和部署不可避免會面臨幾個問題:

應用啟停困難

通常由於架構的原因,為了偵錯某個中間節點或上游應用的功能,需要把相關的應用都啟動起來,如果手工啟停,無疑是痛苦的。

外部系統不問題

如果不能把整個系統都在本地啟動起來,那麼本地就會有一部分服務依賴於外部公共環境,它們通常不止一個人甚至不止一個團隊在用。 一旦外部服務不可用,就會影響到原生的開發和測試。

因此準備一個微型本地開發環境是有必要的,至少在開發和偵錯階段。況且如果最困難的啟停問題被解決了,何樂不為呢?