้ ็ฝฎไธญๅฟ๏ผ่ฎฉๆนๅจ"็ง็บง็ๆ"็ๅนๅ่ฑ้ ๐ง
่ฟ้็"็ง็บง"๏ผๆฏ็็็ง็บง๏ผไธๆฏ"็ญๆๅๆฏๅๅก้ๅฏไธไธ"็้ฃ็ง็ง็บงใ
ไธใๅ ่ฎฒไธช"่กๆณช"ๆ ไบ ๐
ๅจไบไธๅ5็น๏ผ่ฟ่ฅ็ซๆฅ็ซ็ๅฐ่ท่ฟๆฅ๏ผ"้ ้ฟ๏ผๆดปๅจๅฅๅฑ่ฆ็ฟปๅ๏ผ่ๆฟ่ฏด็ฐๅจๅฐฑไธ๏ผ"
ไฝ ๅนไบๅฃๆฐ๏ผๆๅผๆๅกๅจ๏ผๆพๅฐ้ ็ฝฎๆไปถ๏ผๅฐๅฟ็ฟผ็ฟผๅฐๆนๅฎไฟๅญใ็ถๅโโ
# ้ๅฏๆๅกA
ssh server-a "systemctl restart game-service"
# ้ๅฏๆๅกB
ssh server-b "systemctl restart game-service"
# ้ๅฏๆๅกC...็ญ็ญ๏ผ่ฟๆDใEใF๏ผ
็ญไฝ ๅฟๅฎ๏ผๅคฉ้ฝ้ปไบใ่ฟ่ฅๅๆฅ้ฎ๏ผ"ๆไน่ฟๆฒก็ๆ๏ผ็ฉๅฎถ้ฝๅจๆ่ฏไบ๏ผ"
ไฝ ๆ ๅฅๅฐ่ฏด๏ผ"ๅจ้ๅฏไบ,ๅ็ญ็ญ...่ฟๆ3ๅฐๆๅกๅจๆฒก้ๅฏๅฎ..."
ๆดๆจ็ๆฏ๏ผไฝ ๆ นๆฌไธ็กฎๅฎ่ฟๆๆฒกๆๆผๆ็ๆๅกๅจใ้ ็ฝฎๆฃ่ฝๅจๅไธชๆๅกๅจ๏ผๅ่ๅจๅไธชๅฐๆน็้ฅๅ๏ผไฝ ๅฟไบๅ ถไธญไธไธชๅจๅชใ
่ฟไธๆฏๆ็้ฎ้ข๏ผๆฏๅฟๆ้ฎ้ขโโ่ฐๆฟๆๅ ไธบๆนไธช้ ็ฝฎๅฐฑๅ ็ญ๏ผ
ไบใไธบไปไน้่ฆ้ ็ฝฎไธญๅฟ๏ผ็ต้ญไธ้ฎ ๐ค
2.1 ้ ็ฝฎๆไปถ"ๆปกๅคฉ้ฃ"๏ผๆไน็ฎก๏ผ
ไผ ็ปๆถๆ้๏ผ้ ็ฝฎๆไปถๅไปฃ็ ไธๆ ท๏ผ่บบๅจๅไธชๆๅกๅจ็่ง่ฝ๏ผ
/server-a/config/database.yml
/server-b/config/redis.yml
/server-c/config/game-config.json
...่ฟๆ่ฐ๏ผๆฒกไบบ็ฅ้
ไฝ ๆณๆนไธชๆฐๆฎๅบ่ฟๆฅ๏ผๆต็จๆฏ่ฟๆ ท็๏ผ
- ่ฎฐๅพ๏ผๆ็๏ผ้ ็ฝฎๅจๅชๅฐๆๅกๅจ
- SSH ็ปๅฝไธๅป
vim config.yml๏ผๅฐๅฟ็ฟผ็ฟผๅฐๆน:wqไฟๅญ- ้ๅฏๆๅก
- ็ฅ็ฅทๆฒกๆน้
่ฟๅฐฑๅไฝ ๆ้ฅๅ่ๅจๅไธชๅฐๆน๏ผ็ถๅๅฟไบๅ ถไธญไธไธชๅจๅชใ็ญไฝ ้่ฆๅผ้จ็ๆถๅ๏ผๅช่ฝไธไธชไธช่ฏใ
2.2 ไฟฎๆน้ ็ฝฎ = ้ๅฏๆๅก๏ผๅญไปไน๏ผ
ๆนไปฃ็ ่ฆ้ๅฏ๏ผๅคงๅฎถ้ฝ่ฝๆฅๅโโๆฏ็ซไปฃ็ ้ป่พๅไบๅใ
ไฝๆนไธช่ถ ๆถๆถ้ดใๅผๅ ณใๆดปๅจๅฅๅฑๅ็๏ผไน่ฆ้ๅฏ๏ผ
# ๅฐฑๆนไบ่ฟไธช
timeout: 30 โ timeout: 60
# ็ปๆ่ฆ่ฟๆ ท
systemctl restart my-service # ็ญๅพ
30็ง...
่ฟๅฐฑๅฅฝๆฏไฝ ่ฐไธช็ฉบ่ฐๆธฉๅบฆ๏ผ้่ฆๆๆดๆ ๆฅผ็็ต้ธๆไบๅๅผใ
2.3 ๅค็ฏๅข็ฎก็๏ผ่ฐๆฅไฟๅฝ๏ผ
ๅผๅ็ฏๅขใๆต่ฏ็ฏๅขใ้ขๅ็ฏๅขใ็ไบง็ฏๅข...
ๆฏไธช็ฏๅข้ฝๆ่ชๅทฑ็ๆฐๆฎๅบใ็ผๅญใๅๅ๏ผ
# ๅผๅ็ฏๅข
database:
host: dev-mysql.internal
port: 3306
# ๆต่ฏ็ฏๅข
database:
host: test-mysql.internal
port: 3306
# ็ไบง็ฏๅข
database:
host: prod-mysql.internal # โ ๏ธ ่ฟไธช็ปๅฏนไธ่ฝๆ้๏ผ
port: 3306
ไฝ ๆไนไฟ่ฏ็ไบง็ฏๅข็้ ็ฝฎไธไผๆๆปๅคๅถๅฐๆต่ฏ็ฏๅข๏ผ
ๆๅคงๅ็ๅฎๆกไพ๏ผๅๆจ3็น๏ผๅผ็ญๅๅญฆๆๆต่ฏ็ฏๅข็้ ็ฝฎ่ฆ็ๅฐไบ็ไบง็ฏๅข๏ผๅฏผ่ดๅ จ็ซๆฐๆฎๅบ่ฟๆฅ้่ฏฏ๏ผๆๅก้ชๅดฉใ
ไธใๆไปฌ็้ ็ฝฎไธญๅฟๆถๆ ๐ฏ
ๅบไบๆธธๆๅ่กๅนณๅฐ็ไธๅก็น็น๏ผๆไปฌ่ช็ ไบ configcenter ้ ็ฝฎไธญๅฟๆๅกใ
3.1 ๆดไฝๆถๆ่ฎพ่ฎก
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ configcenter ๆถๆ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โ
โ โ Web็ฎก็ๅฐ โ โ API ๆๅก โ โ ๅญๅจๅฑ โ โ
โ โ (Portal) โโโโโถโ (Server) โโโโโถโ (MySQL) โ โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โ
โ โ โ โ
โ โ โผ โ
โ โ โโโโโโโโโโโโ โ
โ โ โ ๅๆด้็ฅ โ โ
โ โ โ (Notify) โ โ
โ โ โโโโโโโโโโโโ โ
โ โ โ โ
โโโโโโโโโโผโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โผ โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๅฎขๆท็ซฏ SDK (Go/Java) โ
โ โโโโโโโ โโโโโโโ โโโโโโโ โโโโโโโ โ
โ โๆธธๆๆโ โๆฏไปๆโ โๅน้
ๆโ โ่ฟ่ฅๅฐโ โ
โ โโโโโโโ โโโโโโโ โโโโโโโ โโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
| ็ปไปถ | ๆๆฏๆ | ่่ดฃ |
|---|---|---|
| Web็ฎก็ๅฐ | React + Ant Design | ้ ็ฝฎ็ๅฏ่งๅ็ฎก็็้ข |
| APIๆๅก | Go + Gin | ้ ็ฝฎ็ๅขๅ ๆนๆฅใๆ้ๆงๅถ |
| ๅญๅจๅฑ | MySQL 8.0 | ้ ็ฝฎๆไน ๅๅญๅจ |
| ๅๆด้็ฅ | Go + ้ฟ่ฝฎ่ฏข | ้ ็ฝฎๅๆดๅฎๆถๆจ้ |
| ๅฎขๆท็ซฏSDK | Go / Java | ๅบ็จ้ๆ๏ผๆๅๅ็ๅฌ้ ็ฝฎ |
3.2 ๆ ธๅฟ่ฝๅ
่ฝๅไธ๏ผ้ไธญๅญๅจ + ๅฝๅ็ฉบ้ด้็ฆป
ๆๆ้ ็ฝฎ็ปไธๅญๅจๅจ MySQL๏ผ้่ฟๅฝๅ็ฉบ้ด๏ผnamespace๏ผๅฎ็ฐๅค็ฏๅข้็ฆป๏ผ
-- ้
็ฝฎ่กจ็ปๆ
CREATE TABLE config (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
namespace VARCHAR(100) NOT NULL COMMENT 'ๅฝๅ็ฉบ้ด๏ผๅฆ๏ผdev/test/prod',
config_key VARCHAR(255) NOT NULL COMMENT '้
็ฝฎ้ฎ',
config_value TEXT NOT NULL COMMENT '้
็ฝฎๅผ๏ผJSON๏ผ',
version INT NOT NULL DEFAULT 1 COMMENT '็ๆฌๅท',
is_deleted BOOLEAN DEFAULT FALSE COMMENT '้ป่พๅ ้ค',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_namespace_key (namespace, config_key),
KEY idx_namespace (namespace)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
# ๅฝๅ็ฉบ้ด็คบไพ
application:
dev: # ๅผๅ็ฏๅข
game:
timeout: 30
debug: true
test: # ๆต่ฏ็ฏๅข
game:
timeout: 30
debug: false
prod: # ็ไบง็ฏๅข
game:
timeout: 60
debug: false
่ฟๅฐฑๅๅ ฌๅธๅชๆไธไธชๆกฃๆกๅฎค๏ผไฝๅไบไธๅ็ๆๅญ๏ผๅๅๆใ่ดขๅกๆใไบบไบๆใ่ฐ่ฆ็้ฝๅป้ฃ้ๆฅ๏ผไฝๅช่ฝ็่ชๅทฑๆๆ้็ๆๅญใ
่ฝๅไบ๏ผ็ๆฌ็ฎก็ + ๅๆดๅฎก่ฎก
ๆฏๆฌกไฟฎๆน้ฝๆ่ฎฐๅฝ๏ผ่ฐๆน็ใไปไนๆถๅๆน็ใๆนไบไปไน๏ผ
-- ้
็ฝฎๅๆดๅๅฒ่กจ
CREATE TABLE config_history (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
config_id BIGINT NOT NULL COMMENT '้
็ฝฎID',
namespace VARCHAR(100) NOT NULL,
config_key VARCHAR(255) NOT NULL,
old_value TEXT COMMENT 'ๆงๅผ',
new_value TEXT COMMENT 'ๆฐๅผ',
operator VARCHAR(100) NOT NULL COMMENT 'ๆไฝไบบ',
change_type VARCHAR(20) NOT NULL COMMENT 'ๅๆด็ฑปๅ๏ผCREATE/UPDATE/DELETE',
comment VARCHAR(500) COMMENT 'ๅๆด่ฏดๆ',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
KEY idx_config_id (config_id),
KEY idx_namespace_key (namespace, config_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
// ้
็ฝฎๅๆดๅๅฒ็คบไพ
{
"configKey": "game.activity.bonusRate",
"changes": [
{
"version": 5,
"oldValue": "1.0",
"newValue": "2.0",
"operator": "zhangsan",
"timestamp": "2026-02-28 14:30:00",
"comment": "ๆฅ่ๆดปๅจๅฅๅฑ็ฟปๅ"
}
]
}
ๅบไบ้ฎ้ข๏ผไธ้ฎๅๆปๅฐไธไธไธช็ๆฌใ่ฟๅฐฑๅ Git ็ฎก็ไปฃ็ ไธๆ ท๏ผ้ ็ฝฎไน้่ฆ็ๆฌๆงๅถใ
่ฝๅไธ๏ผ็ญๆดๆฐ โก๏ผๆ ธๅฟไปทๅผ๏ผ
็ญๆดๆฐๅฎ็ฐๅ็๏ผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ็ญๆดๆฐๆต็จ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ 1. ๆๅกๅฏๅจๆถไป้
็ฝฎไธญๅฟๆๅ้
็ฝฎ โ
โ โโโโโโโโโโโ HTTP GET โโโโโโโโโโโโ โ
โ โ ๆธธๆๆ โ โโโโโโโโโโโโโโโโถโconfigcenterโ โ
โ โโโโโโโโโโโ โโโโโโโโโโโโ โ
โ โ
โ 2. ๆๅกๅปบ็ซ้ฟ่ฝฎ่ฏข่ฟๆฅ๏ผ็ๅฌ้
็ฝฎๅๅ โ
โ โโโโโโโโโโโ Long Polling โโโโโโโโโโโโ โ
โ โ ๆธธๆๆ โ โโโโโโโโโโโโโโโโถโconfigcenterโ โ
โ โโโโโโโโโโโ โโโโโโโโโโโโ โ
โ โ
โ 3. ้
็ฝฎๅๆดๆถ๏ผ้
็ฝฎไธญๅฟ็ซๅปๅๅบ โ
โ โโโโโโโโโโโ ่ฟๅๆฐ้
็ฝฎ โโโโโโโโโโโโ โ
โ โ ๆธธๆๆ โ โโโโโโโโโโโโโโโโโconfigcenterโ โ
โ โโโโโโโโโโโ โโโโโโโโโโโโ โ
โ โ
โ 4. ๆๅกๆถๅฐๆฐ้
็ฝฎ๏ผๆดๆฐๅ
ๅญ๏ผๆ ้้ๅฏ๏ผ โ
โ โโโโโโโโโโโ โ
โ โ ๆธธๆๆ โ reload config... done! (3็งๅ
) โ
โ โโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
่ฝๅๅ๏ผ็ฐๅบฆๅๅธ
ๆฐ้ ็ฝฎไธๆขๅ จ้ไธ็บฟ๏ผๅ ๆจ็ป้จๅๅฎไพ่ฏ่ฏ๏ผ
็ฐๅบฆๅๅธๆต็จ๏ผ
5% โโโถ ่งๅฏ1ๅฐๆถ โโโถ ๆ ๅผๅธธ โโโถ 20% โโโถ ่งๅฏ2ๅฐๆถ โโโถ 50% โโโถ 100%
โ โ
โโโโถ ๆ้ฎ้ข๏ผไธ้ฎๅๆป โโโ
่ฟๅฐฑๅๆฐ่ฏๅ ๅไธดๅบ่ฏ้ช๏ผ็กฎ่ฎคๅฎๅ จๅๅคง่งๆจกๆจๅนฟใ
่ฝๅไบ๏ผๆ้ๆงๅถ
ๅบไบ RBAC ็ๆ้ๆจกๅ๏ผ็ฒพ็ปๆงๅถ่ฐ่ฝๆน้ ็ฝฎ๏ผ
ๆ้็ฉ้ต๏ผ
โโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโ
โ ่ง่ฒ โ ๅผๅ็ฏๅขโ ๆต่ฏ็ฏๅขโ ็ไบง็ฏๅขโ
โโโโโโโโโโโผโโโโโโโโโผโโโโโโโโโผโโโโโโโโโค
โ ๅฎไน ็ โ ๅช่ฏป โ ๅช่ฏป โ ๆ ๆ้ โ
โ ๅผๅ โ ่ฏป+ๅ โ ่ฏป+ๅ โ ๅช่ฏป โ
โ ่ฟ็ปด โ ่ฏป+ๅ โ ่ฏป+ๅ โ ่ฏป+ๅ โ
โ ๆๆฏ่ด่ดฃไบบโ ่ฏป+ๅ โ ่ฏป+ๅ โ ่ฏป+ๅ+ๅฎกๆนโ
โโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโดโโโโโโโโโ
ๅใ้ ็ฝฎๆจ้ๆบๅถ๏ผ้ฟ่ฝฎ่ฏขๅฎ็ฐ ๐
้ ็ฝฎๆดๆฐๅ๏ผๆไน่ฎฉๆๅก็ฅ้๏ผ่ฟๆฏ้ ็ฝฎไธญๅฟ็ๆ ธๅฟๆๆฏ้พ็นใ
4.1 ไธบไปไน้ๆฉ้ฟ่ฝฎ่ฏข๏ผ
ๆไปฌๅฏนๆฏไบไธ็งๆนๆก๏ผๆ็ป้ๆฉไบ้ฟ่ฝฎ่ฏข๏ผ
| ๆนๆก | ๅฎๆถๆง | ๆๅก็ซฏๅๅ | ๅฎ็ฐๅคๆๅบฆ | ๆ็ป้ๆฉ |
|---|---|---|---|---|
| ๆจๆจกๅผ | ๆฏซ็ง็บง | ้ซ๏ผ็ปดๆคๅคง้่ฟๆฅ๏ผ | ๅคๆ | โ |
| ๆๆจกๅผ | ็ง็บงๅปถ่ฟ๏ผ่ฝฎ่ฏข้ด้๏ผ | ไธญ๏ผ้ข็นๆ ๆ่ฏทๆฑ๏ผ | ็ฎๅ | โ |
| ้ฟ่ฝฎ่ฏข | 3็งๅ | ไฝ๏ผๆ่ตท็ญๅพ ๏ผ | ไธญ็ญ | โ |
- ๅฎๆถๆงๅฅฝ๏ผ้ ็ฝฎๅๆดๅ3็งๅ ็ๆ
- ๆๅก็ซฏๅๅๅฐ๏ผ่ฏทๆฑ่ขซๆ่ตท๏ผไธๅ ็จ CPU
- ๅฏ้ ๆง้ซ๏ผๅฎขๆท็ซฏไธปๅจ่ฏทๆฑ๏ผไธไพ่ต็ฝ็ป็จณๅฎๆง
4.2 ้ฟ่ฝฎ่ฏขๅฎ็ฐ็ป่
ๅฎขๆท็ซฏๅฎ็ฐ๏ผGo SDK๏ผ
// ๅฎขๆท็ซฏ้ฟ่ฝฎ่ฏขๆ ธๅฟ้ป่พ
func (c *ConfigClient) startLongPolling() {
for {
// ๅ่ตท้ฟ่ฝฎ่ฏข่ฏทๆฑ๏ผ่ถ
ๆถๆถ้ด30็ง
resp, err := c.httpClient.Get(
c.serverURL + "/api/v1/configs/watch",
func(req *http.Request) {
// ๆบๅธฆๅฝๅ้
็ฝฎ็ๆฌๅท
req.Header.Set("X-Config-Version", c.getCurrentVersion())
// ่ฎพ็ฝฎ30็ง่ถ
ๆถ
req.Header.Set("X-Long-Polling-Timeout", "30")
},
)
if err != nil {
// ็ฝ็ป้่ฏฏ๏ผ็ญๅพ
ๅ้่ฏ
time.Sleep(5 * time.Second)
continue
}
if resp.StatusCode == 200 {
// ๆ้
็ฝฎๅๆด
var changes []ConfigChange
json.NewDecoder(resp.Body).Decode(&changes)
// ๅบ็จๆฐ้
็ฝฎ
c.applyConfigChanges(changes)
} else if resp.StatusCode == 304 {
// ๆ ๅๆด๏ผNot Modified๏ผ๏ผ็ปง็ปญไธไธ่ฝฎ่ฝฎ่ฏข
}
// ็ซๅณๅ่ตทๆฐ็้ฟ่ฝฎ่ฏข
}
}
// ๅบ็จ้
็ฝฎๅๆด
func (c *ConfigClient) applyConfigChanges(changes []ConfigChange) {
for _, change := range changes {
// ๆดๆฐๅ
ๅญไธญ็้
็ฝฎ
c.configCache.Store(change.Key, change.Value)
// ่งฆๅๅ่ฐๅฝๆฐ
if listener, ok := c.listeners[change.Key]; ok {
listener.onChange(change)
}
}
}
ๆๅก็ซฏๅฎ็ฐ๏ผGo API๏ผ
// ้ฟ่ฝฎ่ฏขๆฅๅฃ
func (h *ConfigHandler) WatchConfig(c *gin.Context) {
namespace := c.Query("namespace")
currentVersion := c.GetHeader("X-Config-Version")
timeout := c.GetHeader("X-Long-Polling-Timeout")
// ้ป่ฎค30็ง่ถ
ๆถ
pollTimeout := 30 * time.Second
if timeout != "" {
if seconds, err := strconv.Atoi(timeout); err == nil {
pollTimeout = time.Duration(seconds) * time.Second
}
}
ctx, cancel := context.WithTimeout(c.Request.Context(), pollTimeout)
defer cancel()
// ๆฃๆฅๆฏๅฆๆ้
็ฝฎๅๆด
changes := h.checkConfigChanges(namespace, currentVersion)
if len(changes) > 0 {
// ๆๅๆด๏ผ็ซๅณ่ฟๅ
c.JSON(http.StatusOK, changes)
return
}
// ๆ ๅๆด๏ผ็ญๅพ
้
็ฝฎๅๆดไบไปถๆ่ถ
ๆถ
watcher := h.notifyService.Watch(namespace)
defer watcher.Stop()
select {
case <-watcher.Changed():
// ้
็ฝฎๅๆดไบ๏ผ่ฟๅๆฐ้
็ฝฎ
changes := h.checkConfigChanges(namespace, currentVersion)
c.JSON(http.StatusOK, changes)
case <-ctx.Done():
// ่ถ
ๆถ๏ผ่ฟๅ 304 Not Modified
c.Status(http.StatusNotModified)
}
}
// ๆฃๆฅ้
็ฝฎๅๆด
func (h *ConfigHandler) checkConfigChanges(namespace, currentVersion string) []ConfigChange {
// ๆฅ่ฏขๆๆฐ็ๆฌ
latestVersion := h.configService.GetLatestVersion(namespace)
if latestVersion <= currentVersion {
return nil
}
// ๆฅ่ฏขๅๆด็้
็ฝฎ
return h.configService.GetChangedConfigs(namespace, currentVersion)
}
้ ็ฝฎๅๆด้็ฅๆๅก
// ้
็ฝฎๅๆด้็ฅๆๅก
type NotifyService struct {
mu sync.RWMutex
watchers map[string][]*Watcher
eventChan chan ConfigChangeEvent
}
type Watcher struct {
Changed chan struct{}
stopped chan struct{}
}
func (s *NotifyService) Start() {
// ๅฏๅจไบไปถๅค็ๅพช็ฏ
go func() {
for event := range s.eventChan {
s.notifyWatchers(event.Namespace)
}
}()
}
// ้
็ฝฎๅๆดๆถ่ฐ็จ
func (s *NotifyService) NotifyChange(namespace string) {
s.eventChan <- ConfigChangeEvent{Namespace: namespace}
}
// ้็ฅๆๆ็ญๅพ
็ watcher
func (s *NotifyService) notifyWatchers(namespace string) {
s.mu.RLock()
defer s.mu.RUnlock()
if watchers, ok := s.watchers[namespace]; ok {
for _, watcher := range watchers {
select {
case watcher.Changed <- struct{}{}:
default:
// ้ฒๆญข้ปๅก
}
}
}
}
4.3 ๆง่ฝไผๅ
ไธบไบๆฏๆๅคง่งๆจกๆๅกๅฎไพ๏ผ1000+๏ผ๏ผๆไปฌๅไบไปฅไธไผๅ๏ผ
ไผๅไธ๏ผ่ฟๆฅๆฑ ๅค็จ
// ๅฎขๆท็ซฏไฝฟ็จ่ฟๆฅๆฑ
client := &http.Client{
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
},
Timeout: 35 * time.Second, // ๆฏ้ฟ่ฝฎ่ฏข่ถ
ๆถๅค5็ง
}
ไผๅไบ๏ผๆน้ๅๆด้็ฅ
// ๆน้ๆถ้ๅๆด๏ผๅๅฐ้็ฅ้ข็
type BatchNotifier struct {
timer *time.Timer
pendingChan chan string
batchChan chan []string
}
func (n *BatchNotifier) Start() {
batch := make([]string, 0, 10)
for {
select {
case namespace := <-n.pendingChan:
batch = append(batch, namespace)
if len(batch) >= 10 {
// ่พพๅฐๆน้ๅคงๅฐ๏ผ็ซๅณๅ้
n.batchChan <- batch
batch = make([]string, 0, 10)
n.timer.Reset(100 * time.Millisecond)
} else {
// ้็ฝฎๅฎๆถๅจ๏ผ100msๅๆน้ๅ้
n.timer.Reset(100 * time.Millisecond)
}
case <-n.timer.C:
// ๅฎๆถๅจๅฐๆ๏ผๅ้ๆน้้็ฅ
if len(batch) > 0 {
n.batchChan <- batch
batch = make([]string, 0, 10)
}
}
}
}
ไผๅไธ๏ผ้ ็ฝฎ็ๆฌๅทๅฟซ้ๆฏๅฏน
// ไฝฟ็จ็ๆฌๅทๅฟซ้ๅคๆญๆฏๅฆๆๅๆด
func (s *ConfigService) GetLatestVersion(namespace string) string {
// ไป็ผๅญไธญ่ทๅ
if version, ok := s.versionCache.Get(namespace); ok {
return version.(string)
}
// ไปๆฐๆฎๅบๆฅ่ฏขๆๆฐ็ๆฌ
version := s.configDao.GetLatestVersion(namespace)
s.versionCache.Set(namespace, version, 5*time.Minute)
return version
}
ไบใ็ฐๅบฆๅๅธไธ็ๆฌ็ฎก็ ๐
5.1 ็ฐๅบฆๅๅธๅฎ็ฐ
ๆไปฌๆฏๆๆๅฎไพ่ฟ่ก็ฐๅบฆๅๅธ๏ผ
-- ็ฐๅบฆ้
็ฝฎ่กจ
CREATE TABLE config_gray (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
config_id BIGINT NOT NULL COMMENT '้
็ฝฎID',
gray_value TEXT NOT NULL COMMENT '็ฐๅบฆ้
็ฝฎๅผ',
gray_type VARCHAR(20) NOT NULL COMMENT '็ฐๅบฆ็ฑปๅ๏ผinstance/percent',
gray_rule TEXT NOT NULL COMMENT '็ฐๅบฆ่งๅ๏ผๅฎไพIDๅ่กจๆ็พๅๆฏ',
status VARCHAR(20) NOT NULL COMMENT '็ถๆ๏ผgray/publish/rollback',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
KEY idx_config_id (config_id),
KEY idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
็ฐๅบฆๅๅธๆต็จ
// ็ฐๅบฆๅๅธๆๅก
type GrayReleaseService struct{}
// ่ทๅ้
็ฝฎ๏ผ่ชๅจๅคๆญๆฏๅฆ็ฐๅบฆ๏ผ
func (s *GrayReleaseService) GetConfig(namespace, key, instanceID string) (string, error) {
// 1. ๆฃๆฅๆฏๅฆๆ่ฏฅๅฎไพ็็ฐๅบฆ้
็ฝฎ
grayConfig := s.grayDao.GetGrayConfig(namespace, key, instanceID)
if grayConfig != nil {
return grayConfig.GrayValue, nil
}
// 2. ๆฃๆฅๆฏๅฆๆๆ็พๅๆฏ็็ฐๅบฆ้
็ฝฎ
percentConfig := s.grayDao.GetPercentGrayConfig(namespace, key)
if percentConfig != nil && s.shouldApplyGray(instanceID, percentConfig) {
return percentConfig.GrayValue, nil
}
// 3. ่ฟๅๆญฃๅผ้
็ฝฎ
return s.configDao.GetConfig(namespace, key)
}
// ๅคๆญๅฎไพๆฏๅฆๅบ่ฏฅๅบ็จ็ฐๅบฆ้
็ฝฎ
func (s *GrayReleaseService) shouldApplyGray(instanceID string, config *GrayConfig) bool {
// ๅฎไพIDๅๅธๅผๅๆจก
hash := crc32.ChecksumIEEE([]byte(instanceID))
percent := config.GrayPercent
return int(hash%100) < percent
}
5.2 ็ๆฌ็ฎก็
็ๆฌๅๆปๅฎ็ฐ
// ็ๆฌๆๅก
type ConfigVersionService struct{}
// ๅๆปๅฐๆๅฎ็ๆฌ
func (s *ConfigVersionService) Rollback(configID int64, targetVersion int) error {
// 1. ๆฅ่ฏข็ฎๆ ็ๆฌ็้
็ฝฎๅผ
history, err := s.historyDao.GetByVersion(configID, targetVersion)
if err != nil {
return err
}
// 2. ๅผๅฏไบๅก
tx := s.db.Begin()
// 3. ๆดๆฐๅฝๅ้
็ฝฎ
err = s.configDao.UpdateValue(tx, configID, history.NewValue)
if err != nil {
tx.Rollback()
return err
}
// 4. ่ฎฐๅฝๅๆปๆไฝ
err = s.historyDao.Save(tx, &ConfigHistory{
ConfigID: configID,
OldValue: s.getCurrentValue(configID),
NewValue: history.NewValue,
Operator: "system",
ChangeType: "ROLLBACK",
Comment: fmt.Sprintf("ๅๆปๅฐ็ๆฌ %d", targetVersion),
})
if err != nil {
tx.Rollback()
return err
}
tx.Commit()
return nil
}
ๅ ญใๆธธๆ่กไธ็้ ็ฝฎไธญๅฟๅฎ่ทต ๐ฎ
ๆธธๆ่กไธๅฏน้ ็ฝฎไธญๅฟ็้ๆฑๆด่ฟซๅโโๆดปๅจ้ข็นใๅผๅ ณๅคใๆถๆๆงๅผบใ
6.1 ๆธธๆ้ ็ฝฎ็็นๆฎๆง
ๆธธๆ้ ็ฝฎๅๆฎ้ไธๅก้ ็ฝฎๆๅพๅคงๅบๅซ๏ผ
| ็นๆง | ๆฎ้ไธๅก้ ็ฝฎ | ๆธธๆ้ ็ฝฎ |
|---|---|---|
| ๅๆด้ข็ | ๆฏๅคฉ 1-10 ๆฌก | ๆฏๅฐๆถๅฏ่ฝๅคๆฌก |
| ๆถๆๆง่ฆๆฑ | ๅ้็บง | ็ง็บง๏ผๆดปๅจๅ็นๅผๅฏ๏ผ |
| ้ ็ฝฎ็ฑปๅ | ้ฎๅผๅฏนไธบไธป | ๅคๆๅฏน่ฑก๏ผ่ฃ ๅคใๆ่ฝใๆดปๅจ๏ผ |
| ๆฐๆฎ้ | KB ็บง | MB ็่ณ GB ็บง |
| ็ฐๅบฆ้ๆฑ | ๆๅฎไพ | ๆ็ฉๅฎถใๅบๆใๆธ ้ |
6.2 ๆธธๆ้ ็ฝฎๅๅฑ่ฎพ่ฎก
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๆธธๆ้
็ฝฎๅๅฑๆถๆ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Layer 1: ็ณป็ป้
็ฝฎ๏ผSystem Config๏ผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ ๆฐๆฎๅบ่ฟๆฅใRedis ๅฐๅใๆฅๅฟ็บงๅซ โ โ
โ โ ๅๆด้ข็๏ผๆไฝ โ โ
โ โ ๆฏๅฆ็ญๆด๏ผ้ๅธธไธ้่ฆ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Layer 2: ไธๅก้
็ฝฎ๏ผBusiness Config๏ผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ ๅ่ฝๅผๅ
ณใๆดปๅจๅผๅ
ณใๆจ่็ฎๆณๅๆฐ โ โ
โ โ ๅๆด้ข็๏ผๆฏๅคฉ โ โ
โ โ ๆฏๅฆ็ญๆด๏ผ้่ฆ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Layer 3: ๆธธๆๆฐๆฎ๏ผGame Data๏ผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ ่ฃ
ๅคๅฑๆงใๆ่ฝ้
็ฝฎใๆดปๅจ่งๅ โ โ
โ โ ๅๆด้ข็๏ผ้็ๆฌๆดๆฐ โ โ
โ โ ๆฏๅฆ็ญๆด๏ผ้่ฆ๏ผ็ดงๆฅไฟฎๅค๏ผ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
6.3 ๅฎ้ ๆกไพ๏ผๆดปๅจ้ ็ฝฎ็ญๆดๆฐ
- ๆๅๆน้ ็ฝฎๆไปถ
- ๅๅฎๆถไปปๅก๏ผ20:00 ้ๅฏๆๅก
- ็ฅ็ฅท้ๅฏๆๅ
// ๆดปๅจ้
็ฝฎ็ปๆ
type ActivityConfig struct {
ActivityID string `json:"activityId"`
BonusRate float64 `json:"bonusRate"`
StartTime time.Time `json:"startTime"`
EndTime time.Time `json:"endTime"`
Enabled bool `json:"enabled"`
}
// ้
็ฝฎๅๆด็ๅฌๅจ
func (s *ActivityService) OnConfigChange(change *ConfigChange) {
var newConfig ActivityConfig
json.Unmarshal([]byte(change.Value), &newConfig)
// ๆฃๆฅๆฏๅฆ้่ฆ็ซๅณ็ๆ
if newConfig.Enabled && time.Now().After(newConfig.StartTime) {
// ๆดปๅจๅทฒๅผๅง๏ผ็ซๅณ็ๆ
s.UpdateBonusRate(newConfig.BonusRate)
log.Infof("ๆดปๅจ้
็ฝฎ็ญๆดๆฐๆๅ๏ผ%+v", newConfig)
}
}
- ๆๅๅจ configcenter ๅๅปบ้ ็ฝฎ๏ผenabled=false๏ผ
- 19:59:30๏ผ่ฟ่ฅไฟฎๆน้ ็ฝฎ๏ผenabled=true, bonusRate=2.0๏ผ
- 20:00:00๏ผ้ ็ฝฎๆจ้ๅฐๆๆๆๅก๏ผๆดปๅจ็ๆ
- ๅ จ็จๆ ้้ๅฏ๏ผ็ฉๅฎถๆ ๆ็ฅ
6.4 ๅ่ฝๅผๅ ณ๏ผ็บฟไธๆ็ๅ
ๆฐๅ่ฝไธ็บฟ๏ผๆๆๅบ้ฎ้ขใๅ่ฝๅผๅ ณ่ฎฉไฝ ่ฝๅฟซ้ๆญข่กใ
// ๅ่ฝๅผๅ
ณ้
็ฝฎ
type FeatureToggle struct {
FeatureName string `json:"featureName"`
Enabled bool `json:"enabled"`
Whitelist []string `json:"whitelist"`
GrayPercent int `json:"grayPercent"`
}
// ไธๅกไปฃ็ ไธญไฝฟ็จๅ่ฝๅผๅ
ณ
func (s *NewFeatureService) Execute(player *Player) {
toggle := s.configCenter.GetConfig("feature.new-battle-system")
// ๆฃๆฅๅผๅ
ณ็ถๆ
if !toggle.Enabled {
// ๅ่ฝๅ
ณ้ญ๏ผ่ตฐ่้ป่พ
s.ExecuteOld(player)
return
}
// ๆฃๆฅ็ฝๅๅ
if contains(toggle.Whitelist, player.ID) {
s.ExecuteNew(player)
return
}
// ๆฃๆฅ็ฐๅบฆๆฏไพ
if hash(player.ID)%100 < toggle.GrayPercent {
s.ExecuteNew(player)
} else {
s.ExecuteOld(player)
}
}
ๆๆธธๆไธ็บฟๆฐๆๆ็ณป็ป๏ผๅ็ฐๆๆ่ฎก็ฎๆ Bug๏ผๅฏผ่ด้จๅ็ฉๅฎถๆๅๅผๅธธใ
- ่ฟ็ปดๅจ configcenter ๅ ณ้ญๅ่ฝๅผๅ ณ๏ผenabled=false๏ผ
- ้ ็ฝฎ3็งๅ ๆจ้ๅฐๆๆๆๅก
- ๆฐๆๆ็ณป็ป็ซๅปๅ็จ๏ผๅๅ่็ณป็ป
- ๅ จ็จๆ ้้ๅฏ๏ผ็ฉๅฎถๅ ไนๆ ๆ็ฅ
่ฟๆฏ็ดงๆฅๅๆปไปฃ็ ไผ้ ๅคไบโโๅๆปไปฃ็ ้่ฆ้ๆฐ้จ็ฝฒ๏ผ่ณๅฐๅ ๅ้๏ผๅ ณๅผๅ ณๅช้่ฆ3็งใ
6.5 ๅค็ฏๅข้็ฆป๏ผๆต่ฏๆ็ธไบไธๅฝฑๅๆญฃๅผๆ
ๆธธๆๅผๅ้ๅธธๆ๏ผๅผๅๆใๆต่ฏๆใไฝ้ชๆใๆญฃๅผๆใ
็ฏๅข้็ฆปๆถๆ๏ผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ configcenter โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโโ
โ โ ๅผๅ็ฏๅข โ โ ๆต่ฏ็ฏๅข โ โ ไฝ้ช็ฏๅข โ โ ๆญฃๅผ็ฏๅข โโ
โ โ (dev) โ โ (test) โ โ (stage) โ โ (prod) โโ
โ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโโ
โ โ โ โ โ โ
โ โผ โผ โผ โผ โ
โ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโโ
โ โ ๅผๅๆ โ โ ๆต่ฏๆ โ โ ไฝ้ชๆ โ โ ๆญฃๅผๆ โโ
โ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโโ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# ๅผๅ็ฏๅข (dev)
game:
activity:
bonusRate: 10.0 # ๅผๅ็ฏๅขๅฅๅฑ10ๅ๏ผๆนไพฟๆต่ฏ
debugMode: true
# ๆต่ฏ็ฏๅข (test)
game:
activity:
bonusRate: 2.0 # ๆต่ฏ็ฏๅขๆญฃๅธธๅ็
debugMode: true
# ๆญฃๅผ็ฏๅข (prod)
game:
activity:
bonusRate: 1.0 # ๆญฃๅผ็ฏๅข้ป่ฎคไธ็ฟปๅ
debugMode: false # ๅ
ณ้ญ่ฐ่ฏๆจกๅผ
// ๅฎขๆท็ซฏๅฏๅจๆถๆๅฎๅฝๅ็ฉบ้ด๏ผ็ฏๅข๏ผ
func main() {
env := os.Getenv("ENV") // dev / test / prod
client := configcenter.NewClient(&configcenter.Config{
ServerURL: "http://config-center:8080",
Namespace: env, // ๅ
ณ้ฎ๏ผๆๅฎ็ฏๅข
AppID: "game-server",
})
client.Start()
}
ไธใconfigcenter ้จ็ฝฒๆถๆ ๐๏ธ
7.1 ็ไบง็ฏๅข้จ็ฝฒ
โโโโโโโโโโโโโโโโโโโ
โ SLB / Nginx โ
โ (่ด่ฝฝๅ่กก) โ
โโโโโโโโโโฌโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโ
โ โ โ
โผ โผ โผ
โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ
โconfigcenterโ โconfigcenterโ โconfigcenterโ
โ Node 1 โ โ Node 2 โ โ Node 3 โ
โโโโโโโฌโโโโโ โโโโโโโฌโโโโโ โโโโโโโฌโโโโโ
โ โ โ
โโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโ
โ MySQL ้็พค โ
โ (ไธปไปๅคๅถ) โ
โโโโโโโโโโโโโโโโโ
7.2 ้ซๅฏ็จไฟ้
- ้จ็ฝฒ 3 ไธช่็น๏ผ้่ฟ SLB ๅ่ด่ฝฝๅ่กก
- ไปปๆ่็นๆ ้ไธๅฝฑๅๆๅก
- ไธปๅบ่ด่ดฃๅๆไฝ
- ไปๅบ่ด่ดฃ่ฏปๆไฝ
- ไธปๅบๆ ้ๆถๅฟซ้ๅๆข
// ๅฅๅบทๆฃๆฅๆฅๅฃ
func (h *HealthHandler) Check(c *gin.Context) {
// ๆฃๆฅๆฐๆฎๅบ่ฟๆฅ
if err := h.db.Ping(); err != nil {
c.JSON(503, gin.H{"status": "unhealthy", "error": "database"})
return
}
// ๆฃๆฅๅ
ๅญไฝฟ็จ
var m runtime.MemStats
runtime.ReadMemStats(&m)
if m.Alloc > 2*1024*1024*1024 { // 2GB
c.JSON(503, gin.H{"status": "unhealthy", "error": "memory"})
return
}
c.JSON(200, gin.H{"status": "healthy"})
}
7.3 ็ๆงๆๆ
// ๅ
ณ้ฎ็ๆงๆๆ
type Metrics struct {
// QPS
RequestQPS int64
// ๅปถ่ฟ
P50Latency time.Duration
P99Latency time.Duration
// ้ฟ่ฝฎ่ฏข
ActivePollingCount int64
// ้
็ฝฎๅๆด
ConfigChangeCount int64
// ้่ฏฏ็
ErrorRate float64
}
ๅ ซใๆไฝณๅฎ่ทต ๐
8.1 ้ ็ฝฎ่ฎพ่ฎกๅๅ
// โ ้่ฏฏ๏ผ้
็ฝฎๅๆญปๅจไปฃ็ ้
const MaxLevel = 100
const BonusRate = 1.5
// โ
ๆญฃ็กฎ๏ผ้
็ฝฎไป้
็ฝฎไธญๅฟ่ฏปๅ
maxLevel := configCenter.GetInt("game.player.maxLevel", 100)
bonusRate := configCenter.GetFloat("game.activity.bonusRate", 1.5)
# โ ๅคช็ฒ๏ผไธไธช้
็ฝฎๅ
ๅซๆๆ
game: '{"maxLevel":100,"bonusRate":1.5,"timeout":30}'
# โ ๅคช็ป๏ผๆฏไธชๅญๆฎตไธไธช้
็ฝฎ
game.maxLevel: 100
game.bonusRate: 1.5
game.timeout: 30
# โ
้ไธญ๏ผๆๅ่ฝๆจกๅๅ็ป
game:
player:
maxLevel: 100
activity:
bonusRate: 1.5
system:
timeout: 30
// ๆๆ้
็ฝฎๅ ๅฏๅญๅจ
type SecureConfig struct {
DatabasePassword string `json:"databasePassword" encrypt:"true"`
APIKey string `json:"apiKey" encrypt:"true"`
}
// ๅญๅจๆถ่ชๅจๅ ๅฏ
func (s *ConfigService) SaveConfig(config *SecureConfig) error {
// ๅ ๅฏๆๆๅญๆฎต
config.DatabasePassword = s.encrypt(config.DatabasePassword)
config.APIKey = s.encrypt(config.APIKey)
// ไฟๅญๅฐๆฐๆฎๅบ
return s.configDao.Save(config)
}
// ่ฏปๅๆถ่ชๅจ่งฃๅฏ
func (s *ConfigService) GetConfig() (*SecureConfig, error) {
config := s.configDao.Get()
// ่งฃๅฏๆๆๅญๆฎต
config.DatabasePassword = s.decrypt(config.DatabasePassword)
config.APIKey = s.decrypt(config.APIKey)
return config, nil
}
8.2 ๅฎขๆท็ซฏ้ๆ็คบไพ
// ๆธธๆๆๅกๅจ้ๆ configcenter
package main
import (
"github.com/ourcompany/configcenter-go"
)
func main() {
// ๅๅงๅ้
็ฝฎไธญๅฟๅฎขๆท็ซฏ
client := configcenter.NewClient(&configcenter.Config{
ServerURL: "http://config-center:8080",
Namespace: os.Getenv("ENV"), // dev/test/prod
AppID: "game-server",
InstanceID: getInstanceID(),
})
// ๅฏๅจๅฎขๆท็ซฏ
if err := client.Start(); err != nil {
log.Fatal("ๅฏๅจ้
็ฝฎไธญๅฟๅคฑ่ดฅ๏ผ", err)
}
// ็ๅฌ้
็ฝฎๅๆด
client.Watch("game.activity.bonusRate", func(change *configcenter.ConfigChange) {
newRate := change.Value.(float64)
log.Infof("ๆดปๅจๅ็ๅๆด๏ผ%f โ %f", change.OldValue, newRate)
// ๆดๆฐๅ
ๅญไธญ็้
็ฝฎ
activityService.UpdateBonusRate(newRate)
})
// ่ทๅ้
็ฝฎๅผ
bonusRate := client.GetFloat("game.activity.bonusRate", 1.0)
log.Infof("ๅฝๅๆดปๅจๅ็๏ผ%f", bonusRate)
// ๅฏๅจๆธธๆๆๅก
gameServer.Run()
}
8.3 ็ๆงไธๅ่ญฆ
้ ็ฝฎๅๆด้่ฆ็ๆง๏ผ้ฒๆญข่ฏฏๆไฝ๏ผ
// ้
็ฝฎๅๆดๅฎก่ฎกๆฅๅฟ
type ConfigAudit struct {
Operator string
ConfigKey string
OldValue string
NewValue string
Timestamp time.Time
}
// ๆๆ้
็ฝฎๅๆดๅ่ญฆ
func (s *ConfigService) UpdateConfig(key, value, operator string) error {
// ๆฃๆฅๆฏๅฆไธบๆๆ้
็ฝฎ
if s.isSensitiveConfig(key) {
// ๅ้ๅ่ญฆ้็ฅ
s.alertService.SendAlert(
"ๆๆ้
็ฝฎๅๆดๅ่ญฆ",
fmt.Sprintf("้
็ฝฎ %s ่ขซ %s ไฟฎๆน", key, operator),
)
}
// ่ฎฐๅฝๅฎก่ฎกๆฅๅฟ
s.auditService.Log(&ConfigAudit{
Operator: operator,
ConfigKey: key,
OldValue: s.getCurrentValue(key),
NewValue: value,
Timestamp: time.Now(),
})
return nil
}
ไนใๆป็ป๏ผ้ ็ฝฎไธญๅฟ็ไปทๅผ ๐
ๆ ธๅฟ่ฆ็นๅ้กพ
- ้ ็ฝฎไธญๅฟ็ๆฌ่ดจ๏ผๆๆฃ่ฝ็้ ็ฝฎ้ไธญ็ฎก็๏ผ่ฎฉๆนๅจๅฟซ้็ๆ๏ผ่ฎฉๅๆดๆ่ฟนๅฏๅพชใ
- ็ญๆดๆฐๆฏๆ ธๅฟไปทๅผ๏ผๆน้ ็ฝฎไธ้ๅฏ๏ผ่ฟๆๆฏ้ ็ฝฎไธญๅฟๅญๅจ็ๆไนใๆไปฌ็ configcenter ๆฏๆ 3็งๅ ้ ็ฝฎ็ๆใ
- ๆจ้ๆบๅถ้้ฟ่ฝฎ่ฏข๏ผๅ ผ้กพๅฎๆถๆงๅๆง่ฝ๏ผ้ ็ฝฎๅๆดๅ3็งๅ ็ๆ๏ผๆๅก็ซฏๅๅๅฏๆงใ
- ็ฐๅบฆๅๅธๆฏๅฎๅ จ็ฝ๏ผๆฐ้ ็ฝฎๅ ๅฐ่ๅด้ช่ฏ๏ผๅๅ จ้ๅๅธใ
- ็ๆฌ็ฎก็ๆฏๆถๅ ๆบ๏ผๅบไบ้ฎ้ขไธ้ฎๅๆป๏ผๆๆๅคฑ้ๅฐๆไฝใ
- ๆธธๆ่กไธๆด้่ฆ๏ผๆดปๅจ้ข็นใๆถๆๆงๅผบ๏ผ้ ็ฝฎไธญๅฟๅ ไนๆฏๅ้ใ
- ่ช็ ็ไผๅฟ๏ผๅฎๅ จ่ชไธปๅฏๆง๏ผ้ๅฏนๆธธๆไธๅกๅบๆฏไผๅ๏ผ่ฝป้้ซๆใ
configcenter ๅธฆๆฅ็ๆนๅ
- ๆน้ ็ฝฎ = ็ปๆๅกๅจ + vim + ้ๅฏ
- ๅบ้ฎ้ข = ๆๅฟ่ไนฑๆพๅๅ
- ๅค็ฏๅข = ไบบ่ๅๆญฅ้ ็ฝฎ
- ้ ็ฝฎ็ๆ = ็ญๅพ ้ๅฏๅฎๆ
- ๆน้ ็ฝฎ = ๆๅผ็ฝ้กต + ็นๅปไฟๅญ
- ๅบ้ฎ้ข = ไธ้ฎๅๆป
- ๅค็ฏๅข = ่ชๅจ้็ฆป๏ผไบไธๅนฒๆฐ
- ้ ็ฝฎ็ๆ = 3็งๅ ๅฎๆ
ๅใๅๅจๆๅ ๐ญ
้ ็ฝฎไธญๅฟ็่ตทๆฅๆฏไธชๅฐไธ่ฅฟโโไธๅฐฑๆฏๅญไธช้ ็ฝฎๅ๏ผ
ไฝ็ๆญฃ็จ่ฟไนๅ๏ผไฝ ไผๅ็ฐ๏ผๅฎๆนๅไบไฝ ๅ้ ็ฝฎ็ๅ ณ็ณปใ
ไปฅๅ๏ผๆน้ ็ฝฎๆฏไปถ"ๅคงไบ"โโ่ฆ็ปๅฝๆๅกๅจใ่ฆๆนๆไปถใ่ฆ้ๅฏใ่ฆ็ฅ็ฅทใๆไปฅ่ฝไธๆนๅฐฑไธๆน๏ผ่ฝๆๅฐฑๆใ
็ฐๅจ๏ผๆน้ ็ฝฎๅๆ"ๅฐไบ"โโๆๅผ็ฝ้กตใๆนๅฎไฟๅญใ3็ง็ๆใ้ๅฐ้ฎ้ข่ฟ่ฝไธ้ฎๅๆปใ
ไบๆฏ๏ผไฝ ๆดๆฟๆ่ฐๆดๅๆฐใไผๅ้ ็ฝฎใๅฟซ้่ฏ้ใ
ๅจๅฟซ้่ฟญไปฃ็ไบ่็ฝๆถไปฃ๏ผ่ฐๅๅพๅฟซ๏ผ่ฐๅฐฑ่ตขใ้ ็ฝฎไธญๅฟ๏ผๅฐฑๆฏ่ฎฉไฝ ๅๅฟซ็้ฃไธชๅทฅๅ ทใ
ไธๆ้ขๅ๏ผๆถๆฏ้ๅโโ่ฎฉ็ณป็ป"ๅผๆญฅ่งฃ่ฆ"็็งๅฏๆญฆๅจ๏ผๆฌ่ฏทๆๅพ ๏ผ
๐ฌ ่ฏ่ฎบ (0)