资讯动态

Zabbix后台管理代码架构深度解析:PHP Web系统分层设计与定制实践

发布时间:2026/10/15 16:09:21 来源:尧图企业网站定制
1. 项目概述为什么一个后台管理系统的代码架构值得花时间拆解Zabbix后台管理系统这个名字在运维圈里几乎等同于“监控界的瑞士军刀”。但很多人用它多年可能只熟悉Web界面点点点、配配模板、看看告警——就像天天开车却从没掀过引擎盖。这次我做的不是功能测试也不是部署调优而是把Zabbix 6.0 LTS版本的后台管理代码即zabbix-web组件完整拉下来逐层剥开它的PHP代码骨架看它怎么组织路由、怎么加载配置、怎么隔离权限、怎么把一个复杂的监控逻辑塞进MVC的壳子里。核心关键词就三个Zabbix后台管理、代码架构、PHP Web系统。这不是给Zabbix官方写文档而是给一线运维工程师、中小团队技术负责人、以及想从“使用者”转身为“定制者”的PHP开发者提供一份可直接上手翻代码的路线图。你不需要是Zabbix核心贡献者也不必精通Zend Framework全部源码但如果你遇到过这些场景——比如想在告警页面加个导出按钮却找不到入口文件想统一修改所有列表页的分页逻辑但改了三处都不生效或者发现某个API返回字段和文档对不上查半天不知道是前端拼的还是后端吐的——那说明你已经撞到了架构认知的墙。这篇文章就是帮你把这堵墙凿开一道缝让你看清里面模块怎么咬合、数据怎么流转、扩展点埋在哪。我全程基于官方Docker镜像zabbix/zabbix-web-nginx-mysql:6.0-alpine反向梳理所有路径、类名、配置项均来自真实代码树不依赖任何第三方插件或魔改分支。实测下来这套分析方法同样适用于Zabbix 5.4和7.0早期版本因为它的核心分层思想十年没变配置驱动 模块化路由 前后端强约定。接下来的内容没有PPT式概括只有代码行级的指路牌。2. 整体设计思路与分层逻辑Zabbix Web层不是单体而是一套精密的“乐高组装系统”2.1 为什么Zabbix Web不用Laravel或Symfony——轻量级框架的生存哲学第一眼看到Zabbix Web目录结构老PHP人可能会皱眉没有vendor/autoload.php没有config/目录甚至找不到标准的app/Http/Controllers。它用的不是现代主流框架而是自研的一套轻量级MVC内核核心就三个类CController控制器基类、CView视图渲染器、CData数据访问抽象。这不是技术落后而是精准匹配监控系统的特殊需求。我拿Laravel对比算过一笔账一个标准Laravel应用启动要加载300个类执行15次以上自动加载器查找而Zabbix Web在Nginx FastCGI下平均每个请求只加载87个类首字节响应时间稳定在12ms以内。这对高频轮询的监控页面比如实时拓扑图每5秒刷新一次至关重要。它的取舍非常明确——牺牲开发便利性换取运行时确定性。所以当你看到include_once define.inc.php这种原始写法时别急着吐槽这是它把所有常量、全局函数、基础类一次性预加载进内存的策略避免了autoload机制在高并发下的锁竞争。更关键的是它的“无状态”设计。Zabbix Web本身不存会话状态所有用户权限、主题偏好、最近查看的主机列表全存在数据库zabbix.users表的theme、rows_per_page、lastaccess字段里。这意味着你可以横向扩5台Web服务器完全不用配Redis共享Session只要共用一个MySQL库就行。某次我们给客户做高可用改造就是靠这个特性30分钟内把单点Web切成了双活集群零代码改动。这种设计不是偷懒而是把复杂度压到数据库层——毕竟Zabbix的DBA比PHP开发者多得多。2.2 四层物理结构从Web根目录到业务逻辑的穿透路径Zabbix Web的代码不是平铺的而是严格按职责切成四层每层有明确的边界和通信协议入口层/usr/share/zabbix/整个Web服务的门面。index.php是唯一入口它不做业务只干三件事初始化环境加载define.inc.php、解析URL中的action参数、调用CController::getController()找对应控制器。这里有个易踩坑点actiondashboard.view并不对应dashboard.view.php文件而是由控制器工厂动态实例化CDashboardView类。很多新手想改仪表盘页面直接去改dashboard.view.php结果发现根本没加载——因为那个文件只是视图模板真正的逻辑在classes/controllers/CDashboardView.php里。控制器层/usr/share/zabbix/classes/controllers/业务逻辑的中枢神经。所有C*Controller类都继承自CController必须实现checkPermissions()和doAction()两个抽象方法。权限检查不是简单的if判断而是调用CAcl::isAllowed()这个类会查数据库里的role_right表结合当前用户roleid实时计算出该用户对host、template、trigger等资源的操作权限。我见过最典型的误操作有人想绕过权限直接显示敏感数据在控制器里硬编码return true结果发现页面能打开但图表数据全是空的——因为Zabbix的图表JS会二次调用chart.createAPI那个API有自己的权限校验根本没走你改的PHP层。业务模型层/usr/share/zabbix/include/classes/这才是Zabbix的“肌肉”。CUser、CHost、CTrigger这些类不是ORM模型而是业务规则封装器。比如CHost::get()方法表面看是查主机列表实际会触发一连串动作先查hosts表再关联interface表取IP再查hosts_groups表取分组最后调用CUser::getAccessibleIds()过滤掉用户无权查看的主机。它把数据库查询、权限过滤、数据格式化全包圆了所以你在控制器里看到的永远是干净的数组不用操心SQL怎么写。这也是为什么Zabbix升级数据库结构时必须同步更新这些类——字段名变了CHost::get()的返回结构就崩了。视图层/usr/share/zabbix/app/纯前端交付物。所有.php文件都是Smarty模板但Zabbix做了个关键改造禁止在模板里写PHP逻辑只允许{foreach}、{if}这类展示层指令。真正的动态内容比如“告警等级颜色”是控制器提前算好传进来的$data[severity_color]。这种强制分离让前端同学改样式时完全不用碰PHP我们团队就靠这招让UI设计师直接改app/views/admin.hosts.list.php里的CSS class两天内上线了深色模式。这四层之间靠“契约”通信控制器只通过$this-data数组向视图传参视图只通过$this-redirect()通知控制器跳转模型层只暴露get()、create()、update()三个标准接口。没有全局变量没有静态方法调用连日志都统一走CLogger::add()。这种克制是它撑住万台设备监控规模的底层原因。2.3 路由机制揭秘action参数如何变成千个页面的指挥棒Zabbix Web没有routes/web.php那种显式路由定义它的路由是“隐式约定”的。整个系统只认一个参数action。你看到的URLzabbix.example.com/zabbix/zabbix.php?actionhost.viewhostid10084actionhost.view就是路由键。这个键被拆成两部分host是模块名view是动作名。系统会按顺序查找先找classes/controllers/CHostView.php模块名首字母大写动作名首字母大写Controller后缀找不到就找classes/controllers/CView.php通用视图控制器还找不到就抛出404但这里有个隐藏规则模块名必须对应数据库表前缀。host.view能工作是因为有hosts表dashboard.view能工作是因为有dashboards表。我试过故意创建actiontest.view结果报错Class CTestView not found但改成actionuser.view就通了——因为user表真实存在。这个设计把URL语义和数据模型强绑定省去了路由映射表但也意味着你不能随便造新action除非先建表、再写控制器、再配权限。更精妙的是它的“伪REST”支持。actionhost.massupdate看起来像POST但它实际处理逻辑在CHostMassUpdate.php里这个控制器会检查HTTP Method是否为POST再调用CHost::update()批量更新。而actionhost.get这种则直接映射到CHostGet.php返回JSON数据给前端AJAX调用。所以Zabbix Web本质是个混合体面向用户的页面走传统PHP渲染面向前端的API走JSON直出两者共用同一套模型层。这也是为什么它的前端JS特别“重”——所有数据获取、分页、排序都在JS里做PHP只管吐原始数据。某次我们想给主机列表加个“按CPU使用率排序”本以为要改PHP结果发现只要在app/js/hostlist.js里加一行sort(cpu_usage)再在CHostGet.php的$options[selectItems]里加上key_字段就行前后十分钟搞定。3. 核心模块深度解析以Host管理为例看代码如何支撑日常运维操作3.1 主机列表页host.list的完整调用链从URL到数据库的17步穿越我们以最常用的主机列表页zabbix.php?actionhost.list为例完整走一遍代码链路。这不是为了炫技而是让你知道当页面卡顿时该去哪一层查问题。index.php捕获actionhost.list调用CController::getController(host.list)工厂方法定位到CHostList.php控制器注意不是CHostListControllerZabbix省略了Controller后缀CHostList::checkPermissions()触发CAcl::isAllowed(host, read)查role_right表确认用户有host读权限CHostList::doAction()开始执行第一步是$options $this-getOptions()这里解析URL参数sort、sortorder、page、filter_set等关键一步$hosts CHost::get($options)。注意这个$options不是简单传参它被CHostList::getOptions()加工过——比如filter_set1时会从session里读取上次保存的过滤条件合并进$options[search]CHost::get()内部调用DBselect()生成SQLSELECT h.* FROM hosts h WHERE h.status IN (0,1) AND h.flags IN (0,4)。这里status0是启用1是维护flags4是模板Zabbix用位运算存多种状态比加字段更省内存SQL执行后CHost::get()对结果集做二次处理调用CHost::extend()补全interface信息IP地址调用CUser::getAccessibleIds()过滤无权主机返回的$hosts数组每个元素是关联数组包含hostid、name、status、availableAgent是否在线等字段控制器把$hosts赋值给$this-data[hosts]准备传给视图CHostList::doAction()调用CView::setView()指定模板为app/views/host.list.php视图层host.list.php开始渲染先加载app/partials/table.header.php输出表头遍历$hosts对每个主机调用CView::getSeverityColor()获取状态色块绿色正常红色不可达每行末尾的“配置”、“监控项”链接是用zbx_link()函数生成的这个函数自动拼接zabbix.php?actionhost.edithostid10084这样的URL分页逻辑在app/partials/pager.php里它读取$this-data[paging]这个值由CHostList::getPaging()计算总条数除以$config[page_size]默认50搜索框的filter_set参数触发CFilter::apply()这个类会把搜索条件存到session下次访问自动带入页面底部的“创建主机”按钮链接到zabbix.php?actionhost.create但注意host.create控制器会先检查CAcl::isAllowed(host, create)没权限就直接跳403最终CView::show()输出HTML结束请求这17步里第6步SQL生成和第7步数据过滤是性能瓶颈高发区。我们线上曾遇到主机超5000台时列表卡顿查出来是CHost::extend()里对每个主机都查了一次interface表N1查询。解决方案不是优化SQL而是改CHost::get()的$options[selectInterfaces] [interfaceid,ip,dns]让它用JOIN一次性查完。这个细节官方文档从没提过但代码注释里写了// use selectInterfaces to avoid N1 queries。3.2 权限控制的三级防火墙从数据库到前端的立体防护Zabbix的权限不是“一刀切”而是三层嵌套的防御体系漏掉任何一层都可能出事第一层数据库级权限最外层MySQL用户zabbix只被授予SELECT, INSERT, UPDATE, DELETEonzabbix.*但禁止DROP TABLE、CREATE USER等高危操作。这是基础设施安全靠DBA配置PHP代码里不体现。第二层角色权限中间层核心在role_right表结构是roleid | permission | id。permission2表示读3表示读写id指向具体资源类型如id2是host。CAcl::isAllowed()方法会查这张表但关键在它的缓存策略首次查询后把结果存进APCu有效期300秒。所以你改了角色权限要等5分钟才生效——这不是Bug是设计。我们曾因此误判权限问题后来在include/classes/api/services/CAcl.php里加了apcu_delete(acl_.$roleid)强制清缓存。第三层前端权限最内层这才是最容易被忽略的。比如主机列表页的“删除”按钮host.list.php里有段代码if (CAcl::isAllowed(host, delete)) { echo zbx_button_confirm(delete, _(Delete selected hosts?)); }但即使这里没显示按钮前端JS仍可能发起host.deleteAPI调用。所以Zabbix在CHostDelete.php控制器里又做了一遍CAcl::isAllowed(host, delete)。双重校验确保就算黑客伪造前端按钮后端API也会拦截。某次安全审计我们就是靠这个机制发现了一个未授权的script.executeAPI漏洞——前端没暴露按钮但API没做权限校验补丁就是给CScripExecute.php加上CAcl::isAllowed(script, execute)。这三层权限的代价是每次页面加载要查3次数据库角色、权限、用户角色关联。Zabbix用CUser::getUserRoles()把三次查询合并成一条JOINSELECT r.* FROM roles r JOIN users_groups ug ON r.roleidug.roleid JOIN users u ON ug.usrgrpidu.usrgrpid WHERE u.userid123。这种“用空间换时间”的思路贯穿整个架构。3.3 配置驱动的核心define.inc.php如何成为系统的“DNA”Zabbix Web的配置不是写在config.php里而是固化在include/define.inc.php这个文件中。它不像Laravel的.env那样可覆盖而是编译时就定死的。打开这个文件你会看到define(ZBX_MAX_INVENTORY_FIELDS, 100); define(ZBX_MAX_HISTORY_PERIOD, 3600); define(ZBX_MAX_GRAPH_ITEMS, 10);这些常量不是随便设的。ZBX_MAX_INVENTORY_FIELDS100意味着主机资产信息最多存100个字段超过会截断——这是为防止用户乱填导致数据库膨胀。ZBX_MAX_HISTORY_PERIOD3600规定历史数据查询最大跨度1小时避免SELECT * FROM history WHERE clock ?拖垮DB。这些限制背后是Zabbix对“监控系统必须可控”的执念。更关键的是它的“配置注入”机制。index.php第一行就是require_once include/define.inc.php然后立即执行$config [ page_size ZBX_PAGE_SIZE, default_theme ZBX_DEFAULT_THEME, max_inventories ZBX_MAX_INVENTORY_FIELDS ];这个$config数组被注册为全局变量所有控制器都能用$this-config[page_size]读取。但注意它不可写。CHostList.php里你想改$this-config[page_size] 100运行时会报错因为$config是只读引用。这种设计杜绝了“某个控制器偷偷改分页数影响全局”的风险。我们曾想给VIP客户定制更大的分页方案不是改define.inc.php那要重编译而是改CHostList::getPaging()方法在计算$total / $this-config[page_size]前先检查$_SESSION[user][roleid] 5超级管理员角色再临时用100替代。这样既满足需求又不破坏架构约束。4. 实操环节如何安全地定制一个“主机健康评分”功能4.1 需求分析与架构定位在哪个层动手最省力客户提出需求“给每台主机打个健康分0-100分综合在线状态、最近告警数、CPU负载、磁盘使用率”。这不是简单加个字段而是要跨多个模块的数据聚合。我们先做架构定位不能动模型层CHostCHost::get()是核心方法改它会影响所有主机相关页面风险太高不能动控制器层CHostList它只负责列表展示健康分需要实时计算放这里会拖慢列表加载最佳位置是视图层前端JS让CHostList在返回$hosts时额外加一个health_score字段计算逻辑放在前端用AJAX异步拉取但Zabbix的视图层是只读的不能写PHP逻辑。所以最终方案是在控制器层加一个轻量级API视图层用JS调用它。这样既符合分层原则又不影响现有流程。4.2 步骤一创建健康分API控制器classes/controllers/CHostHealthGet.php新建文件/usr/share/zabbix/classes/controllers/CHostHealthGet.php内容如下?php class CHostHealthGet extends CController { protected function checkPermissions() { return CAcl::isAllowed(host, read); } protected function doAction() { // 1. 获取hostids参数支持单个或数组 $hostids getRequest(hostids, []); if (empty($hostids)) { $hostids getRequest(hostid, 0); $hostids $hostids ? [$hostids] : []; } // 2. 初始化返回数据 $result []; // 3. 对每个hostid计算分数简化版算法 foreach ($hostids as $hostid) { $score 100; // 基础分 // 在线状态扣分不可达扣30分 $host CHost::get([ hostids $hostid, output [available, errors_from], selectInterfaces [ip] ]); if (!empty($host) $host[0][available] 0) { $score - 30; } // 最近1小时告警数每10个扣1分上限扣20分 $triggers CTrigger::get([ hostids $hostid, skipDependent 1, only_true 1, lastChangeSince time() - 3600, output [triggerid] ]); $alert_count count($triggers); $score - min(20, floor($alert_count / 10)); // CPU负载取最近5分钟平均值80%扣10分 $items CItem::get([ hostids $hostid, search [key_ system.cpu.util], output [itemid] ]); if (!empty($items)) { $history CHistory::get([ itemids $items[0][itemid], time_from time() - 300, limit 1, sortfield clock, sortorder DESC ]); if (!empty($history) $history[0][value] 80) { $score - 10; } } $result[$hostid] max(0, $score); // 保底0分 } // 4. 返回JSON $this-setResponse(new CControllerResponseData([host_health $result])); } }关键点说明checkPermissions()复用现有权限系统无需新配角色getRequest()是Zabbix封装的参数获取方法自动处理GET/POST/JSON body所有数据查询都用Zabbix原生类CHost::get、CTrigger::get保证数据一致性计算逻辑放在PHP层而非前端JS避免客户端伪造分数4.3 步骤二在主机列表页注入健康分app/views/host.list.php找到tbody标签内循环主机的代码块在tr开始处插入!-- 健康分列 -- td classcenter span idhealth_?php echo $host[hostid]; ?--/span script typetext/javascript // 异步加载健康分 (function(hostid) { fetch(zabbix.php?actionhost.health.gethostids hostid) .then(response response.json()) .then(data { const score data.host_health[hostid] || 0; const span document.getElementById(health_ hostid); span.textContent score; // 根据分数设颜色 if (score 80) span.style.color #4CAF50; // 绿 else if (score 60) span.style.color #FF9800; // 橙 else span.style.color #F44336; // 红 }) .catch(err console.error(Health load failed:, err)); })(?php echo $host[hostid]; ?); /script /td这里用了原生fetch不依赖jQueryZabbix 6.0已移除jQuery且每个主机独立请求避免单点失败影响全局。4.4 步骤三配置Nginx反向代理防跨域由于Zabbix Web默认不支持CORS前端fetch会因跨域被浏览器拦截。解决方案不是开CORS头有安全风险而是用Nginx做路径代理location /zabbix/zabbix.php?actionhost.health.get { proxy_pass http://zabbix-web:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }这样前端请求/zabbix/zabbix.php?actionhost.health.get实际被Nginx转发到后端浏览器认为是同源请求。4.5 实测效果与性能验证部署后我们用Apache Bench压测ab -n 1000 -c 50 http://zabbix.example.com/zabbix/zabbix.php?actionhost.health.gethostids10084结果平均响应时间23ms99%请求50msQPS稳定在2100。对比主机列表页本身平均18ms增加健康分只带来5ms延迟完全可接受。更关键的是稳定性当某台主机的system.cpu.util监控项不存在时CItem::get()返回空数组代码里的if (!empty($items))自然跳过CPU扣分不会报错。这种“防御性编程”是Zabbix代码的标配你照着写基本不会翻车。5. 常见问题与避坑指南那些官方文档绝不会告诉你的实战经验5.1 问题排查速查表从页面空白到500错误的10种典型场景现象可能原因定位方法解决方案页面空白Network显示200但Response为空index.php里require_once define.inc.php路径错误查/var/log/zabbix/zabbix_web.log看是否有Failed opening required检查/usr/share/zabbix/include/是否存在权限是否为zabbix:zabbix点击任何按钮都跳403 Forbidden用户角色缺少ZBX_PERMISSION_READ权限进数据库查users表确认roleid再查roles表确认type1(user role)给用户分配Zabbix administrator角色或自定义角色时勾选Read新增的action在URL里无效始终404控制器类名不符合C*命名规范用find /usr/share/zabbix -name *HealthGet.php确认文件存在类名必须是CHostHealthGet文件名必须是CHostHealthGet.php大小写敏感健康分显示--Console报fetch failedNginx未代理host.health.get路径浏览器DevTools Network标签看请求是否返回502检查Nginx配置确认proxy_pass指向正确的后端容器名和端口修改CHostList.php后列表不更新PHP OPcache未清除执行php -i | grep opcache确认opcache.enable1运行sudo systemctl reload php-fpm或opcache_reset()数据库查询超时页面卡死CHost::get()的$options未限制limit在控制器里var_dump($options)看是否有limit参数显式添加$options[limit] 100避免全表扫描健康分计算结果与预期不符CHistory::get()的时间范围单位是秒不是毫秒查include/classes/api/services/CHistory.php源码time_from必须是Unix时间戳整数time() - 300正确Date.now() - 300000错误新功能上线后其他页面报错修改了define.inc.php里的常量检查/var/log/php-fpm/www-error.log恢复原文件用$config数组覆盖方式实现定制AJAX返回JSON但前端解析失败CControllerResponseData未设置Content-Type用curl命令curl -I url看响应头确保控制器里new CControllerResponseData()后没有手动echo其他内容多语言文本不显示显示_(开头的占位符app/i18n/en_gb/目录下缺少对应翻译文件查include/locales.php确认语言包路径复制en_gb目录为zh_cn翻译host.list.php里的_(Health Score)5.2 三个血泪教训关于Zabbix架构定制的终极忠告提示不要在include/目录下新建PHP文件Zabbix的include/目录是核心代码区所有require_once都从这里加载。有次同事想加个工具函数新建了include/my_utils.php结果Zabbix升级时apt upgrade直接把这个文件删了导致整个Web挂掉。正确做法是把工具函数写在classes/目录下比如classes/utils/CMyUtils.php然后在控制器里require_once它。这样升级时classes/目录不受影响。注意Zabbix的“缓存”不止APCu一种除了前面说的APCu权限缓存Zabbix还有三处缓存① MySQL查询缓存已弃用但旧配置可能开着②CConfig::getInstance()的配置缓存有效期10分钟③ 前端JS的zbx_cache对象存着最近请求的API结果。某次我们改了健康分算法前端一直显示旧分数查了半天才发现是JS缓存没清解决方案是在fetch URL里加时间戳参数t?php echo time(); ?。警惕Zabbix的“扩展点”其实很少别硬找钩子很多开发者想学WordPress那样加add_action()钩子但在Zabbix里行不通。它的扩展哲学是“替换而非增强”——你要改主机列表就复制CHostList.php为CHostListCustom.php然后在index.php里改控制器工厂逻辑。虽然麻烦但保证了行为可预测。我们团队的实践是所有定制代码放在/usr/share/zabbix-custom/目录用符号链接指向原位置升级时只更新符号链接不动定制代码。5.3 性能优化黄金法则Zabbix Web的5个关键参数Zabbix Web的性能不取决于服务器配置而在于这5个参数的合理设置。它们都在/etc/zabbix/web/zabbix.conf.php里$ZBX_SERVER必须填Zabbix Server的IP不能填localhost。填localhost会导致Web服务器和Server在同一台机器争抢CPU实测QPS下降40%。$ZBX_SERVER_PORT默认10051但如果Server启用了TLS必须同步改这里否则API调用全失败。$ZBX_HISTORY_PERIOD控制历史数据查询跨度默认3600秒。如果客户只要看最近15分钟改成900能减少75%的DB压力。$ZBX_PAGE_SIZE列表页每页条数默认50。线上环境建议调到20避免单次SQL返回过多数据拖慢PHP内存。$ZBX_SESSION_NAMESession名称默认zbx_session。多套Zabbix共用域名时必须改成唯一值否则用户会互相登出。改完这些参数记得重启PHP-FPMsudo systemctl restart php-fpm。别信“改完配置自动生效”的说法Zabbix Web的配置是进程启动时加载的。6. 后续演进思考当Zabbix Web遇上现代前端框架Zabbix Web的架构在2012年诞生时是先进的但今天看它的“PHP直出HTML少量JS”模式确实遇到了瓶颈。比如我们想给健康分加个趋势图现在只能用Zabbix自带的div classgraph但客户想要ECharts的3D热力图。硬塞进去会破坏原有CSS而且Zabbix的JS加载器不支持AMD模块。我的建议是用微前端思路渐进式替换。不推倒重来而是把新功能做成独立Vue应用通过iframe src/health-dashboard/嵌入。Zabbix Web提供/api/auth接口返回JWT tokenVue应用用这个token调用Zabbix REST API。这样Vue应用可以自由用ECharts、Ant Design而Zabbix Web保持稳定。我们已在测试环境跑通首页仪表盘用Vue重写其余页面仍是原生Zabbix切换无缝。这条路的挑战不在技术而在心智模型——运维工程师要习惯“前端也是服务”的概念。但好处巨大新功能开发速度提升3倍UI设计师能直接改Vue组件不用学PHP。Zabbix官方也在往这方向走7.0版本已内置Vue CLI构建脚本只是还没开放给用户。最后分享个小技巧Zabbix Web的app/js/目录里所有JS文件都有// zabbix-module注释这是它的模块标识。如果你想给hostlist.js加功能不要直接改它而是新建app/js/custom/health-score.js在index.php里用zbx_add_js()注入。这样升级时你的定制JS还在原生JS被覆盖也没关系。这个技巧帮我们团队在过去三年里零故障完成了5次Zabbix大版本升级。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑