ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Delphi UniGUI部署:三种模式选型对比

Delphi UniGUI部署:三种模式选型对比 1. 部署模式全景Standalone、ISAPI、Windows Service到底该选谁UniGUI这么多年在Delphi圈子里能一直有人用核心优势就是“一套VCL代码直接出Web应用”。但这个优势真正落地的时候第一道坎不是写代码而是部署。我见过太多人在开发环境里点一下F9跑得飞起到了生产环境却不知道怎么把程序“放”到服务器上。UniGUI的部署选项其实是三条主线Standalone独立执行程序模式、ISAPI模式、Windows Service模式。每条线解决不同的问题没有绝对的好坏只有适合不适合。1.1 三种模式的适用场景对比先上一张我自己整理过的对比表方便你快速建立整体认知部署模式交付形态适合场景维护量并发能力Standalone单exe自带HTTP服务器内网工具、小规模业务系统、开发演示低拷过去就能跑中等取决于服务器配置和线程池设置Windows Service编译为服务程序需要7x24小时后台运行的内网生产系统低开机自启异常自动拉起中等和Standalone相近ISAPIDLL挂在IIS下大型系统、需要和现有IIS站点混部、利用IIS进程管理能力较高更新时需要回收应用池高可借助IIS集群和负载均衡这里要强调一个容易混淆的点很多人以为ISAPI是UniGUI自己定的东西其实ISAPI是微软定义的一套Web服务器扩展接口UniGUI只是把业务逻辑编译成了符合这个接口的DLL。换句话说Standalone是UniGUI自己做了个微型Web服务器ISAPI是UniGUI交出控制权让IIS来加载你的DLL。1.2 决定选型的关键考虑因素选型时我一般会问自己三个问题第一服务器是谁维护的。如果运维权限抓在客户手里对方还只会装个Win Server、开个IIS那ISAPI是最好的选择因为IIS是他们熟悉的范畴。如果客户就给你一台裸机让“以最简单方式跑起来”优先那Standalone直接省去所有IIS配置成本。第二程序要不要频繁更新。Standalone模式更新时直接把exe替换掉就行如果是Windows Service停服务、替换、启服务一条命令链就完成了。ISAPI模式更新DLL时IIS会锁定文件必须回收应用池这中间如果处理不好在线用户会直接掉线。第三有没有公网访问需求。只要涉及公网访问我基本不推荐裸奔的Standalone因为它的内置HTTP服务器没经过高并发和恶意流量测试安全防护也比较基础。稳妥做法是Standalone绑定内网端口前面挂一层Nginx等反向代理做HTTPS和限流或者直接走ISAPI让IIS来扛。另外提醒一句如果你是从UniGUI官网下载的试用版部署时要注意许可机制——试用版本体会在程序运行一段时间后弹出提示或自动停止。正式交付前务必确认好授权方式这是很多新手栽跟头的地方。2. Standalone模式最省心的部署方式2.1 内置HTTP服务的启动过程UniGUI的Standalone模式说白了就是程序一跑起来UniGUI内部启动了一个HTTP Listener然后监听你指定的端口。你编译出来的exe既是业务逻辑又是Web服务器。开发时按下F9启动的其实就是一个Standalone实例。你在浏览器输入http://localhost:8077就能看到界面。这也是为什么UniGUI调试体验好——没有IIS那层中间环节出问题定位快。生产环境直接复用这个模式完全可行。我在一个客户现场就干过这事对方内网有一台Windows Server 2016不想装IIS业务系统只有二十几个内部用户使用。我把编译好的exe复制过去用sc create命令做成系统服务再设置一个固定端口半小时搞定交付。2.2 端口选择与配置细节端口配置在ServerModule里进行。新建UniGUI工程时默认端口是8077。这个端口号记住就行调试阶段不需要改。到了生产环境端口选择有几个原则避开8080、80这些常用端口防止和客户现有的应用冲突尽量选择一个高位端口比如8090、9001减少被扫描器盯上的概率如果服务器有防火墙记得同步放行对应端口的入站规则用代码方式修改端口也是可以的。在ServerModule的OnCreate事件里或者在ServerModule.Create之后设置Server.Port属性即可procedure TUniServerModule.UniServerModuleCreate(Sender: TObject); begin Self.Port : 9001; Self.ServerSecurity : 0; // 0HTTP, 1HTTPS end;这里Self.ServerSecurity是控制HTTP还是HTTPS的开关置为1时还要配合证书配置后面我会单独写一段。2.3 生产环境Standalone要注意的隐藏问题Standalone最典型的坑有三个我挨个说一下。第一个坑是用户没退出程序被强杀。Standalone模式跑起来后如果直接关掉控制台窗口在线Session可能没来得及持久化用户数据容易丢失。虽然UniGUI有Session恢复机制但保险起见生产环境不要在桌面上开着控制台跑要么用Windows服务承载要么用TrayIcon最小化到托盘。第二个坑是操作系统休眠。如果程序跑在笔记本或开启了睡眠策略的服务器上系统一睡网络连接全部断开Wake up后不一定能自动恢复。部署时务必把服务器的电源计划设为“从不睡眠”。第三个坑是单IP多实例的端口冲突。同一台服务器上想跑两套UniGUI程序必须分配不同端口。但如果你用了0作为端口号UniGUI会自动获取系统空闲端口这样每次启动端口都可能变外部连接就找不到了。所以生产环境一定要用固定端口。3. 把UniGUI做成Windows服务后台运行的正确姿势3.1 使用内置Service组件改造成服务的思路Standalone模式的exe在服务器上还有一个问题必须有人登录Windows程序才会在桌面会话里启动。一旦服务器重启没人登录程序就起不来。解决方式很直接——把程序变成Windows Service。UniGUI原生的做法是用它的TUniGUIServiceApplication组件。新建工程时如果你选择的是Service Application模板生成的工程就是服务形态。原理很简单UniGUI不再把UI渲染绑定到桌面会话而是直接在Session 0服务会话里运行HTTP Listener普通用户通过浏览器访问时完全感觉不到后台是服务。如果你已经有一个Standalone工程改造也不复杂。核心步骤是把工程文件中默认的Application.CreateForm(TUniServerModule, ...)相关逻辑换成TUniGUIServiceApplication启动方式在ServerModule里放置一个TUniServiceApplication组件设置服务的显示名称、服务名称、启动类型等信息编译后通过sc create或InstallService方法注册到Windows服务管理器3.2 服务安装、卸载与自动启动我不喜欢在ServerModule里放一个按钮来安装服务虽然UniGUI提供了一行代码安装的方式但生产环境我更喜欢用命令行来管理这样自动化脚本也好写。假设编译出来的程序叫MyBizServer.exe以管理员身份打开cmd执行sc create MyBizServer binPath C:\UniGUIApp\MyBizServer.exe start auto sc description MyBizServer UniGUI业务服务 sc start MyBizServer注意binPath等号后面必须有一个空格这是sc命令的一个历史遗留坑。如果忘了加空格命令会报参数错误。卸载时先停止服务再删除sc stop MyBizServer sc delete MyBizServer如果想做得更精细还可以用sc failure命令设置服务异常退出后的自动重启策略sc failure MyBizServer reset 86400 actions restart/5000/restart/10000/restart/60000这条命令我强烈建议加它表示如果服务因为未处理异常崩溃Windows会在5秒后自动重启再次崩溃隔10秒再重启第三次隔60秒。这样半夜服务挂了你都不用起床。3.3 服务模式下的日志与监控Windows服务模式下程序是后台运行的日志成为你唯一能“看到”程序内部状态的手段。UniGUI自带的日志机制默认会输出到程序所在目录的uniGUI子文件夹里面会有请求日志和异常日志。我一般会在ServerModule的OnLog事件里接一条日志转发把内容同步写到Windows事件日志中procedure TUniServerModule.UniServerModuleLog(Sender: TObject; ALog: TUniLogInfo); begin TUniLogWriter.WriteToWindowsEventLog(ALog.Text); end;这样即使在服务没有控制台窗口的情况下你也可以在“事件查看器”里看到UniGUI的运行日志排错效率会高很多。另外服务模式下如果出现“程序启动了但端口打不开”的情况先检查是不是服务权限问题。默认的LocalSystem账户权限很高但有些客户会用普通域账户跑服务这个时候监听高端口可能会被系统策略挡住。遇到这种情况直接在服务属性的“登录”选项卡里切换账户或者给该账户分配SeNetworkLogonRight就行了。4. ISAPI模式借助IIS部署UniGUI DLL4.1 ISAPI模式的配置流程ISAPI模式是UniGUI在企业环境里最“正经”的部署方式。开发时新建一个ISAPI类型的UniGUI工程编译后得到一个DLL放到IIS的虚拟目录中配置好应用池访问http://域名/虚拟目录/你的DLL名称.dll就能打开页面。配置流程看起来不复杂但每一步都有细节第一步创建Web站点。在IIS里建一个站点物理路径指向你的DLL所在目录绑定端口和域名。第二步设置应用程序池。这里有个关键如果IIS是32位模式运行的取决于你系统装了什么版本的IIS和是否有32位组件必须把应用池的“启用32位应用程序”设为True否则加载DLL时会报错。UniGUI官方对32位还是64位的问题说得比较含糊实践时我建议直接按照你Delphi的编译目标位来XE系列默认编译32位就开32位池编译64位就需要确保IIS应用池也对应64位模式。第三步添加处理程序映射。在站点功能里找到“处理程序映射”添加一个脚本映射请求路径为*.dll可执行文件选择C:\Windows\System32\inetsrv\isapi.dll名称随意。这一步是让IIS知道DLL请求要交给ISAPI扩展去执行。第四步配置身份验证。如果是内网系统一般用Windows身份验证如果是对外系统通常用匿名身份验证。注意ISAPI模式下通过Request.Browser或Session拿到的用户IP等信息与Standalone模式下略有差异取客户端IP时要考虑反向代理头。4.2 热更新与性能取舍ISAPI最大的痛点是DLL被IIS锁定。你想更新系统直接把新DLL复制过去会提示“文件被占用”。常规做法是在IIS里对被占用的站点执行“回收应用程序池”回收后DLL解锁你才能覆盖。我遇过一个小概率但很尴尬的情况回收了应用池DLL还是被锁。原因是有个残留的w3wp.exe进程没有被杀掉手动在任务管理器里结束进程或者用iisreset重启IIS服务才能解决。性能方面ISAPI模式其实比Standalone更扛压因为IIS自带工作进程隔离、故障快速失败、CPU限制等功能。每个ISAPI应用池默认会启动多个w3wp.exe工作进程UniGUI的Session状态如果使用了内存存储多进程之间Session是不能共享的。这就带来一个很现实的限制ISAPI模式下除非把Session存储改成SQL Server或Redis等外部共享存储否则别轻易在一台机器上开多个工作进程。我的建议是IIS应用池中的“最大工作进程数”保持默认的1。如果单进程性能不够优先升级服务器硬件或者在外层用负载均衡挂多台IIS服务器配合共享Session存储才是正确的扩展姿势。5. 部署前必做的目录设计与配置文件管理5.1 部署目录结构与静态资源路径很多UniGUI初学者在部署时只拷贝一个exe或DLL结果页面打开样式全乱、图片不显示。原因很简单UniGUI的客户端运行时依赖一组静态资源文件包括ExtJS库、CSS、图片等。编译后在输出目录下会生成一个uniGUI文件夹或类似名称的目录这个目录就是运行时静态资源的根目录。部署时一定要保持exe/DLL和这个静态资源目录的相对位置不变。我习惯的组织方式是C:\UniGUIApp\ ├─ MyApp.exe // 或MyApp.dll ├─ uniGUI\ // UniGUI运行时静态资源 ├─ config.ini // 自定义配置文件 ├─ logs\ // 日志目录 └─ upload\ // 用户上传文件目录在代码里引用静态资源时尽量不要硬编码绝对路径。用ServerModule自带的一些方法拼接URL比如// 生成一个指向uniGUI下文件的完整URL function GetResUrl(const AFileName: string): string; begin Result : UniServerModule.UniURL(/uniGUI/ AFileName); end;这样无论程序部署在根目录还是虚拟子目录资源路径都不会飘。5.2 关键配置项端口、超时、并发数UniGUI的ServerModule提供了很多和部署强相关的属性其中有几个我每次上线前都会检查一遍。第一个是SessionTimeout默认单位是分钟。内网管理系统我一般设120分钟即2小时无操作自动退出。如果是公网系统建议缩短到30分钟降低Session被劫持的风险。第二个是MaxSessions或类似的并发连接数限制。这个属性决定了同一时刻能容纳多少在线用户。注意它和Web请求并发是两个概念MaxSessions管的是Session数量而真正影响并发能力的是线程池配置。线程池相关配置在ServerModule的Config里比如ThreadPoolSize具体名称和你用的UniGUI版本有关系但基本逻辑一致。第三个是上传文件大小限制。默认可能只有几MB如果业务要传大附件把相关属性调大并且同步检查IIS的maxAllowedContentLength设置否则文件传到一半会被IIS直接切断。把配置项集中放到一个外部配置文件是明智的比如config.ini启动时ServerModule读入。这样你换端口、改连接字符串时不用重新编译程序只需要编辑配置文件。这一点在客户现场尤为好用我经常被要求“改一下数据库地址”如果都写在代码里来回编译能把人逼疯。6. 常见部署问题排查实录6.1 端口被占用与程序崩溃的处置Standalone模式下最常见的启动失败原因就是端口被占用。我自己习惯用下面这串命令确认端口占用情况netstat -ano | findstr 9001 tasklist | findstr PID号查出是哪个进程占用端口后要么换端口要么关掉冲突进程。还有一种隐藏很深的占用来源Windows的HTTP.sys保留了某些URL。比如之前装过其他Web程序调用过netsh http add urlacl就可能把某个端口纳入系统保留列表普通程序无法监听。排查方法netsh http show urlacl如果看到端口被系统保留删除对应保留项即可。程序启动后崩溃先看Windows事件查看器的“应用程序”日志一般能找到异常模块。如果是kernelbase.dll或者ntdll.dll报错十有八九是内存访问越界这类问题往往是代码里使用了流、对象但释放时机不对。ISAPI模式崩溃时事件日志会记录对应工作进程的Event ID 1000或类似信息里面会写明出错的DLL模块名。6.2 DLL未找到与VCL库版本冲突ISAPI模式部署时最经典的问题是“DLL未找到”或者启动后页面500错误。排查顺序我建议这么来第一确认IIS应用池的位数和DLL编译位数是否一致。32位DLL必须配32位池否则加载时直接提示失败。第二确认Delphi运行时库是否齐全。如果你的机器上装了多个版本的Delphi编译时链接的VCL版本可能和部署服务器上的运行库不匹配。最简单粗暴的解决办法是打开项目选项在“运行时包”里选择“不使用运行时包”或“静态链接运行时包”让所有VCL代码直接编进DLL这样就彻底摆脱运行时库版本问题的困扰。第三确认ISAPI扩展的“可执行文件”配置没被覆盖。有时在IIS安装其他组件时ISAPI映射会被重置需要重新配置。UniGUI版本不同编译出的DLL对IIS的ISAPI支持版本也可能有细微差别。如果你下载的是比较新的UniGUI版本建议把IIS版本保持在Windows Server 2012 R2以上老版本Windows Server上的IIS对ISAPI支持不完整容易出莫名奇妙的500错误。6.3 反向代理下的WebSocket与长连接问题用Nginx做反向代理放在UniGUI前面时默认配置下页面能打开但实时推送会失效。原因是UniGUI的Ajax长轮询和WebSocket机制要求代理服务器支持HTTP 1.1的Upgrade头。在Nginx的站点配置里要给对应的location加上升级头配置location / { proxy_pass http://127.0.0.1:9001; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }不加Upgrade头时UniGUI页面虽然能正常显示但类似进度条、在线状态、消息推送这类依赖长连接的功能会间歇性失效。排查时你会在浏览器开发者工具里看到WebSocket请求一直Pending或直接失败。还有一点反向代理后UniGUI拿到的客户端IP都是127.0.0.1这在做操作日志审计时会很难受。在ServerModule里需要解析代理头比如读取X-Real-IP或X-Forwarded-For来还原真实IP。UniGUI的OnBeforeRequest事件里可以处理这个逻辑但我更建议在Nginx层处理好头信息再把经过验证的客户端IP传入应用。7. 我的几个实战经验文章写到最后分享几个我在现场攒下来的土办法不一定都在UniGUI官方文档里但实战非常管用。第一部署前把日志级别调低。UniGUI默认日志级别可能比较啰嗦生产环境长期开着高日志级别会把磁盘塞满。上线时把日志级别调到Error出问题时再临时调高问题解决后记得调回来。第二数据库连接字符串放到外部配置。UniGUI ServerModule可以读取相对路径下的文件启动时拼出完整连接串传给DataModule。我一直坚持这个习惯因为客户换服务器、换数据库IP的事情太常见了有一次凌晨两点被叫起来就是只为了改服务器IP从那以后我所有项目的数据库连接字符串再也没写过死值。第三先用内网IP跑通全部功能再做公网发布。UniGUI在本地跑没问题不代表通过域名访问也没问题。涉及跨域、Cookie、HTTPS证书时本地调试环境很难提前暴露问题。我现在的固定流程是开发机F9调通内网服务器Standalone跑通最后才挂到正式域名下走ISAPI或加Nginx代理。每一步都验证一遍Session、上传、下载、打印这些和浏览器环境强相关的功能。第四给IIS应用池设置定时回收。ISAPI模式下DLL跑时间久了内存会缓慢增长定期回收应用池是好事。但回收会导致所有Session丢失如果系统里有大量在线用户建议把回收时间设到凌晨低峰期并勾选“在固定时间间隔回收”选项。UniGUI的部署选项其实就这么多选好了后面几年维护都舒服选不好每次上线都像是在拆炸弹。我个人的经验是没有万能的部署方案只有对系统规模、运维水平、客户资源最适配的组合。你把这几种模式都在测试环境里跑一遍生产部署时心里就会有底得多。
返回列表