Автор статьи: первый головастик
Введение Решение для горячего развертывания, которое позволяет вам заставить новый обновленный код вступить в силу без перезапуска онлайн-сервиса nodejs.
задний план
Все мы знаем, что серверная служба запускается nodejs, и в случае изменения кода процесс необходимо перезапустить, прежде чем код вступит в силу.
Когда процесс nodejs будет перезапущен, будет короткий период времени, когда пользователь получит доступ к сервису.502 bad gateway
Если ваш сервер добавляет механизм наблюдения
Когда код на сервере часто меняется или часто меняется за короткий промежуток времени, он всегда будет502 bad gateway
В последнее время при выполнении требований, связанных с компиляцией онлайн-сервиса, за короткий период времени часто происходили изменения кода онлайн-сервиса, а функциональные модули кода часто обновлялись, и обновленный код может вступить в силу, когда сервис не может быть перезапущен.
Это включает в себя концепцию горячего развертывания, когда вновь развернутый код вступает в силу без перезапуска службы.
Далее я объясню вам принцип и план реализации горячего развертывания.
Причина, по которой код не действует в режиме реального времени
когда мы проходимrequire('xx/xx.js')
При загрузке функционального модуля узел будетrequire('xx/xx.js')
Полученный результат кэшируется вrequire.cache('xx/xx.js')
середина
когда мы звоним несколько разrequire('xx/xx.js')
, нода уже не перезагружается, а напрямую изrequire.cache('xx/xx.js')
чтение кеша
Итак, когда друг модифицирует на сервереxx/xx.js
Когда используется файл по этому пути, узел будет только читать кеш и не будет загружать последний код малого партнера.
Исходный адрес и использование
Чтобы реализовать этот механизм горячего развертывания, я искал информацию повсюду в Интернете и преодолел множество ям, чтобы сделать это правильно.
Следующий код представляет собой извлеченный, полный и работоспособный базовый код горячего развертывания. Вы можете самостоятельно расширить его на основе этого кода:smart-node-reload
Обратите внимание, что последняя версия узла 12 сообщит об ошибке.Официал скорректировал require.cache, и о проблеме было сообщено официальному.Рекомендуется использоватьnodejs版本:v10.5.0
git clone
После спуска не нужно устанавливать, запускать напрямую
npm start
В это время включен мониторинг изменений в горячем развертывании.
Как увидеть эффект
приятель, пожалуйста, посмотри/hots/hot.js
документ
const hot = 1
module.exports = hot
Измените первую строку кода на const hot = 111.
const hot = 111
module.exports = hot
В это время вы можете видеть, что изменения кода отслеживаются в терминале, а затем динамически загружают ваш последний код и получают результат выполнения.Вывод:
热部署文件: hot.js ,执行结果: { 'hot.js': 111 }
Служба горячего развертывания отслеживает изменения кода и перезагружает код, а небольшие партнеры могут получать результаты выполнения последнего кода в режиме реального времени Весь процесс выполняется в онлайн-среде, и процесс узла не перезапускается.
Анализ исходного кода
основная функция loadHandlers
const handlerMap = {};// 缓存
const hotsPath = path.join(__dirname, "hots");
// 加载文件代码 并 监听指定文件夹目录文件内容变动
const loadHandlers = async () => {
// 遍历出指定文件夹下的所有文件
const files = await new Promise((resolve, reject) => {
fs.readdir(hotsPath, (err, files) => {
if (err) {
reject(err);
} else {
resolve(files);
}
});
});
// 初始化加载所有文件 把每个文件结果缓存到handlerMap变量当中
for (let f in files) {
handlerMap[files[f]] = await loadHandler(path.join(hotsPath, files[f]));
}
// 监听指定文件夹的文件内容变动
await watchHandlers();
};
loadHandlers
Является основной функцией всего сервиса горячего развертывания, указываем корневую директорию сервераhots
Папки используются для мониторинга изменений и горячего развертывания.
использоватьfs.readdir
сканированиеhots
Все файлы в папке черезloadHandler
способ загрузки и запуска каждого отсканированного файла с кэшированием результатов вhandlerMap
внутри
затем используйтеwatchHandlers
Способ включения мониторинга изменений файлов
watchHandlers отслеживает изменения файлов
// 监视指定文件夹下的文件变动
const watchHandlers = async () => {
// 这里建议用chokidar的npm包代替文件夹监听
fs.watch(hotsPath, { recursive: true }, async (eventType, filename) => {
// 获取到每个文件的绝对路径
// 包一层require.resolve的原因,拼接好路径以后,它会主动去帮你判断这个路径下的文件是否存在
const targetFile = require.resolve(path.join(hotsPath, filename));
// 当你适应require加载一个模块后,模块的数据就会缓存到require.cache中,下次再加载相同模块,就会直接走require.cache
// 所以我们热加载部署,首要做的就是清除require.cache中对应文件的缓存
const cacheModule = require.cache[targetFile];
// 去除掉在require.cache缓存中parent对当前模块的引用,否则会引起内存泄露,具体解释可以看下面的文章
//《记录一次由一行代码引发的“血案”》https://cnodejs.org/topic/5aaba2dc19b2e3db18959e63
//《一行 delete require.cache 引发的内存泄漏血案》https://zhuanlan.zhihu.com/p/34702356
if (cacheModule.parent) {
cacheModule.parent.children.splice(cacheModule.parent.children.indexOf(cacheModule), 1);
}
// 清除指定路径对应模块的require.cache缓存
require.cache[targetFile] = null;
// 重新加载发生变动后的模块文件,实现热加载部署效果,并将重新加载后的结果,更新到handlerMap变量当中
const code = await loadHandler(targetFile)
handlerMap[filename] = code;
console.log("热部署文件:", filename, ",执行结果:", handlerMap);
});
};
watchHandlers
Функция используется для мониторинга файлов в указанном изменении папки, очистите кэш-накопитель.
использоватьfs.watch
Мониторинг встроенных функцийhots
Файл в папке меняется, при изменении файла вычисляется абсолютный путь к файлу.targetFile
а такжеrequire.cache[targetFile]
то естьrequire
правильноtargetFile
Кэш исходного файла, очистить кешrequire.cache[targetFile] = null;
Здесь возникает ловушка, просто установите кеш в ноль, будет утечка памяти, нам также нужно очистить ссылку родителя кешаrequire.cache[targetFile].parent
, это следующий код
if (cacheModule.parent) {
cacheModule.parent.children.splice(cacheModule.parent.children.indexOf(cacheModule), 1);
}
loadHandler загружает файл
// 加载指定文件的代码
const loadHandler = filename => {
return new Promise((resolve, reject) => {
fs.readFile(filename, (err, data) => {
if (err) {
resolve(null);
} else {
try {
// 使用vm模块的Script方法来预编译发生变化后的文件代码,检查语法错误,提前发现是否存在语法错误等报错
new vm.Script(data);
} catch (e) {
// 语法错误,编译失败
reject(e);
return;
}
// 编译通过后,重新require加载最新的代码
resolve(require(filename));
}
});
});
};
Роль функции loadHandler заключается в загрузке указанного файла и проверке синтаксиса кода нового файла.
Чтение содержимого файла через fs.readFile
Используйте метод vm.Script собственного модуля vm узла, чтобы предварительно скомпилировать измененный код файла, проверить синтаксические ошибки и заранее выяснить, есть ли синтаксические ошибки и другие ошибки.
После прохождения теста перезагрузите требуемый файл с помощью метода resolve(require(filename)) и автоматически добавьте его в кэш require.cache.
конец:
Выше приведено все содержание горячего развертывания.Кодовой адрес:smart-node-reload
Этот код мой упрощенный код, который удобно читать и понимать всем.Заинтересованные друзья могут использовать этот код для дальнейшего расширения