资讯动态

GeoServer集群搭建实战:基于JMS配置同步与Nginx负载均衡

发布时间:2026/10/17 2:04:52 来源:尧图企业网站定制
GeoServer集群这个词第一次接触的人容易想得太简单装两个GeoServer实例前面挂一个Nginx完事。真动手做的时候才会发现麻烦全藏在“节点间状态同步”这四个字后面。我前阵子正好在一套纯虚拟机环境里把这件事完整跑通了一遍包括集群搭建、配置同步、负载均衡和性能压测本文就把整个过程从头到尾整理出来给准备搞GIS服务高可用的同学一份可以直接照抄的作业。GeoServer作为开源GIS服务器单机部署其实很轻松通常跑个WMS、发布切片也没压力。但单机有两个天然瓶颈一是硬件扩展总有上限CPU和内存加到一定程度就加不动了二是没有故障转移能力进程一挂整个地图服务全断。集群的目标就是解决这两个问题同时把吞吐能力从“垂直扩展”变成“水平扩展”。1. 项目目标和整体方案选型1.1 为什么需要GeoServer集群先聊一个底层问题GeoServer的瓶颈到底在哪儿。GeoServer本身是一套Java Web应用默认跑在Servlet容器里。每次WMS请求进来它会经历参数解析、图层匹配、样式解析、空间数据读取、图形渲染、图片编码这一整条链路。其中最吃CPU的是渲染最吃内存的是缓存和并发请求堆积最吃磁盘I/O的是数据文件读取。单机环境下这些资源都是共享的。缓存命中率低时每次都要去读Shapefile或者数据库响应时间自然被拖长并发一涨Tomcat线程数飙上去JVM老年代占用率快速上升GC频繁响应时间开始抖动最后变成端口都连不上。所以单机的天花板不是某个参数能调出来的而是整体资源上限决定的。集群要做的事情就是让多个节点分担请求同时保证它们对外的配置和数据是一致的用户无论打到哪个节点拿到的结果都应该一样。虚拟机环境下做这件事比物理机更划算。可以随时快照、随时销毁重建三台虚拟机的资源开销也不大完全够把集群的机制和坑都摸清楚。1.2 三种常见集群方案的对比GeoServer集群网上方案很多但总结下来基本就三条路线。第一种是共享数据目录。把GeoServer的数据目录放到NFS或者GlusterFS共享存储上多个节点读同一份配置。这种方式配置一致性很好不用管节点间同步但问题也很明显文件锁的竞争、共享存储的I/O延迟、单点存储故障都会直接影响所有节点。虚拟机环境里再单独搞一套NFS先不说性能光权限就够折腾。第二种是本地数据目录加JMS同步。GeoServer官方提供了JMS Cluster插件节点启动时连接消息总线某个节点上做了配置变更增删图层、修改样式、调整工作区其他节点会通过JMS收到事件消息自动同步到本地数据目录。这种方式不依赖共享存储节点之间完全独立非常适合虚拟机环境模拟。缺点是需要额外维护一个ActiveMQ或ActiveMQ Artemis消息服务而且底层数据文件本身不会被自动同步需要单独处理。第三种是容器化编排。把GeoServer打成Docker镜像再用专门的编排平台管理调度。这种方式运维最规范扩展也最方便但用在虚拟机环境里相当于又叠了一层复杂度学习成本高调试起来也绕。如果你想先理解GeoServer集群本身的原理我不建议一上来就上容器。综合对比后我选了第二种方案每个节点用自己的本地数据目录通过JMS做配置同步数据文件用脚本同步。这套方案在纯虚拟机环境里最容易复现也能把集群的原理彻底讲清楚。1.3 本次采用的架构拓扑整个集群由三台虚拟机组成角色分配如下表所示。虚拟机角色建议配置主要服务负载均衡与消息节点1核2GNginx、ActiveMQ ArtemisGeoServer节点12核4GTomcat、GeoServerGeoServer节点22核4GTomcat、GeoServer有人会问消息服务为什么和负载均衡放一起而不是单独拆一台因为JMS消息量本身不大配置变更在发布阶段才产生运行阶段几乎没什么流量和Nginx共存在虚拟机环境里完全够用这样还能省下一台虚拟机的资源。如果生产环境要求隔离再把MQ拆出去就行。请求走向是这样的用户请求先打到NginxNginx通过配置好的upstream把请求轮询分发到node1或node2。两个GeoServer节点连接同一个Artemis消息服务。当在任意一个节点上修改配置或发布新图层时JMS会把变更消息广播给另一个节点本地数据目录自动同步。数据文件比如Shapefile则通过独立脚本从node1同步到node2。后面的所有操作都围绕这个拓扑展开。2. 虚拟机与环境准备工作2.1 虚拟机规划与资源分配先说虚拟机本身。我用的是常见的桌面级虚拟化方案物理宿主机的CPU和内存按比例划分给三台虚拟机。操作系统统一用Linux发行版既然做服务器实验尽量别用带图形桌面的版本纯命令行更节省资源。网络模式这里要提醒一下虚拟机之间尽量采用同一个虚拟网络确保三台机器可以互ping。如果用NAT模式虚拟机之间能互通但宿主机和虚拟机之间的关系容易乱Nginx做反向代理时还得注意端口转发没必要给自己加戏。建议把三台虚拟机都设置在同一网段例如192.168.56.0/24固定IP。磁盘分配也很重要。系统盘给30G就够但GeoServer的数据目录最好单独规划一个分区或者至少用一个独立的目录不要把数据堆在系统盘里。我习惯把GeoServer的数据目录放在/opt/geoserver_data如果后面要挂额外数据盘直接mount到/opt/geoserver_data下面迁移和备份都方便。资源分配上node1和node2虽然只有2核4G但要注意Java进程默认的堆内存可能不太合理后面一定要手动调JVM参数。如果宿主机本身内存不大三台虚拟机加起来很容易超配建议根据物理机实际内存合理分配不要硬塞。2.2 安装JDK与部署GeoServerGeoServer长期稳定版本我直接用最新稳定版JDK建议装11或者172.24以上版本对JDK17的支持没问题JDK8已经不推荐用于新版GeoServer了。我把GeoServer以war包形式部署到外置Tomcat而不是直接下载GeoServer的二进制发行包。因为二进制发行包自带Jetty多节点管理起来反而麻烦换成外置Tomcat后JVM参数、线程池、日志文件都集中在同一个容器里后续调优更方便。安装步骤大致是这样# 安装JDK yum install -y java-11-openjdk java-11-openjdk-devel # 下载解压Tomcat tar -zxvf apache-tomcat-9.0.x.tar.gz -C /opt mv /opt/apache-tomcat-9.0.x /opt/tomcat # 下载GeoServer war包 wget https://.../geoserver-2.24.x-war.zip unzip geoserver-2.24.x-war.zip -d /tmp/geoserver-war cp /tmp/geoserver-war/geoserver.war /opt/tomcat/webapps/两个节点都按同样步骤操作。所以分别是node1和node2的/opt/tomcat/webapps/geoserver.war。启动之前先设置数据目录环境变量。GeoServer默认会把数据目录放在用户目录下写成绝对路径更稳妥cat /etc/profile.d/geoserver.sh EOF export GEOSERVER_DATA_DIR/opt/geoserver_data export JAVA_HOME/usr/lib/jvm/java-11-openjdk EOF source /etc/profile.d/geoserver.sh然后启动Tomcat第一次启动会自动初始化数据目录生成workspaces、styles、layers、logs等文件夹。访问http://192.168.56.11:8080/geoserver/web看到GeoServer登录页就说明部署成功。默认管理员账号密码是admin/geoserver第一次登录后建议改掉。2.3 发布压测用的测试图层集群配置做完再发布图层容易连着节点一起重启所以先把测试图层准备好。我用的是一份行政区划边界的Shapefile数据量不大字段也不复杂非常适合做WMS压测。在GeoServer管理界面新建工作区demo创建新的数据存储类型选Shapefile把文件路径指到数据目录下的某个位置。然后发布图层命名为demo:boundary。发布完成后先单独在node1上验证一下请求地址能不能正常出图。用浏览器或curl打开类似下面的地址curl http://192.168.56.11:8080/geoserver/wms?serviceWMSversion1.1.1requestGetMaplayersdemo:boundarybbox-180,-90,180,90width800height600srsEPSG:4326stylesformatimage/png能返回一张PNG图片说明发布成功。后续压测就用这个URL把它复制到JMeter里就能直接跑。3. 集群核心配置与实操细节3.1 用ActiveMQ Artemis打通配置同步GeoServer官方集群依赖消息中间件来广播配置变更事件。官方插件支持的broker协议是OpenWireActiveMQ和ActiveMQ Artemis都兼容我这里选了ActiveMQ Artemis性能和资源占用表现更好。在负载均衡那台虚拟机上下载Artemis然后创建一个broker实例tar -zxvf apache-artemis-2.x.x-bin.tar.gz -C /opt cd /opt/apache-artemis-2.x.x/bin ./artemis create /opt/artemis-broker --user geoserver --password geoserver --allow-anonymous创建--allow-anonymous是为了测试阶段方便生产环境不要开匿名认证。启动broker/opt/artemis-broker/bin/artemis run默认监听端口就是61616。可以在node1上测一下连通性curl telnet://192.168.56.10:61616能通说明broker就绪。接下来在两个GeoServer节点上安装JMS集群插件。从官方下载geoserver-2.24.x-jms-cluster-plugin.zip解压后把里面的gs-jms-*.jar文件全部复制到/opt/tomcat/webapps/geoserver/WEB-INF/lib目录下。注意两个节点都要拷贝缺一个就有一个节点收不到消息。然后编辑/opt/tomcat/webapps/geoserver/WEB-INF/classes/cluster.properties。这个文件需要手动创建内容参考如下Cluster.connection.enabledtrue Cluster.connection.embeddedBrokerfalse Cluster.connection.brokerURLtcp://192.168.56.10:61616 Cluster.connection.usergeoserver Cluster.connection.passwordgeoserver Cluster.connection.send.interval5000 Cluster.connection.max.retries10 Cluster.session.connection.namenode1 Cluster.node.config.connection.enabledtrue重点说几个参数。Cluster.session.connection.name必须唯一node1和node2要设置成不同的值这是消息总线用来识别消息来源的。brokerURL指向MQ地址。Cluster.node.config.connection.enabled开启后节点启动时会自动向其他节点请求最新的配置状态确保新加入的节点能快速补齐配置。配置完重启两个Tomcat然后在node1的管理界面里随便新建一个空工作区回到node2刷新如果能看到这个新建的工作区说明JMS配置同步已经生效。这一步是后面所有流程的地基必须验证通过。3.2 数据目录一致性与数据文件同步JMS自动同步的是GeoServer的配置类文件比如workspaces、styles、layers中的XML配置但底层数据文件不会通过JMS自动传。也就是说node1上发布图层时JMS会把“demo:boundary”的图层定义同步给node2但如果node2上根本没有对应的Shapefile文件出图时就会报找不到数据源。解决办法有两个。简单粗暴的方式是把数据文件直接放在两个节点各自的数据目录里然后用rsync定期同步。另一种方式是把数据文件放到共享存储两个节点通过相同路径访问。虚拟机环境里我先用rsync方案。同步脚本核心就一行命令rsync -av --delete /opt/geoserver_data/data/ geoserver192.168.56.12:/opt/geoserver_data/data/这里建议只同步实际的数据文件目录不要把整个/opt/geoserver_data都同步。因为workspaces下的配置XML已经由JMS管了如果再用rsync去覆盖两个同步机制互相打架会出现配置文件和JMS消息状态不一致的情况。数据文件路径也要一致例如node1和node2都把数据文件放在/opt/geoserver_data/data下这样图层配置里引用的绝对路在两个节点上才都能读到。实际操作中还有一个容易踩的坑rsync默认会保留文件权限如果GeoServer运行用户不同同步过去的文件权限不对节点就读不了。建议两个节点的Tomcat和GeoServer都用同一个系统用户运行或者rsync时加上权限覆盖参数确保文件可读。3.3 Nginx负载均衡配置与调优Nginx配置是集群流量的门面配置不当会出现各种奇怪问题。我的配置文件核心内容如下upstream geoserver_cluster { server 192.168.56.11:8080 max_fails3 fail_timeout30s; server 192.168.56.12:8080 max_fails3 fail_timeout30s; } server { listen 80; server_name gis.lab.local; client_max_body_size 100m; location /geoserver { proxy_pass http://geoserver_cluster/geoserver; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 10s; proxy_read_timeout 60s; proxy_send_timeout 60s; } }注意我用的proxy_pass http://geoserver_cluster/geoserver;后面的/geoserver路径和location里的路径一致这是因为GeoServer的Web应用上下文路径就是/geoserver请求经过Nginx转发时要把路径保留住。如果写成proxy_pass http://geoserver_cluster;不带路径访问/geoserver/wms时会变成转发到后端的/wms直接404。max_fails3和fail_timeout30s这两个参数很关键。某个GeoServer节点如果拒绝连接或者请求超时Nginx会在30秒内把节点标记为不可用不再往这个节点分发请求避免大量请求打到半死状态的节点上。但要注意这只能探活TCP连接不能完全反映GeoServer应用层的健康状态更严谨的做法是配一个主动健康检查脚本检测到/geoserver/web接口无响应再自动摘除节点。在虚拟机环境里压测Nginx的worker_processes auto和worker_connections 4096我用的是默认值基本够用。如果压测并发超过几百连接再考虑调大worker_rlimit_nofile和worker_connections。这里还有一个管理界面会话的问题。默认轮询负载均衡会把管理界面请求随机分发到两个节点登录状态只保存在其中一个Tomcat的Session里刷新一下可能就跳到另一个节点导致登录失效。解决方法是给管理界面单独走ip_hash或者干脆测试时直接访问node1的IP执行管理操作对外地图服务才走均衡负载。我采用的是后者管理操作固定node1地图服务全走Nginx逻辑清晰也省心。3.4 JVM与Tomcat线程池参数调整集群节点默认使用Tomcat启动脚本里的参数内存和线程数都没优化过。压测前必须把两个节点的参数调一遍。修改/opt/tomcat/bin/setenv.sh文件不存在就新建写入CATALINA_OPTS-Xms2g -Xmx4g -XX:MaxMetaspaceSize512m -Djava.awt.headlesstrue-Djava.awt.headlesstrue必须加上GeoServer渲染地图图片依赖Java图形处理库在无显示器的服务端环境里不加这个参数图片编码时会报HeadlessException。内存分配不能拍脑袋。node1和node2虚拟机都是4G内存Tomcat进程给2G到4G堆内存剩下留给操作系统缓存和一些Tomcat自身的非堆内存。-Xms和-Xmx设置成一样避免运行过程中JVM频繁扩展堆导致的性能波动。生产环境如果物理内存足够可以再给大一点但虚拟机上建议保守一些给操作系统留足余量否则Swap一启动性能反而崩掉。Tomcat的线程池配置在conf/server.xml里这个参数很容易被忽略。默认Connector的maxThreads只有200压测并发上去后请求会在Tomcat线程池排队响应时间迅速恶化。我调整成Connector port8080 protocolHTTP/1.1 connectionTimeout20000 maxThreads400 minSpareThreads50 acceptCount200 /不要无脑把maxThreads调成几千线程太多会带来上下文切换开销4G内存的虚拟机根本撑不住。400是一个相对平衡的值。压测时观察线程池如果没有打满就保持现状如果打满且CPU没到瓶颈再逐步往上加。4. 性能测试方案设计与结果解读4.1 压测请求构造与JMeter脚本性能测试工具我这里用JMeter原因是跨平台、录制脚本方便、聚合报告直观。如果你更习惯其他压测工具原理是一样的关键是请求参数和测试场景要设计对。压测请求就用前面验证过的那个GetMap地址。把图层和数据范围固定住URL完整写成http://192.168.56.20/geoserver/wms?serviceWMSversion1.1.1requestGetMaplayersdemo:boundarybbox-180,-90,180,90width800height600srsEPSG:4326stylesformatimage/png这里192.168.56.20是Nginx所在虚拟机的IP。压测时JMeter作为客户端只访问Nginx地址不要直接访问node1或node2。这样测出来的才是整个集群对外提供的服务能力。JMeter里我这样设计线程组预热轮次10个并发循环5次让GeoServer的数据缓存和图片编码器先热起来。正式压测分别用10、50、100、200并发每个并发级别持续3分钟Ramp-Up时间设5秒。每个并发级别跑完之后停一下记录聚合报告里的TPS、平均响应时间、错误率再跑下一个级别。中间间隔足够长避免上一轮缓存残留影响下一轮数据。压测机的位置也有讲究。我最初把JMeter和Nginx放在同一台虚拟机里跑结果CPU被JMeter占用了一大截压测结果明显偏低。后来把JMeter换到独立的物理宿主机上跑数据才恢复可信。如果你的实验环境没有独立机器至少也要保证压测机不承担GeoServer或Nginx的负载。4.2 单机基线数据与集群压测数据性能测试不能直接跑集群必须先测一个单机基线。方法很简单把Nginx的upstream临时改成只指向node1或者直接用node1的IP地址访问GeoServer其余参数不变跑同样的并发级别。我实测的结果大概如下表数据仅供趋势分析参考不同机器配置会有差异并发数单机TPS单机平均响应(ms)集群TPS集群平均响应(ms)集群错误率集群TPS提升比例10248246780%92%50582101031700%78%100843901423300.3%69%200976201685102.8%73%从数据可以读出几个信息。低并发时集群提升接近两倍因为两个节点都在低负载下干活没有明显的资源冲突。并发到了100以上集群TPS提升幅度降到70%左右而且出现少量错误率这说明瓶颈开始从“节点CPU”往“网络连接、JVM GC、文件I/O共享”转移。虚拟机环境下两个节点跑在同一台宿主机上CPU和磁盘I/O是共享的压测到高并发时宿主机本身的资源争抢也会限制扩展效果。响应时间方面集群平均响应比单机低但并没有按照TPS的提升比例同幅度下降原因是GeoServer的缓存和数据读取延迟占比高请求分发到两个节点后单请求本身的渲染时间并没有缩短太多吞吐量提升主要靠并行度。4.3 压测结果关键指标解读与调优方向只看TPS和平均响应时间还不够。JMeter聚合报告里的90% Line、95% Line、99% Line才是判断系统稳定性的关键。如果平均响应时间很低但99% Line很高说明有少量请求特别慢这通常是垃圾回收停顿、数据源连接池等待或者文件读取争用造成的。我在压测中还重点看了两个系统指标一个是每个GeoServer节点的CPU使用率另一个是JVM的GC日志。top里看到node1的CPU打满、node2只有40%左右说明Nginx轮询是均匀分配的但节点之间的性能可能不均比如其中一个节点的数据目录IO较慢导致对单次请求处理更慢。如果某个节点长时间比另一个节点空闲可以考虑Nginx的weight权重分配让性能强的节点多承担一些请求。GC日志的排查方向也很明确。在CATALINA_OPTS里加上-Xlog:gc:/opt/tomcat/logs/gc.log输出GC日志压测完成后打开gc.log看Full GC次数和停顿时间。如果Full GC频繁说明堆内存不足或者大对象过多先考虑加内存再考虑调整GeoServer的图片编码缓存配置。不要小看GCGeoServer单节点在渲染大图时会产生大量的中间对象GC停顿直接表现为响应时间毛刺。还有一个容易忽略的指标是错误率的分布。集群错误率不是均匀出现的我遇到的情况是高并发下node1报错多node2正常后来发现是node1的数据目录所在磁盘空间不足临时文件无法写入。排查错误时一定要把JMeter的响应数据保存下来按返回内容归类不要只看一个Error率百分比。5. 常见问题和排障实录5.1 节点间配置不一致导致404最典型的故障表现是通过Nginx访问图层第一次能出图第二次报404或者ServiceException。原因是Nginx把两次请求分发到了不同节点而第二次命的节点没有这个图层配置或数据文件。处理思路分三步。第一步直接分别访问node1和node2的GeoServer服务地址绕过Nginx看是不是有一个节点本身就报错。第二步登录GeoServer管理界面查看两个节点的图层列表中是否有demo:boundary。第三步看日志如果节点A有这个图层、节点B没有基本就是JMS同步断了。我遇到过一次JMS消息丢失的情况查看cluster.properties发现Cluster.connection.enabled被误写成了false。还有就是Artemis服务重启后GeoServer节点没有自动重连导致后续的配置变更没有同步。解决办法是在clust.properties里把Cluster.connection.max.retries调大同时建议写一个守护脚本检测到节点与MQ的TCP连接断开就自动重启Tomcat简单粗暴但有效。5.2 管理界面会话闪断和登录失效这个问题在集群里太常见了。部署完集群后我用Nginx的地址登录GeoServer管理界面输入账号密码跳转后又被踢回登录页反复几次都是这样。原因前面已经提过Nginx默认轮询导致两个Tomcat的Session不一致。我在实验环境里直接把管理操作固定到node1的IP服务访问走Nginx绕开了这个麻烦。但如果你的场景要求管理界面也必须通过Nginx访问可以在server块里单独加一个location /geoserver/web这个路径使用ip_hash策略或者直接只代理静态资源和JSON接口管理页面入口独立配置。更工程化的方案是配置Tomcat的DeltaManager实现Session集群同步但多节点Session复制会带来额外的网络开销和复杂度虚拟机实验环境里不太划算。5.3 内存溢出和连接池耗尽高并发压测时日志里出现java.lang.OutOfMemoryError: Java heap space这个现象很直接。粗暴的解决办法是继续调大-Xmx但要先想清楚内存为什么暴涨。GeoServer渲染800x600的PNG图每个请求都会创建图像缓冲区和渲染上下文这些对象在压测时会像滚雪球一样堆积起来。我遇到的问题是为了压出效果把请求的图片尺寸调到了1024x768结果JVM堆直接被图片缓冲区占满。解决办法除了加内存还可以在GeoServer管理界面的“全局设置”里调整内存缓存上限比如把Tile Cache的内存缓存容量调小。数据源连接池也要检查如果GeoServer连接的是PostgreSQL数据库连接池默认连接数有限高并发下数据库连接排队会拖垮整个请求链路。5.4 压测结果远远低于预期的排查顺序如果你压测出来集群TPS和单机差不多甚至更差先别怀疑集群方案本身大概率是配置出了问题。我建议按这个顺序排查。第一步看网络链路。JMeter直接压测Nginx再用Nginx压测后端节点对比各环节延迟确认Nginx本身没有成为瓶颈。第二步看系统负载。在压测过程中用top实时观察三台虚拟机的CPU、内存、I/O占用情况定位是哪台机器的资源被打满。第三步看GC日志。如果两个节点CPU都不高但TPS上不去重点看GC停顿和线程阻塞情况。第四步看错误响应内容。把JMeter的聚合报告和查看结果树结合起来看出错请求的具体响应体判断是GeoServer应用抛异常还是Nginx返回超时。虚拟机环境下还有一个特别容易忽略的点三台虚拟机如果是运行在同一块物理磁盘上的高并发压测时共享磁盘I/O会互相拖累。我在测试时曾觉得集群TPS提升不大后来把node1和node2的虚拟磁盘放到两块不同的物理硬盘上集群扩展性能立刻好了不少。这个排查顺序基本能把90%的“假性性能问题”排除掉。最后说点个人体会。GeoServer集群里真正麻烦的不是把多个节点跑起来而是让它们在配置、数据、缓存三个维度上保持一致。配置同步有JMS数据同步可以靠rsync或共享存储分布式缓存则要额外引入独立的瓦片缓存服务才能彻底解决。虚拟机环境做这套实验能用很低的成本把所有坑都踩一遍但压测数据受宿主机共享资源影响比较大想严谨评估生产容量最好用物理机或云主机环境再复测一轮。整套流程跑通之后再去看容器化方案或者其他高可用架构你会发现底层的思路都是互通的。

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

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

免费获取报价 →
↑