一 HTTP/HTTPS 服务端口监测的简易实现方法
在当今快节奏的工作环境中,工作忙碌成为了许多职场人的常态。就拿我们团队最近经历的事情来说,工作任务一个接一个,大家都在各自的岗位上争分夺秒地忙碌着。然而,就在这样高强度的工作节奏下,一个严重的问题悄然发生了。
我们负责的一个重要项目,在服务器重启时,其中的 http 服务竟然关闭了长达 3 天之久,令人遗憾的是,这期间居然没有一个人发现这个异常情况。这个看似不起眼的疏忽,却引发了极其严重的后果。由于 http 服务的长时间中断,我们丢失了部分订单。这些订单的丢失,不仅仅意味着直接的经济损失,还对我们与客户之间的信任关系造成了冲击,可能会影响到未来的业务合作。
基于对过往故障的深入反思与分析,为有效规避类似情况的再度发生,有必要采取切实可行的应对策略。于是便开发一款简易的 HTTP 服务端口监测程序,该程序仅通过单个 HTML 文件实现。此举旨在提升监测的便捷性与高效性,以实现对 HTTP 服务端口状态的实时监控。一旦检测到异常情况,能够迅速做出响应并采取相应的解决措施,从而有效避免因服务中断而引发的一系列严重后果。这一基于 HTML 的简易监测工具,不仅承担着保障业务稳定运行的关键职责,同时也警示我们,在复杂的业务环境中,任何可能影响系统稳定性的细节都不容忽视。
二 HTTP/HTTPS 服务端口监测
HTML 的简易监测效果图如下:
https://i-blog.csdnimg.cn/direct/4e2915c9b2664f8b85c14c5a2fd52d0b.png" width="1544" />
只需一个html,放在本地电脑或服务器Nginx都可运行,以下是项目完整html代码:
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>http服务状态监控</title><style>* {margin: 0;padding: 0;box-sizing: border-box;font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;}body {background-color: #f5f7fa;padding: 20px;color: #333;}.container {max-width: 1400px;margin: 0 auto;}h1 {text-align: center;margin-bottom: 20px;color: #2c3e50;}.projects-grid {display: grid;grid-template-columns: repeat(3, 1fr);gap: 15px;}.project {background: white;border-radius: 6px;box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);overflow: hidden;height: 100%;display: flex;flex-direction: column;}.project-header {background: #3498db;color: white;padding: 10px 15px;font-size: 16px;font-weight: 600;}.services-container {padding: 10px;flex-grow: 1;}.service-item {display: grid;grid-template-columns: minmax(100px, 1fr) minmax(150px, 2fr) auto;gap: 8px;align-items: center;padding: 8px 0;border-bottom: 1px solid #eee;font-size: 14px;}.service-item:last-child {border-bottom: none;}.service-name {font-weight: 500;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}.service-url {overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}.service-url a {color: #3498db;text-decoration: none;}.service-url a:hover {text-decoration: underline;}.service-status {display: flex;align-items: center;white-space: nowrap;justify-content: flex-end;}.status-indicator {width: 10px;height: 10px;border-radius: 50%;margin-right: 6px;display: inline-block;}.status-normal {background-color: #2ecc71;}.status-closed {background-color: #e74c3c;}.status-loading {background-color: #f39c12;animation: pulse 1.5s infinite;}.refresh-container {text-align: center;margin: 15px 0 20px;}.refresh-btn {background: #3498db;color: white;border: none;padding: 8px 16px;border-radius: 4px;cursor: pointer;font-size: 14px;transition: background 0.3s;}.refresh-btn:hover {background: #2980b9;}.refresh-controls {display: flex;justify-content: center;align-items: center;gap: 15px;margin-top: 10px;}.refresh-info {font-size: 14px;color: #666;}.loading-overlay {position: fixed;top: 0;left: 0;right: 0;bottom: 0;background: rgba(255, 255, 255, 0.8);display: flex;justify-content: center;align-items: center;z-index: 1000;visibility: hidden;opacity: 0;transition: all 0.3s;}.loading-overlay.active {visibility: visible;opacity: 1;}.spinner {width: 40px;height: 40px;border: 4px solid rgba(52, 152, 219, 0.3);border-top-color: #3498db;border-radius: 50%;animation: spin 1s linear infinite;}.tooltip {position: relative;display: inline-block;}.tooltip .tooltiptext {visibility: hidden;width: 250px;background-color: #555;color: #fff;text-align: center;border-radius: 4px;padding: 5px;position: absolute;z-index: 1;bottom: 125%;left: 50%;transform: translateX(-50%);opacity: 0;transition: opacity 0.3s;font-size: 12px;word-break: break-all;}.tooltip:hover .tooltiptext {visibility: visible;opacity: 1;}@keyframes spin {to {transform: rotate(360deg);}}@keyframes pulse {0% { opacity: 0.6; }50% { opacity: 1; }100% { opacity: 0.6; }}@media (max-width: 1200px) {.projects-grid {grid-template-columns: repeat(2, 1fr);}}@media (max-width: 768px) {.projects-grid {grid-template-columns: 1fr;}.service-item {grid-template-columns: 1fr auto;gap: 5px;}.service-url {grid-column: 1 / 3;grid-row: 2;}}</style>
</head>
<body><div class="container"><h1>http服务状态监控</h1><div class="refresh-container"><button id="refreshBtn" class="refresh-btn">立即刷新</button><div class="refresh-controls"><div class="refresh-info">上次刷新时间: <span id="lastRefreshTime">-</span></div><div class="refresh-info">自动刷新间隔: <select id="refreshInterval"><option value="30">30秒</option><option value="60" selected>1分钟</option><option value="300">5分钟</option><option value="600">10分钟</option></select></div></div></div><div id="projectsGrid" class="projects-grid"></div></div><div class="loading-overlay" id="loadingOverlay"><div class="spinner"></div></div><script>// 示例数据 - 10个项目,每个项目5个服务,这里根据自己的项目修改即可var project_list = [{name: '项目名称1',serv_list: [{name: '服务名1-1', url: 'https://www.baidu.com', status: '正常'},{name: '服务名1-2', url: 'https://www.not-exist-example.com', status: '关闭'},{name: '服务名1-3', url: 'https://www.bing.com', status: '正常'},{name: '服务名1-4', url: 'https://www.qq.com', status: '正常'},{name: '服务名1-5', url: 'https://www.taobao.com', status: '正常'}]},{name: '项目名称2',serv_list: [{name: '服务名2-1', url: 'https://www.bing.com', status: '正常'},{name: '服务名2-2', url: 'https://www.google.com', status: '关闭'},{name: '服务名2-3', url: 'https://www.baidu.com', status: '正常'},{name: '服务名2-4', url: 'https://www.github.com', status: '正常'},{name: '服务名2-5', url: 'https://www.douban.com', status: '正常'}]},{name: '项目名称3',serv_list: [{name: '服务名3-1', url: 'https://www.jd.com', status: '正常'},{name: '服务名3-2', url: 'https://www.not-exist-example.com', status: '关闭'},{name: '服务名3-3', url: 'https://www.zhihu.com', status: '正常'},{name: '服务名3-4', url: 'https://www.weibo.com', status: '正常'},{name: '服务名3-5', url: 'https://www.163.com', status: '正常'}]},{name: '项目名称4',serv_list: [{name: '服务名4-1', url: 'https://www.douyin.com', status: '正常'},{name: '服务名4-2', url: 'https://www.not-exist-example.com', status: '关闭'},{name: '服务名4-3', url: 'https://www.alipay.com', status: '正常'},{name: '服务名4-4', url: 'https://www.sohu.com', status: '正常'},{name: '服务名4-5', url: 'https://www.bilibili.com', status: '正常'}]},{name: '项目名称5',serv_list: [{name: '服务名5-1', url: 'https://www.tmall.com', status: '正常'},{name: '服务名5-2', url: 'https://www.not-exist-example.com', status: '关闭'},{name: '服务名5-3', url: 'https://www.csdn.net', status: '正常'},{name: '服务名5-4', url: 'https://www.oschina.net', status: '正常'},{name: '服务名5-5', url: 'https://www.cnblogs.com', status: '正常'}]},{name: '项目名称6',serv_list: [{name: '服务名6-1', url: 'https://www.tencent.com', status: '正常'},{name: '服务名6-2', url: 'https://www.not-exist-example.com', status: '关闭'},{name: '服务名6-3', url: 'https://www.huawei.com', status: '正常'},{name: '服务名6-4', url: 'https://www.xiaomi.com', status: '正常'},{name: '服务名6-5', url: 'https://www.oppo.com', status: '正常'}]},{name: '项目名称7',serv_list: [{name: '服务名7-1', url: 'https://www.vivo.com', status: '正常'},{name: '服务名7-2', url: 'https://www.not-exist-example.com', status: '关闭'},{name: '服务名7-3', url: 'https://www.apple.com', status: '正常'},{name: '服务名7-4', url: 'https://www.samsung.com', status: '正常'},{name: '服务名7-5', url: 'https://www.mi.com', status: '正常'}]},{name: '项目名称8',serv_list: [{name: '服务名8-1', url: 'https://www.sina.com.cn', status: '正常'},{name: '服务名8-2', url: 'https://www.not-exist-example.com', status: '关闭'},{name: '服务名8-3', url: 'https://www.163.com', status: '正常'},{name: '服务名8-4', url: 'https://www.sogou.com', status: '正常'},{name: '服务名8-5', url: 'https://www.360.cn', status: '正常'}]},{name: '项目名称9',serv_list: [{name: '服务名9-1', url: 'https://www.so.com', status: '正常'},{name: '服务名9-2', url: 'https://www.not-exist-example.com', status: '关闭'},{name: '服务名9-3', url: 'https://www.ifeng.com', status: '正常'},{name: '服务名9-4', url: 'https://www.cctv.com', status: '正常'},{name: '服务名9-5', url: 'https://www.people.com.cn', status: '正常'}]}];// 全局变量let autoRefreshTimer;let refreshIntervalSeconds = 60;// DOM 元素const projectsGrid = document.getElementById('projectsGrid');const refreshBtn = document.getElementById('refreshBtn');const lastRefreshTimeEl = document.getElementById('lastRefreshTime');const refreshIntervalSelect = document.getElementById('refreshInterval');const loadingOverlay = document.getElementById('loadingOverlay');// 初始化页面function initPage() {// 设置刷新间隔refreshIntervalSelect.addEventListener('change', function() {refreshIntervalSeconds = parseInt(this.value);resetAutoRefreshTimer();});// 刷新按钮事件refreshBtn.addEventListener('click', function() {checkAllServices();});// 初始渲染项目列表renderProjects();// 初次检查服务状态checkAllServices();// 设置自动刷新resetAutoRefreshTimer();}// 渲染项目列表function renderProjects() {projectsGrid.innerHTML = '';project_list.forEach(project => {const projectEl = document.createElement('div');projectEl.className = 'project';const projectHeader = document.createElement('div');projectHeader.className = 'project-header';projectHeader.textContent = project.name;const servicesContainer = document.createElement('div');servicesContainer.className = 'services-container';project.serv_list.forEach(service => {const serviceItem = document.createElement('div');serviceItem.className = 'service-item';const serviceName = document.createElement('div');serviceName.className = 'service-name';serviceName.textContent = service.name;const serviceUrl = document.createElement('div');serviceUrl.className = 'service-url';const urlLink = document.createElement('a');urlLink.href = service.url;urlLink.target = '_blank';urlLink.textContent = shortenUrl(service.url);urlLink.title = service.url;serviceUrl.appendChild(urlLink);const serviceStatus = document.createElement('div');serviceStatus.className = 'service-status';const statusIndicator = document.createElement('span');statusIndicator.className = `status-indicator status-loading`;const statusText = document.createElement('span');statusText.textContent = '检测中...';serviceStatus.appendChild(statusIndicator);serviceStatus.appendChild(statusText);serviceItem.appendChild(serviceName);serviceItem.appendChild(serviceUrl);serviceItem.appendChild(serviceStatus);servicesContainer.appendChild(serviceItem);});projectEl.appendChild(projectHeader);projectEl.appendChild(servicesContainer);projectsGrid.appendChild(projectEl);});}// 缩短URL显示function shortenUrl(url) {try {const urlObj = new URL(url);return urlObj.hostname;} catch (e) {// 如果解析失败,返回原始URL的一部分return url.length > 20 ? url.substring(0, 20) + '...' : url;}}// 检测所有服务状态async function checkAllServices() {showLoading();// 更新上次刷新时间lastRefreshTimeEl.textContent = new Date().toLocaleString();const projectElements = document.querySelectorAll('.project');for (let i = 0; i < project_list.length; i++) {const project = project_list[i];for (let j = 0; j < project.serv_list.length; j++) {const service = project.serv_list[j];// 获取对应的 DOM 元素const serviceItems = projectElements[i].querySelectorAll('.service-item');const serviceStatusEl = serviceItems[j].querySelector('.service-status');const statusIndicator = serviceStatusEl.querySelector('.status-indicator');const statusText = serviceStatusEl.querySelector('span:last-child');// 将状态设置为检测中statusIndicator.className = 'status-indicator status-loading';statusText.textContent = '检测中...';try {// 使用 fetch 带超时进行检测const status = await checkServiceStatus(service.url);// 更新服务状态service.status = status ? '正常' : '关闭';// 更新 UIif (status) {statusIndicator.className = 'status-indicator status-normal';statusText.textContent = '正常';} else {statusIndicator.className = 'status-indicator status-closed';statusText.textContent = '关闭';}} catch (error) {console.error(`检测服务 ${service.name} 失败:`, error);// 出错时将状态设为关闭service.status = '关闭';statusIndicator.className = 'status-indicator status-closed';statusText.textContent = '关闭';}}}hideLoading();}// 检测单个服务状态async function checkServiceStatus(url) {try {// 设置超时const controller = new AbortController();const timeoutId = setTimeout(() => controller.abort(), 5000);const response = await fetch(url, {method: 'HEAD',mode: 'no-cors',signal: controller.signal});clearTimeout(timeoutId);// 返回状态码小于 400 表示服务正常return response.status < 400;} catch (error) {// 出现异常(如超时、网络错误)表示服务异常return false;}}// 重置自动刷新定时器function resetAutoRefreshTimer() {if (autoRefreshTimer) {clearInterval(autoRefreshTimer);}autoRefreshTimer = setInterval(() => {checkAllServices();}, refreshIntervalSeconds * 1000);}// 显示加载中遮罩function showLoading() {loadingOverlay.classList.add('active');}// 隐藏加载中遮罩function hideLoading() {loadingOverlay.classList.remove('active');}// 页面加载完成后初始化document.addEventListener('DOMContentLoaded', initPage);</script>
</body>
</html>
说明:使用时只需根据自己项目修改js部分项目数据即可。