先说明一个容易让人绕进去的点:WebLogic 严格来说不是消息中间件,它是应用服务器。但这个标题又确实有现实依据——WebLogic 内置了相当完整的 JMS 消息服务能力,很多团队就用它来承载队列、订阅等消息场景,省掉一套独立中间件。所以这篇文章我围绕“本地部署 WebLogic 并实现外部访问”这个实操目标展开,先把概念理清楚,然后把安装、域创建、JMS 资源配置、外部连接串写法、常见故障排查这些步骤完整走一遍。适合谁看?一是刚接手老系统、需要临时在本地搭一套 WebLogic 环境的技术人员,二是想把 WebLogic 现有的 JMS 服务暴露给别的机器调用、但不太确定网络配置怎么做的开发者。内容偏落地,不整虚的。
1. 开篇先纠偏:WebLogic 与“消息中间件”的关系
1.1 为什么“消息中间件”这个说法总让人对不上号
做 Java 后端的朋友应该都有印象,ActiveMQ、RabbitMQ、Kafka 这类才是大家口中的消息中间件。而 WebLogic 的官方定位是“应用服务器”,主要干的是部署 Java EE 应用、管理 Web 服务、提供容器能力的活。但 WebLogic 从早期收购来的产品里继承了一套 JMS(Java Message Service)实现,它提供了完整的消息服务:队列、发布订阅、持久化、事务性消息、消息桥接都有。所以“本地部署消息中间件 Weblogic”这个描述并不准确,但在实际业务里你确实可以用它当消息中间件用。
这种情况最常出现在哪里?公司本来就有 WebLogic 部署了若干业务系统,项目组不想再为一个小场景引入 ActiveMQ 或 RabbitMQ,于是直接把 WebLogic 上的 JMS Server 借用过来,配一个连接工厂和队列,应用之间通过 JNDI 拿到连接就能发消息收消息了。同时也能在 WebLogic 管理控制台上看消息堆积、做统计,比单独再搭一套中间件要省事很多。
1.2 它的真实定位:应用服务器里的 JMS 服务
既然要说清楚怎么用,我先把 WebLogic JMS 的组成结构说透。WebLogic JMS 是一个基于 JMS 1.1/2.0 规范的实现,JMS 本身只是一套 Java 接口定义,底层消息的存储、传输、连接管理全部由 WebLogic 自己实现。它允许你定义队列(Queue)和主题(Topic),消息发送方和接收方通过连接工厂(ConnectionFactory)建立连接,再通过目的地(Destination)收发消息。所有这些资源都归属于 JMS Module 统一管理,模块再挂到 JMS Server 上,JMS Server 则是消息真正的运行容器。
用生活化的方式理解:JMS Server 就像一家快递公司的转运中心,消息队列就是一条条分拣滑道,连接工厂就是快递员手里的扫码枪,你往滑道上扔包裹,对面的人从滑道上取包裹,整个过程不关心发件人收件人在哪,只要大家都能连到同一个转运中心就行。WebLogic 在这里扮演的角色就是转运中心,它可以和应用跑在同一台服务器上,也可以独立部署,但资源都是通过管理台来配置和监控。
2. 本地部署实操:从安装包到管理域
2.1 版本与安装包怎么选
本地部署我先说版本选择。现在主流还在用的两代版本:10.3.6 和 12.2.1.x。10.3.6 是老系统的标配,JMS 规范是 1.1,很多旧项目都跑在它上面,今天仍有大量存量系统在维护。12.2.1 是后期较稳定的版本,JMS 支持 2.0,对 JDK 版本要求更高,开发新功能优先考虑它。
安装包方面,Oracle 官方提供的安装文件有几种形态,最常见的是 - generic 的 jar 包。这个 jar 包本身是图形化安装程序,但同时支持静默安装参数。我特别提醒一点:安装 WebLogic 之前一定要先装好对应版本的 JDK,而且是 64 位的,否则后续建域时会遇到各种诡异问题。比如 10.3.6 需要 JDK 1.6 或 1.7,12.2.1 需要 JDK 1.8 以上。各位如果机器上已经装了新版 JDK 17,跑老版本 WebLogic 很容易起不来,倒不是 WebLogic 本身不兼容,而是安装程序在检查 JVM 参数时直接抛异常。
2.2 搭建前的 Java 环境准备
在正式开始前,我建议建一个干净的部署目录结构。以我自己常用的一套为例:
# 假设统一放在 /opt 目录下 /opt/jdk1.8.0_202 /opt/weblogic/Middleware /opt/weblogic/Middleware/wlserver /opt/weblogic/Middleware/user_projects /opt/weblogic/Middleware/user_projects/domainsJDK 安装完成之后,务必做三步检查:java -version 能正确输出版本、JAVA_HOME 环境变量已设置、PATH 中最前面的 java 命令确实指向刚装的 JDK。很多人本地环境装了不止一个 JDK,比如 IntelliJ IDEA 自带了 JBR,命令行里 java -version 输出的是另一个版本,这会导致安装 WebLogic 时版本检测那一步直接失败。所以部署前先执行 which java 看一下路径,如果不对就去调整。
另外要预留磁盘空间。WebLogic 安装包本身不到 2G,但域目录、日志、消息持久化文件会不断增长。尤其是消息服务,队列里如果堆积了大量消息,持久化存储文件会占用额外空间,建议独立给一个数据盘或目录来存放,不要全塞在系统盘。我见过消息堆积把磁盘填满导致整机不可用的案例,所以我把持久化目录单独挂载这件事列为部署必做项。
2.3 创建域的两条路线:向导与 WLST 脚本
安装完成后创建域有两种方式,初学者一般用图形向导,自动化部署用 WLST 脚本。本地部署如果只是自己练手,图形向导最快:安装目录下执行 configure.sh(Windows 上是 configure.cmd),弹出来一个界面,按提示选择域模板,下一步下一步就能出一个可启动的管理服务器。但实际问题是想实现外部访问时,很多配置项都在这个向导里已经固定了监听地址,默认是 localhost,这就意味着外部机器永远连不进来。所以我更建议直接用 WLST 脚本创建域,之后的监听地址、端口、JMS 资源全部用脚本控制,整个过程可重复可审查。
下面这段 WLST 脚本我常用,创建了两个服务器:一个是管理服务器 AdminServer,另一个是受管服务器 ManagedSrv1。管理服务器承载控制台和 JMS 服务,受管服务器可有可无,本地只测消息的话先不建也行,但建了可以之后把应用部署上去:
# 创建域脚本 create_domain.py readTemplate('/opt/weblogic/Middleware/wlserver/common/templates/wls/wls.jar') cd('/Security/baseDomain') set('Name', 'base_domain') set('DomainPassword', 'weblogic2024') cd('/Servers/AdminServer') set('ListenAddress', '0.0.0.0') set('ListenPort', 7001) set('SSLEnabled', 'true') create('ManagedSrv1', 'Server') cd('/Servers/ManagedSrv1') set('ListenAddress', '0.0.0.0') set('ListenPort', 8001) cd('/Machine/localhost') set('ListenAddress', '127.0.0.1') set('ListenPort', 8453) setOption('OverwriteDomain', 'true') writeDomain('/opt/weblogic/Middleware/user_projects/domains/base_domain') closeTemplate() exit()这里最关键的一行是set('ListenAddress', '0.0.0.0')。如果是默认创建的域,AdminServer 的监听地址是 localhost,也就是只绑定本机回环地址,外部请求根本不会进入这个端口。改成 0.0.0.0 的意思是监听本机所有网卡上的对应端口,这样只要网络通路和防火墙是通的,外部机器就能连上。但注意,监听所有网卡也意味着局域网内任何机器都可以尝试连接这个端口,安全上要配合访问控制,不能光靠应用层密码保护。
脚本执行方式是进入 WLST 交互环境:
cd /opt/weblogic/Middleware/wlserver/common/bin ./wlst.sh create_domain.py创建完成后,启动管理服务器:
cd /opt/weblogic/Middleware/user_projects/domains/base_domain/bin ./startWebLogic.sh日志出现Server state changed to ADMIN然后RUNNING就算启动成功,控制台地址默认是 http://服务器IP:7001/console。
3. 消息服务配置:队列、连接工厂与持久化存储
3.1 先建存储还是先建会话
到管理控制台之后,不少人的第一反应是直接创建一个 JMS 队列,发一条消息试试。但正确顺序其实是先把存储和 JMS Server 建好,然后再建队列和连接工厂。原因很简单:队列里的消息必须有个地方放,这个存放位置就是 JMS 持久化存储。如果队列不绑定持久化存储,WebLogic 默认用本地文件存储,消息落到服务器文件系统里,这对验证场景够用,但对生产环境来说是隐患,因为一旦归档文件损坏,消息数据就可能丢。所以我的习惯是先建 File Store,给它一个专门的存储路径,然后再建 JMS Server 并关联到这个 Store。
在管理控制台的路径是:服务 -> 消息服务 -> 存储 -> 新建。配置项里最重要的就是目录路径,我一般会设置成类似/data/weblogic/msgstore这样的独立路径。路径里不要用中文,不要放在安装目录下,也不要放在系统临时目录里,原因很简单:安装目录卸载时会一起被清掉,临时目录可能被系统自动清理。这就等于你上班把重要文件放在公司门口垃圾箱旁边,风险完全不可控。
3.2 JMS Server 与 JMS Module 的关系
存储建好了,接下来创建 JMS Server。可以把它理解为消息的运行容器,每个 JMS Server 可以承载多个队列和主题,但它需要绑定一个 Target,也就是部署到哪个 WebLogic 服务器实例上。在单机本地部署场景里,Target 一般选择 AdminServer 或 ManagedSrv1,端口分别是 7001 和 8001。
真正管理队列和连接工厂的地方是 JMS Module,它是一组消息资源的集合配置。创建模块时需要注意一个细节:模块必须 Target 到 JMS Server 所在的服务器实例上,否则资源虽然建出来了,但没有任何服务器加载它,客户端 JNDI 查询时就会报NameNotFoundException。很多人在本地测试时折腾半天找不到队列,最后发现是模块没有 Target。这是一个非常经典的坑。
模块创建完成后就可以在模块里添加 Connection Factory 和 Queue 了。Connection Factory 相当于消息连接的模板,客户端先通过 JNDI 查到它,再通过它创建真正的连接。Queue 则要配置 JNDI 名称,客户端查队列也是通过这个名称。命名上我建议统一前缀,比如jms/TestCF和jms/TestQueue,方便区分和后续管理。每次配置完资源,都要点控制台左下角的“激活更改”,更改才会真正生效。这个步骤新手特别容易漏,觉得配了资源就能用,结果客户端一直连不上,其实只是没激活。
3.3 一个最小可用的资源清单
综合下来,本地搭建一个最小可用的消息服务,资源清单如下表:
| 资源类型 | 名称示例 | 关键配置项 | 用途 |
|---|---|---|---|
| 服务器实例 | AdminServer | 监听地址 0.0.0.0,端口 7001 | 承载服务 |
| 持久化存储 | MsgFileStore | 目录 /data/weblogic/msgstore | 消息可靠落盘 |
| JMS Server | JMSServer1 | Target 到 AdminServer,关联 MsgFileStore | 消息运行容器 |
| JMS Module | ModuleTest | Target 到 AdminServer | 资源集合 |
| 连接工厂 | jms/TestCF | 默认事务型、消息最大长度等 | 建立连接 |
| 目的地队列 | jms/TestQueue | 关联 jms/TestCF,所属模块 | 发送接收消息 |
这些资源配置完成后,一切看起来就绪,但实际上外部访问能不能成,关键不在这张表里,而在于接下来要讲的网络和监听层面。本地部署要是只在本机测,现在就可以收工,但题目要求是外部访问,那就还要继续往下做。
4. 外部访问配置:监听地址、端口与客户端连接
4.1 为什么外部机器连不上本地 WebLogic
外部访问的失败,十次有九次是监听地址的问题。默认建出来的域,服务器监听地址就是 localhost。外部机器发起连接时,TCP 包是会发到这台服务器的网卡 IP 上,但如果 WebLogic 只监听了回环地址,操作系统的网络栈会把发往网卡 IP 的请求直接丢掉,应用层根本收不到。这个现象非常迷惑,因为你在本机测试一切正常,换台机器就连接超时,不少人还以为是防火墙拦截。
解决办法我在建域脚本里已经埋进去了:把 AdminServer 的监听地址从 localhost 改成 0.0.0.0,也就是监听本机所有网卡。如果机器有多块网卡,比如一个是内网网段、一个连接公网,而你只想让某块网卡对外提供服务,那就把监听地址直接设成那个网卡的 IP,而不是 0.0.0.0,这样更安全更收敛。需要注意,修改监听地址之后一定要重启管理服务器才生效。
判断当前监听地址是什么,可以直接看域目录里的 config.xml,打开后搜<server>标签,找 AdminServer 配置段落里的 ListenAddress。这个文件是域的配置中心,很多网络相关设置都写在这里,改错了还能手动改回来。不过我更推荐用控制台改:环境 -> 服务器 -> AdminServer -> 监听地址,改完保存并激活,然后重启。图形界面不容易改坏,适合不熟悉的同学。
4.2 端口规划:7001 是管理端口,不是万能入口
WebLogic 默认的监听端口很多,控制台和 JMS 走的是 7001 普通端口,7002 是 SSL 端口。如果你建了受管服务器,还会有一个 8001 端口之类。外部客户端连 JMS 服务时,用的是 t3 协议,默认也是走 7001。管理控制台 Web 界面走 HTTP,用的也是 7001。所以从功能角度讲,至少要把 7001 这个 TCP 端口在防火墙里放开。
但这里我要说一个安全建议:如果只是给外部应用程序调用 JMS,不要开放 7001 的管理控制台端口。因为 7001 同时承载了管理控制台,暴露出去相当于把整个服务器管理入口开放给了网络上的其他人。更稳妥的做法是单独建一个受管服务器,比如 ManagedSrv_External,把 JMS Server Target 到它上面,设置一个专门的端口如 8002,防火墙只放开这个端口。管理控制台所在的 AdminServer 只允许内网访问,或者干脆不绑定外部网卡。
端口映射或者网络策略方面,按环境的实际规则来配置即可,原则是最小权限:外部到内部只放行明确要用的端口,不要把整段端口都打开。如果公司内部网络有防火墙规则,通常是提交一个变更申请,写明源网段、目的端口和协议就可以。不要自己直接在服务器上把防火墙关了来图省事,那样代价会很高。
4.3 外部客户端连接串怎么写
当服务端监听地址、端口、防火墙都准备妥当后,外部客户端要连上来,核心就靠两个东西:JNDI 的环境参数和用户认证。我贴一段常见的 JMS 客户端连接代码,发送一条文本消息到队列里:
import javax.jms.*; import javax.naming.*; import java.util.Hashtable; public class JmsSendTest { public static void main(String[] args) throws Exception { Hashtable<String, String> env = new Hashtable<>(); env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory"); env.put(Context.PROVIDER_URL, "t3://192.168.1.100:7001"); env.put(Context.SECURITY_PRINCIPAL, "weblogic"); env.put(Context.SECURITY_CREDENTIALS, "你的密码"); Context ctx = new InitialContext(env); ConnectionFactory factory = (ConnectionFactory) ctx.lookup("jms/TestCF"); Queue queue = (Queue) ctx.lookup("jms/TestQueue"); try (JMSContext jmsContext = factory.createContext()) { jmsContext.createProducer().send(queue, "hello from remote client"); System.out.println("发送成功"); } ctx.close(); } }这里最需要注意的是 PROVIDER_URL 的写法,一定要用t3://开头,IP 换成这台服务器的实际 IP,而不是 localhost。如果服务端启用了 SSL,那协议要改成t3s://,并且客户端的信任库要导入服务端证书。很多人在本地测试一直用 localhost 没出过问题,搬到外部机器上就忘改这里。
4.4 外部访问验证清单
我把外部访问验证按顺序列了一下,按照这个顺序排查,基本能定位 90% 的问题:
- 从外部机器执行
telnet 服务器IP 7001,检查端口是否通。这一步能区分是网络不通还是应用层问题。 - 在外部机器上测试
t3://服务器IP:7001能否建立 JNDI 连接。 - 用管理控制台查看服务器实例状态,确认是 RUNNING。
- 确认 JMS Module 已经 Target 到对外提供服务的服务器上。
- 确认连接工厂和队列的 JNDI 名称没有写错。
- 确认客户端机器的 JDK 和 WebLogic 客户端库版本与服务端匹配。
这套清单看起来简单,但每次排查我都是从头走一遍,因为问题往往不是你以为的那一层。有一次我排查了半天发现是外部机器和服务器之间隔了防火墙,端口没放开,telnet 直接卡住,跟 WebLogic 本身一点关系都没有。
5. 高频故障清单与排查思路
5.1 外部连接超时,一定是网络问题吗
外部客户端连接 WebLogic JMS 超时,表象是网络不通,但原因可能是这几个方向。最常见的是监听地址没改,服务器还在监听 localhost,连接请求被内核丢弃。第二个常见原因是防火墙拦截,既要看本机防火墙,也要看网络边界防火墙。第三个原因是服务端负载已经很高,等待队列满导致新连接被挂起,这在消息大量堆积时会出现。第四个原因是客户端连接的端口和实际监听端口不一致,比如服务端改了端口但客户端还在连旧端口。
排查顺序建议先 telnet 再检查监听端口监听情况。在 Linux 服务器上执行:
netstat -anp | grep 7001如果看到 127.0.0.1:7001,说明监听地址还是 localhost,问题就在这。如果看到 0.0.0.0:7001 或者是具体 IP:7001,那网络层是通的,再查防火墙和客户端代码。
5.2 客户端 JNDI 查询报 NameNotFoundException
报错信息里如果出现NameNotFoundException,说明客户端已经成功连上了服务端,但查不到指定的资源名称。这种问题的原因基本就是资源没创建成功、模块没 Target、或者 JNDI 名称写错。我遇到过一种情况:在管理台上看到队列名称是TestQueue,但客户端查的是jms/TestQueue,两者不同,自然查不到。
从管理控制台查实际 JNDI 名称的方法:环境 -> 服务器 -> 选择 AdminServer -> 查看 JNDI 树选项卡,能看到当前服务器上已经注册的所有 JNDI 名称。队列和连接工厂应该对应显示。如果 JNDI 树里看不到资源,说明资源虽然创建了但没生效到这台服务器上,回过去检查 JMS Module 的 Target 配置。
5.3 JMS 客户端版本和服务端版本不匹配
WebLogic JMS 的客户端库适配没有你想象的那么宽松。服务端如果是 10.3.6,客户端最好用同版本的 weblogic.jar。服务端 12.2.1,客户端也尽量一致。跨大版本偶尔能连,但协议层可能出现奇怪的行为,比如连接建立成功但发消息报错。最简单的方案是在客户端工程里引入与服务端版本一致的 weblogic.jar,避免用较新版本去连老服务端。
这里要注意的一个点是,weblogic.jar 体积很大,而且里面包含一些依赖其他库的类,比如 JMS 客户端运行时会用到 wlclient.jar、wljmxclient.jar 这些。偷懒方案是直接把整个 weblogic.jar 放到客户端的 classpath 里,多数场景都能跑起来。如果想做精简,可以只放必要的几个 jar,但初学者我不建议精简,白白增加排查成本。
5.4 高频问题速查表
| 现象 | 可能原因 | 优先排查动作 |
|---|---|---|
| 外部机器 telnet 端口不通 | 防火墙拦截 / 监听地址未改 | netstat 查看监听地址,检查防火墙 |
| JNDI 查询 NameNotFound | 模块未 Target / JNDI 名称不一致 | 看服务器 JNDI 树,核实名称 |
| 连接建立后发消息失败 | 客户端版本不一致 / 队列无持久化空间 | 核对客户端 jar 版本,查看存储目录 |
| 消息堆积后发送越来越慢 | 存储目录空间不足 / 临时消息堆积 | 查看磁盘空间和管理台消息计数 |
| 控制台访问速度极慢 | 内存不足 / 日志量过大 | 检查 GC 日志和 access.log 大小 |
这张表是日常运维中最常见的几个场景,碰到问题先按表里的方向查,大多数时候能很快定位。排查 WebLogic 问题的思路跟排查普通 Java 应用一样的:先分层次,网络层、服务层、应用层一层层剥离,不要一开始就陷到细节里。
6. 关于这套方案的几点个人建议
最后聊几个我做这类部署时积累下来的习惯,不一定写进文档但很实用。
第一点,我从不把持久化存储跟安装目录放一起。安装目录是程序文件,重装、升级、打补丁都可能是针对它的操作,消息存储放这里容易在操作过程中被误删。单独挂一个数据目录,备份也方便,只要把这块目录定期备份,消息就能找回来。
第二点,监听地址尽可能用具体 IP 而不是 0.0.0.0。虽然 0.0.0.0 配置简单,但它把所有网卡都暴露出来了。如果你这台机器既连内网又连其他网络,0.0.0.0 会让不在预期范围内的网络也能尝试访问端口,增大被扫描的风险。用一个固定 IP 或至少用控制台的访问列表来收敛,是更稳妥的做法。
第三点,给消息服务的 JNDI 名称做一套命名规范。比如队列都用jms/业务名/QueueName,连接工厂用jms/连接工厂名,时间长了资源多了之后,没有规范就是灾难。一个队列叫TestQueue,另一个叫MyQueue2,三个月后没人记得哪个是干什么的。
这套“本地部署 WebLogic 并实现外部访问”的流程,本质上就是把一个默认只服务于本机的应用服务器,改造成一个可被网络内其他应用调用的消息服务节点。整个过程不算复杂,但每一步都要做对:概念上分清应用服务器和 JMS 服务的关系,部署上把监听地址和端口规划好,资源上把存储和队列建对,最后客户端连接串和服务端配置对齐。如果你是在已有 WebLogic 环境上做这件事,那更简单,只需要补建 JMS 资源、调监听地址、改防火墙三个动作就行,按文章里的排查表走一遍,外部访问很快就能通。