1. 与Windows说再见:Jexus到底解决了什么痛点
前些日子帮朋友迁移一套老旧的ASP.NET系统,服务器是Windows Server 2012,IIS上跑着WebForms应用,老板一声令下要压成本迁到Linux,朋友第一个电话就打给了我。这种场景我太熟悉了:在Linux上跑.NET,Mono 是必然选择,但真正把应用撑起来、跑得稳,光有Mono远远不够。Jexus 就是在这个夹缝里生存下来的项目——它是一款专门为 Linux/Unix 平台设计的 .NET Web 服务器,核心任务就是替掉 IIS,让 ASP.NET、ASP.NET MVC 应用能稳稳当当地跑在 Linux 上。
Jexus 的定位不是万能网关,也不是抢 Nginx 地盘的反向代理,它把“站点管理、请求处理、进程守护、URL 重写”这些 IIS 干的事精简成了一个独立服务。对于在 Windows 上发布过 .NET 网站、又不太想跟 FastCGI 配置死磕的同学来说,Jexus 是最接近“IIS 换皮到 Linux”的体验。你不用手动拼装 Nginx 加 Mono 加各种模块,装好它,写一个站点配置文件,启动服务,域名解析一绑定,应用就活了。适合三类人:第一类是手里攥着 WebForms 旧项目、不敢乱动代码的运维;第二类是想把 .NET 应用部署到低配 Linux 服务器上的小团队;第三类是研究 .NET 跨平台方案的技术爱好者。
1.1 它不是简单把 IIS 换个壳
很多人第一次听到 Jexus,会把它理解成“Linux 版 IIS”。这个说法不准确。IIS 和操作系统深度绑定,从内核网络栈到 Windows 服务管理器都有牵连,Jexus 做不到、也不需要做这种事。它是一个完全独立的用户态进程,通过 Mono 运行时承载 ASP.NET 请求管线,再自己实现一套 HTTP 服务器逻辑:监听端口、解析请求、路由到站点、调度 ASP.NET 处理程序、返回响应。
所以你在 Jexus 里看到的站点配置思路,会更接近轻量级服务器习惯。每个站点用一份独立配置文件,包含绑定的域名、端口、物理路径、日志开关,主配置文件控制全局行为。这种“一个应用一份文件”的模型,比 IIS 里层层嵌套的“应用程序池 + 站点绑定 + 模块列表”要直观得多。我在第一次配 Jexus 时,从解压安装到跑起一个测试站点,只用了不到十分钟,当时的感受是:这就是给 .NET 运维准备的“配置即服务”。
1.2 什么时候该选它,什么时候别碰它
Jexus 并不是银弹,选型之前要分清边界。如果你的应用是 .NET Core / .NET 5+,那根本不需要 Jexus,直接 Kestrel + Nginx 反代就是主流方案,性能更好,生态更成熟。Jexus 的主场是 .NET Framework 时代的产物,特别是 WebForms、WCF、老式 ASHX、ASMX 这些跑在 System.Web 管线上的东西。Mono 对 System.Web 的兼容性不错,但兼容归兼容,HTTP 服务器这一层还是需要一个既懂 Mono 又懂 ASP.NET 生命周期的东西来对接,Jexus 恰恰就是这一层。
我个人的判断标准很简单:应用如果能在 Windows 的 IIS 上以 Classic Mode 跑起来,迁移到 Linux 时就值得试试 Jexus;如果应用大量依赖 Windows 独有的系统组件,比如 Active Directory、专用硬件调用、复杂 COM 互操作,那 Jexus 帮不了你,Mono 也帮不了你,这种项目根本不适合做 Linux 迁移,要么留在 Windows,要么干脆用新框架重构。另外,如果你只是需要静态站或者纯后端 API,Jexus 不是最好选择,Nginx 或者直接上 Docker 明显更顺手。
2. 安装与初始化:从下载到第一个站点跑起来
Jexus 的安装属于“下载、解压、执行脚本”三连,但有几个前置条件必须处理好,否则后面会遇到一堆莫名其妙的坑。我建议你在装 Jexus 之前,先在干净的 Linux 环境里把 Mono 装好、跑通一个最简单的 aspx 页面,确认运行时没问题,再引入 Jexus。这个顺序能帮你把“环境问题”和“服务器问题”隔离,排查时方向更清晰。
2.1 环境准备:Mono 版本怎么选
Jexus 依赖 Mono 提供 .NET Framework 兼容运行时,所以 Mono 的安装质量直接决定站点稳不稳。我踩过的第一个坑就是图省事只装了 mono-runtime,结果跑起来之后一堆程序集找不到。这里直接给结论:Debian/Ubuntu 系优先装 mono-complete,它包含完整的参考程序集、编译器和运行时,少折腾。
sudo apt update sudo apt install mono-complete -y mono -V执行完最后一条命令会输出 Mono 版本号。我的经验是保持 Mono 在 6.x 的较新小版本上,旧版本对 MVC 5、Web API 2 的支持不够好,个别特性在运行时会直接抛 NotImplementedException。另外要注意:Jexus 和 Mono 的版本之间存在隐性兼容窗口,不要盲目升级到最新 Mono。曾经有一次我把 Mono 升到 6.12,Jexus 进程频繁退出,回退到 6.10 之后一切正常。这类问题官方文档记录不全,稳妥做法是上线前先在测试环境做一次“Jexus + Mono”组合验证。
2.2 下载、解压、安装三步走
Jexus 官方提供编译好的二进制包,无需自己编译源码。以 5.8.2 x64 版本为例,安装流程如下:
wget https://www.jexus.org/jexus-5.8.2-x64.tar.gz tar -zxvf jexus-5.8.2-x64.tar.gz cd jexus-5.8.2 sudo ./installinstall 脚本会自动把程序文件放到 /usr/jexus 目录,这一步需要 root 权限。装完之后目录结构大概是这样的:
/usr/jexus/ ├── jws ├── jws.regsvr ├── jws.restart ├── jexus.conf ├── siteconf/ │ └── default └── log/jws 是主执行程序,jexus.conf 是全局配置文件,siteconf 目录放站点配置,log 目录积累运行日志。建议把整个 /usr/jexus 目录的属主改为你计划运行服务的普通用户,避免让 Web 服务直接以 root 身份运行。安全线这个东西,平时不觉得,真出事就是大事故。
2.3 基本的启停命令与自启动
Jexus 的启停命令不是 systemctl,至少在 5.x 系列不是,它自带一套脚本式命令:
cd /usr/jexus sudo ./jws start sudo ./jws stop sudo ./jws restart sudo ./jws -vstart 之后可以看 log/jws.log 确认启动过程有没有报错。没有报错不代表端口一定在监听,这时候用 ss 或 netstat 检查一下:
ss -lntp | grep 80如果想让 Jexus 开机自启,可以在 /etc/rc.local 里加一行启动命令,或者用 systemd 写一个简单的 service 单元文件,后者更规范。我一般是写 service 文件,因为 rc.local 在新系统里默认不执行,踩过这个坑之后就长记性了。
3. 配置详解:一套配置吃透 Jexus
Jexus 的配置体系由两层组成:全局配置和站点配置。全局配置管的是服务器级别的行为,站点配置管的是单个应用的行为。理解这个分层之后,你会发现它的配置哲学比 IIS 更接近 Nginx 的 site 文件风格,但又比 Nginx 简单,因为需要暴露的参数本身就不多。
3.1 主配置文件 jexus.conf 的职责
先看 /usr/jexus/jexus.conf,这份文件里比较重要的是监听端口和默认站点的定义。比如默认配置里会有类似这样的内容:
listen 80; site_default default;listen 指定 Jexus 主服务监听的端口,site_default 指定默认加载哪个站点配置。这里的 default 对应 siteconf 目录里的 default 文件。如果服务器上有多个 IP,或希望 Jexus 监听在特定地址上,可以在 listen 里写 IP:端口,例如:
listen 0.0.0.0:80;这个参数和站点配置里的 port 是两回事,容易混淆。listen 管的是 Jexus 进程本身在哪个端口等待连接,siteconf 里的 port 是在多站点共享同一端口时,通过域名区分流量的绑定关系。两者配合起来才是完整的“入口到应用”链路。
3.2 站点配置字段逐个解读
每个站点对应 siteconf 目录下的一个文件,文件名就是站点标识。一个最基础的站点配置长这样:
port=80 root=/var/www/mysite hosts=www.mydomain.com- port:站点对外服务的端口,通常与全局 listen 保持一致。端口相同、域名不同,Jexus 会按 hosts 做分发。
- root:站点物理路径,也就是你发布目录在 Linux 上的位置。这里要注意路径必须以 / 结尾,并且目录权限要正确,否则 Jexus 会报 403 或者干脆找不到文件。
- hosts:绑定的域名,多个域名用逗号分隔。直接通过服务器 IP 访问时,如果 hosts 里没写 IP,请求可能落不到这个站点,这个细节很多人栽过。
除了这三个核心字段,还有几个常用但不一定每个版本都有文档说明的字段。我这里列一下我实际用过且稳定的:
| 字段 | 作用 | 备注 |
|---|---|---|
| default_page | 默认首页文件 | 类似 IIS 默认文档,常用 index.aspx |
| log_mode | 访问日志开关 | 建议生产环境开 |
| log_path | 日志路径 | 独立于全局日志 |
| use_gzip | 是否启用压缩 | 静态资源多时建议开 |
| use_fastcgi | 是否启用 FastCGI | 对接 PHP 等场景用 |
| process_num | 工作进程数 | 高并发场景调整 |
需要注意的是,不同版本对字段的支持会有差异,配置前先看对应版本文档。我见过一个生产事故就是运维照着旧文档写了一个新版已废弃的参数,Jexus 启动时直接跳过整个站点配置,网站全挂。
3.3 多站点和默认站点怎么配
多站点的核心思路是一份配置一个应用。假设服务器上同时有 a.com 和 b.com 两个站点,就在 siteconf 里建两个文件,分别写各自的 port、root、hosts。注意 hosts 一定要写清楚域名,避免出现“两个站点抢流量”的混乱局面。
默认站点承担的是“兜底”职责:当访问者的 Host 头不匹配任何站点配置时,请求会落到默认站点。这个兜底策略在生产环境很有用,比如可以在默认站点放一个 404 提示页,或者引导到公司门户。我个人习惯把默认站点 root 指向一个极其简单的静态页面,而不是指向真实业务应用,这样做的好处是:那些直接扫 IP 的流量不会落到真实业务上,降低被探测的风险。
4. 进阶玩法:反向代理、SSL 和 Nginx 配合
Jexus 本身就已经是一个能独立承担 HTTP 任务的服务器,但真实环境往往不是单打独斗。把 Jexus 作为 ASP.NET 应用的宿主,把 Nginx 放在最前面处理静态文件、负载均衡和 SSL,是一种非常成熟的组合方式。我下面聊的都是我在生产环境验证过的方案。
4.1 用 Jexus 做反向代理
Jexus 不只是托管 .NET 应用,也能做反向代理。场景通常是这样的:.NET 应用跑在 Jexus 上,同时服务器上还有一个 Java 服务或者 Node 服务,你想让它们共享 80 端口,按路径转发到不同后端。这时候 Jexus 可以把特定路径的请求代理到本地另一个端口。
配置思路类似:
port=80 root=/var/www/mysite hosts=api.mydomain.com useproxy=true proxypath=/api proxyto=http://127.0.0.1:9000这种玩法适合中小型团队,服务器数量不多,不想再引入一层网关时非常方便。不过我要提醒一句:你的代理需求一旦变得复杂,比如需要按权重分流、需要健康检查、需要动态路由,Jexus 就不是最优解了,这时候规规矩矩上 Nginx 或者更专业的网关组件更靠谱。
4.2 配置 HTTPS 证书
给 Jexus 配 HTTPS,主要工作在证书文件上。证书格式方面 Jexus 通常接受 pem 和 key 文件,也就是 Nginx 常见的那套格式。假设你从证书厂商拿到了 fullchain.pem 和 privkey.pem,放到一个安全目录,比如 /etc/ssl/jexus/,然后在站点配置里加证书相关参数:
port=443 root=/var/www/mysite hosts=www.mydomain.com certfile=/etc/ssl/jexus/fullchain.pem keyfile=/etc/ssl/jexus/privkey.pem这里有两个坑。第一个是证书文件权限:私钥文件一定要确保只有 root 或运行 Jexus 的用户可读,否则别人拿到私钥就是灾难。第二个是证书链完整:fullchain.pem 必须包含中间证书,不能只放叶证书,否则手机端和部分桌面浏览器会报“证书不完整”。我有一次就是因为只贴了域名证书,Android 一切正常,iPhone 一直提示连接非私人,排查了半天才发现是证书链缺失。
4.3 Jexus 与 Nginx 的经典前后端组合
生产环境中我更推荐的前置组合是:Nginx 监听 80/443,承担 TLS 终结、静态文件直接返回、动态请求反向代理到 Jexus。Jexus 监听一个内网端口,比如 8080,只服务于 .NET 应用请求。这样有几个明显好处:Nginx 的高并发静态文件处理能力很强,把图片、CSS、JS 交给它,能减轻 Jexus 压力;TLS 证书的更新和配置集中在 Nginx,不用每次改 Jexus 站点配置;将来如果 .NET 应用需要扩容,Nginx 可以做负载均衡,Jexus 可以起多个实例。
我常用的 Nginx 动态请求转发配置片段大致是这个思路:
location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }Jexus 那边站点配置里的 port 就是 8080,hosts 可以留空或填写 127.0.0.1、localhost 这类内网标识,因为所有真实域名流量都被 Nginx 处理过了。分层之后,Jexus 的日志会清爽很多,只有真正的 .NET 应用请求,排查问题的时候也不用在一堆静态文件访问里捞业务日志,体验非常好。
5. 实战复盘:把一个 ASP.NET MVC 站点搬上 Jexus
理论说再多,不如完整走一遍迁移流程。这里以我最近做的一个 ASP.NET MVC 5 项目迁移为例,把从改造到上线的每一步都拆开讲。项目背景很简单:老系统,SQL Server 数据库,Windows 服务器,代码跑了好几年,业务不敢大改。
5.1 项目改造:代码层面要动的地方
首先明确一点:Jexus 并不要求你重写代码,但有几个地方几乎每个项目都要动。
第一个是路径大小写问题。Windows 文件系统不区分大小写,Linux 区分,代码里如果写死了引用某个路径,大小写对不上就 404。我的习惯是发布前在 Windows 上把项目里所有涉及文件路径的地方检查一遍,全局搜索引用的文件名,确保与实际文件名一致。
第二个是 web.config 里和 Windows 环境强相关的内容。比如连接字符串里的 Integrated Security、数据库服务器名,这些在 Linux 上都跑不通。SQL Server 连接字符串要改成带用户名密码的完整形式,并且确认 Linux 服务器到数据库的网络链路是通的。数据库驱动方面,Mono 自带的 SQLClient 对老版本 SQL Server 兼容还可以,如果遇到连不上问题,先排查驱动版本,再排查连接参数。
第三个是程序集依赖。System.Web 系列在 Mono 下大部分可用,但像 System.Drawing 这种在某些场景下依赖 Windows 底层库的东西,可能行为不一致。如果项目里用了图形验证码、图片裁剪,建议提前在 Linux 测试环境跑一遍完整流程,不要等到上线再验证。
5.2 发布与部署的上线流程
我在 Windows 上通过 Visual Studio 发布项目,选择“文件系统”发布方式,目标目录选一个干净的文件夹。发布完成后,把整个目录用 tar 打包传到 Linux 服务器,解压到站点物理路径,比如 /var/www/mysite。
注意一点:不要把发布目录直接放在 root 家目录下,也不要用 root 权限去跑应用。我会新建一个专用系统用户,比如 jexus 或 www-data,把站点目录属主改成这个用户。然后修改 web.config,把连接字符串和可能存在的路径配置都改成 Linux 环境的值。
接着写站点配置文件,假设我放在 siteconf 里叫 mysite:
port=8080 root=/var/www/mysite/ hosts=www.mydomain.com default_page=index.aspx log_mode=true然后重启 Jexus:
sudo ./jws restart到这里,如果配置正确、Mono 运行时没问题,浏览器打开域名就能看到站点了。第一次访问通常会比 Windows 下的 IIS 慢一点,因为 Mono 的 JIT 编译需要时间,访问了几个页面之后速度会稳定下来。不要一上来就因为首次访问慢就怀疑 Jexus,给它几分钟“热身”时间。
5.3 性能与安全优化建议
站点稳定运行之后,建议做三件事。
第一,开启 gzip 压缩。如果站点配置支持 use_gzip 参数,直接打开;不支持的版本,用 Nginx 前置压缩也完全可行。压缩对减少带宽消耗效果非常明显,尤其页面里有大量文本和 JSON 接口的场景。
第二,设置访问日志轮转。Jexus 的访问日志如果不管理,几个月就能吃掉好几个 GB 磁盘。我在 cron 里加了一条任务,每天把日志归档压缩,保留最近 30 天,超过的自动删除。这条看似不起眼,但在磁盘只有 40G 的旧服务器上能救命。
第三,隐藏服务器特征。这是很多人容易忽略的安全细节。可以在 Jexus 默认页面上不展示任何版本信息,也建议把不用的站点、默认站点从 siteconf 目录里清理掉,避免不必要的暴露面。安全不是单靠一个开关搞定的,但每关掉一个口子,攻击面就小一分。
6. 常见问题速查与实践心得
运维 Jexus 两三年,踩过的坑不少。这里不按教程的套路讲理论,而是直接把问题、现象、解决办法列出来,方便大家对照自检。
6.1 高频故障与排查清单
| 问题现象 | 常见原因 | 排查与解决 |
|---|---|---|
| 启动提示端口占用 | 其他 Web 服务器占用了端口 | 用 ss -lntp 找到占用进程,停掉或改 Jexus 端口 |
| 访问站点 404 | root 路径错误或 hosts 没匹配 | 检查站点配置里的 root 与 hosts,确认大小写 |
| 页面报 500 错误 | Mono 兼容问题或代码运行时异常 | 查看 log 目录下的错误日志,重点看堆栈中 System.Web 相关内容 |
| 图片/CSS 打不开 | 静态文件路径大小写不一致 | 修正代码中的路径引用 |
| 连接数据库失败 | 连接字符串配置错误或驱动缺失 | 确认 SQL Server 连接串、驱动在 Mono 下的可用性 |
| Jexus 进程频繁退出 | Mono 版本与 Jexus 版本不兼容 | 回退 Mono 到稳定版本,重启服务验证 |
| 第一次访问特别慢 | Mono JIT 即时编译 | 预热站点,或定期访问关键页面保持 JIT 缓存 |
最容易被忽略的是“默认站点”的影响。很多时候你配置了一个新站点,但 hosts 写错了,或者干脆没写,请求落到默认站点,表现就是明明配了域名却打开一个无关页面。处理方式是把默认站点的内容设置成一个明确的提示页,这样一旦出现异常流量你立刻能感知到,而不是在自己的一堆站点里瞎猜。
6.2 几个让我长期受益的配置习惯
第一个习惯:站点配置文件的命名与业务一一对应。不要用 default、site1、site2 这种名字,而是用业务名,比如 pay、crm、portal。Jexus 进程很多,出了一堆同名文件时,靠内容里的域名区分实在痛苦,命名清晰能省很多时间。
第二个习惯:改动配置前先备份。Jexus 的站点配置没有“回滚”按钮,改之前复制一份 .bak 是几秒钟的事,出问题就能立刻恢复。这不是什么高级技巧,但能在半夜故障时保住你的睡眠时间。
第三个习惯:监控进程与端口。我自己用的是一个简单的监控脚本,每五分钟检查一次 jws 进程和对应端口,如果发现服务异常就自动重启并记录日志。Jexus 本身很稳定,但毕竟是一个相对小众的项目,自动化的守护非常有必要,尤其是业务不能断的场景。
第四个习惯:Mono 与 Jexus 的版本组合一旦稳定,就固定下来。生产环境里“稳定跑着”的系统不要随便升级。Jexus 社区更新节奏不快,别指望它能像 Nginx 那样频繁迭代。固定版本组合,把精力放在应用层优化上,比追求新版本更有价值。
我个人在实际操作中还有一个私藏技巧:给 Jexus 的前置 Nginx 配置一个独立 upstream,指向 Jexus 的内网端口,并设置较长的超时时间。原因是老旧的 WebForms 应用里偶尔会有长时间运行的报表请求,Nginx 默认的 60 秒超时可能直接掐断它们。把 proxy_read_timeout 调到 300 秒后,这类超时错误基本消失。这个细节在文档里不会写,但真实环境里特别有用。