HTTP/HTTPS 服务端口监测的简易实现

发布于:2025-02-26 ⋅ 阅读:(18) ⋅ 点赞:(0)

一  HTTP/HTTPS 服务端口监测的简易实现方法

        在当今快节奏的工作环境中,工作忙碌成为了许多职场人的常态。就拿我们团队最近经历的事情来说,工作任务一个接一个,大家都在各自的岗位上争分夺秒地忙碌着。然而,就在这样高强度的工作节奏下,一个严重的问题悄然发生了。

        我们负责的一个重要项目,在服务器重启时,其中的 http 服务竟然关闭了长达 3 天之久,令人遗憾的是,这期间居然没有一个人发现这个异常情况。这个看似不起眼的疏忽,却引发了极其严重的后果。由于 http 服务的长时间中断,我们丢失了部分订单。这些订单的丢失,不仅仅意味着直接的经济损失,还对我们与客户之间的信任关系造成了冲击,可能会影响到未来的业务合作。

        基于对过往故障的深入反思与分析,为有效规避类似情况的再度发生,有必要采取切实可行的应对策略。于是便开发一款简易的 HTTP 服务端口监测程序,该程序仅通过单个 HTML 文件实现。此举旨在提升监测的便捷性与高效性,以实现对 HTTP 服务端口状态的实时监控。一旦检测到异常情况,能够迅速做出响应并采取相应的解决措施,从而有效避免因服务中断而引发的一系列严重后果。这一基于 HTML 的简易监测工具,不仅承担着保障业务稳定运行的关键职责,同时也警示我们,在复杂的业务环境中,任何可能影响系统稳定性的细节都不容忽视。

二 HTTP/HTTPS 服务端口监测

HTML 的简易监测效果图如下:

只需一个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 ? '正常' : '关闭';
                        
                        // 更新 UI
                        if (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部分项目数据即可。


网站公告

今日签到

点亮在社区的每一天
去签到