目录
我只是想删了个没有引用的配置而已
是幽灵?明明引用了变量,静态检查却没有发现
防止犯病,利用git hook监听配置文件并推送消息
不仅要防止还得有解决方案!加个热更新
今天我想和大家分享一个惨痛的教训,就是当我一意孤行地删掉一个看起来没用的配置文件时,结果闹了件大事!
作为一个coder,我们都希望自己的代码像世外桃源一样(至少是人家的就算是shi,我自己的永远是鲜花),无污染,拥有出色的性能、可读性和可维护性。
但是,不是每个需求、每个经手的项目的代码库都像我们想象的那样整洁干净。这次,我遇到了一个需求,需要我重新编写一个全新的功能模块来替换原有的模块。而原有的模块里有一个看似无用的配置文件:
/*******************
* @/components/xxxx01/config.js
*******************/
// 原有模块依赖的角色配置 -- xxw 2010-03-02
export const ROLES_CONFIG = {
"admin": {
canDelete: true,
canViewSecrets: true,
canEdit: true
},
"auditor": {
canDelete: false,
canViewSecrets: true,
canEdit: false
},
"normal": {
canDelete: false,
canViewSecrets: false,
canEdit: false
}
};
既然要替换原有模块,我觉得这个配置应该没有用了。在静态分析的过程中,也没有发现有其他地方引用它。所以,我毫不犹豫地删掉了它。
一切看起来都很顺利,直到"回归环节"出了大麻烦。
A: 兄弟萌,uat环境的xxx页面全白屏了,报了个引用错误,是不是有变量在某处undefined了?
B: 我看了,找不到一个变量,好像是@道长 把那个变量删了。
A: 啊?@道长
道长: 啊?你们还在用那东西?我看那个模块都已经没有引用了。
C: 你又在删删删了!删掉的是哪个变量?老项目别乱动啊!
D: 这是@道长 能整出来的事?
E: 没错,那个模块存在很久了,因为之前的架构耦合严重,很多老功能也引用了那个配置。
道长: 怎么可能呢?静态检查居然没有查出来?
A: ?
B: ???
D: 您已建立群聊“道长代码受害交流群”, @道长
……
经过一番排查,我发现在项目的某个角落有这样一段神秘代码:
/*******************
* main.js
*******************/
function loadConfig() {
// ......
return import('./components/xxxx01/config.js'); // 动态引入配置文件
}
async function useConfig() {
const configModule = await loadConfig();
const config = configModule.ROLES_CONFIG; // 获取配置对象
console.log(config); // 使用配置
// ...
}
useConfig();
我只能说,这位前辈简直是个天才!他这样的操作,使用import()
动态引用 config.js
的变量,确实能实现引用,而且还能轻松躲过静态分析的检查。
果然对老项目一定要有一颗敬畏之心!不能只凭外表来判断它们的价值,像我这样一意孤行的结果,就是让前端团队跟我一起debug了1个多小时哈哈(别骂了别骂了)。
为了弥补这次错误,并且下次犯病之前能及时通知我的好同事们,我决定往“比大气层更高一点的层次”思考一下这个问题。
难道就没有什么方法能够帮助我们的团队快速规避我妄图修改公共代码(犯病)的行为吗?
既然要阻止我犯病,那就要病人发病并采取行动时,及时通知办公室里的“医生们”,进行“专家会诊”。
那就给所有的项目仓库加了个小门卫一样,只要有东西改动,就会拉响我们的微信小门铃。
·在你的仓库中,进入 .git
文件夹,然后找到 hooks
文件夹。这是存放钩子脚本的地方。
·在 hooks
文件夹中创建一个名为 post-commit
的可执行脚本文件(如果该文件已存在,确保它有执行权限)。
·把以下代码贴进 post-commit
文件中:
#!/bin/bash
# 获取仓库中 config.js 文件夹的改动
changes=$(git diff-tree --name-only --no-commit-id HEAD^ HEAD)
# 检查是否有 config.js 文件夹的改动
if echo "$changes" | grep -q "config.js/"; then
# 发送 POST 请求给 Server酱 推送消息
curl -s --data-urlencode "text=仓库中 config.js 文件夹有改动" https://sc.ftqq.com/你的Server酱SCKEY.send
fi
Server酱从服务器、路由器等设备上推消息到手机的工具。网址:Loading...
现在,当你在仓库里提交了代码,并且里面的"config.js"文件夹发生了变化,Git就会自动触发我们的超级厉害的"post-commit"钩子!它会给"Server酱"发一个POST请求,然后"Server酱"就会在微信上敲敲你的门铃,告诉你有人来找你了。
但是小伙伴们说,这样还不够,万一发版当天大家都很忙,你专门挑那个时候夹带私货合进main分支怎么办!
不仅要通知,还得有防灾措施。
立即就办,那现在让我们的配置实现热更新!这意味着当你在配置系统里修改某个字段时,它能立即感知到变化,就像是闪电五连鞭,每一鞭都能让每个人感受到那磅礴的气势。
想象一下,突然出现了一个线上逻辑Bug,需要发布一个紧急公告。这时候,不需要去改代码,也不用重新经历那漫长的CICD流程。只需要访问数据库,然后修改它的值,就像是写下了一句紧急公告的话语。配置监听器会立即感知到变化,就像是收到了一封短信通知,然后立刻将新的配置信息热情地更新到内存中。
代码我都给你写好啦!炫嘴里!码上掘金[1]
数据库配置监听器
数据库配置信息
Host:
Port:
Username:
Password:
// 替换为你的 Server酱 SCKEY
const SERVER_CHAN_SCKEY = 'YOUR_SERVER_CHAN_SCKEY';
// 初始的数据库配置信息
let dbConfig = {
host: '',
port: '',
username: '',
password: ''
};
// 发送消息给 Server酱
function sendServerChanMessage(message) {
const url = `https://sc.ftqq.com/${SERVER_CHAN_SCKEY}.send`;
const data = new URLSearchParams();
data.append('text', '数据库配置变更通知');
data.append('desp', message);
axios.post(url, data)
.catch(error => {
console.error('Failed to send message to Server酱:', error);
});
}
// 假设这是个获取服务器配置的方法
function generateRandomConfigObject() {
function generateRandomString() {
const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let randomString = '';
for (let i = 0; i < 6; i++) {
const randomIndex = Math.floor(Math.random() * characters.length);
randomString += characters[randomIndex];
}
return randomString;
}
return {
host: generateRandomString(),
port: generateRandomString(),
username: generateRandomString(),
password: generateRandomString()
};
}
// 监听配置信息变更
function watchConfigChanges() {
console.log('更新配置!')
// 假设这个是请求
const res = generateRandomConfigObject()
console.log('最新的配置是:', res)
const configData = res;
const newDbConfig = {
host: configData.host,
port: configData.port,
username: configData.username,
password: configData.password
};
if (
newDbConfig.host !== dbConfig.host ||
newDbConfig.port !== dbConfig.port ||
newDbConfig.username !== dbConfig.username ||
newDbConfig.password !== dbConfig.password
) {
dbConfig = newDbConfig;
// 更新网页上的配置信息
document.getElementById('host').textContent = dbConfig.host;
document.getElementById('port').textContent = dbConfig.port;
document.getElementById('username').textContent = dbConfig.username;
document.getElementById('password').textContent = dbConfig.password;
// 发送变更通知消息给 Server酱
const message = `数据库配置信息已变更:\n\nHost: ${dbConfig.host}\nPort: ${dbConfig.port}\nUsername: ${dbConfig.username}\nPassword: ${dbConfig.password}`;
sendServerChanMessage(message);
}
// 每隔一段时间监听一次
setTimeout(watchConfigChanges, 5000);
}
// 开始监听配置信息变更
watchConfigChanges();
这样,你的服务就能立即应对变化,发布紧急公告,不用等待,不用担心。就像是有一只神奇的精灵,帮你实现了配置的即时热更新。
至此,你应该已经知道什么叫对旧项目充满敬畏之心。代码和人,有一个能跑就行。毕竟,我们是专业团队嘛!。
希望本文能够帮助你发现coder生活中的一些乐趣。如果你有任何疑问或者想进一步讨论相关话题,请随时告诉我。