1.事件背景
某客户今年新上线一套业务系统,C/S架构,客户端(C,Client)是使用Java开发的GUI程序,内部主要是EJB Client,服务端(S,Server)是部署在一个12节点儿的WebLogic 8.1集群中的EJB服务。
该系统上线后,经常发生宕机事件,前期经过我公司中间件工程师和应用开发人员的不断修缮,宕机频率有所降低。近期,接到开发人员反馈,当WebLogic集群中1个Server因Java虚拟机内存溢出或CPU高时,整个系统几乎瘫痪,无法登录并进行业务操作。后经过我公司二线工程师的诊断,发现部署在WebLogic集群环境的EJB服务配置上存在一定的问题,WebLogic集群对EJB的负载机制影响了正常Server上的请求。为解决此问题,我们经过多番测试验证,决定放弃WebLogic集群,服务端改单机方式部署EJB服务,同时,我们决定使用F5 BIG-IP对客户端的连接做负载均衡(也可以使用EJB客户端填写12个Server IP、Port的方式进行负载均衡,但效果没有F5 BIG-IP等硬件负载均衡器好)。
随后,在测试环境测试上面的方案时,客户端抛出下面的错误信息:
javax.naming.CommunicationException [Root exception is java.net.ConnectException:
t3://192.168.136.220:8080: Bootstrap to: 192.168.136.220/192.168.136.220:8080'
over: 't3' got an error or timed out]
服务端抛出下面的错误信息:
<2013-12-18 下午03时23分23秒 CST> <Error> <RJVM> <BEA-000572>
<The server rejected a connection attempt JVMMessage from: '-821451925402538226
C:192.168.135.218R:-4468386810750896225S:192.168.136.219:[7005,7005,-1,-1,7005,-1,-1,0,0]:
cluster_domain:mServer2' to: '0S:192.168.136.220:[8080,-1,-1,-1,-1,-1,-1,-1,-1]
' cmd: 'CMD_IDENTIFY_REQUEST', QOS: '101', responseId: '0'
, invokableId: '0', flags: 'JVMIDs Sent, TX Context Not Sent'
, abbrev offset: '229' probably due to an incorrect firewall configuration
or admin command.>
经过查询MOS文档库,得知其为WebLogic Server的一个Bug:
Connecting to WebLogic Server (WLS) Through a Load Balancer to a Different Port than
the Load Balancer Port Gives a javax.naming.CommunicationException (Doc ID 860340.1)
当负载均衡器或者代理Server的端口与后端WebLogic Server监听端口不一致时,后端的WebLogic就会拒绝t3协议的连接并抛出端口不一致(portMatches = false)的错误提示信息。
解决方案就是在后端部署EJB服务的所有WebLogic Server启动脚本命令行添加”-Dweblogic.rjvm.enableprotocolswitch=true”参数,此Bug在WLS 9.2 mp2版本开始修复,WebLogic 9.0, 9.1, 9.2 mp1版本需要打下面这个补丁:
Patch 8103191(SU Patch [G41I]: allow t3 client connect thru BigIP F5 LB when ports are
different using enableProtocolSwitch flag)
没有for WebLogic Server 8.1版本的补丁。
据了解,客户这套业务系统所使用的WebLogic Server 8.1版本是在2003年7月发布、2009年9月终止基础服务(终止客户的补丁请求)、2011年9月终止扩展服务,伴随着这样的顾虑还是通过MOS提的一个SR向Oracle Support索取for WebLogic 8.1的补丁。随后,Oracle Support回复,由于WebLogic Server 8.1版本早已终止基础服务,Oracle BDE部门会直接拒绝申请补丁的请求。考虑到客户这边儿这套系统刚开始投入使用,而且开发商以应用不支持高版本WebLogic为由拒绝升级(应用程序中有诸多第三方jar包,不能妄加升级),这就需要我们手工来制作这个补丁了,好在补丁修复的代码不多。
2.编写补丁
2.1 在高版本WebLogic查找enableProtocolSwitch所关联的类文件
在Eclipse中构建一个Project,使用1.5的JRE作为编译器,然后加载WebLogic Server 9.2 mp3的核心类库文件(weblogic.jar),接着在此Project中搜索”enableProtocolSwitch”关键字:
find class
在搜索结果中可以看出,该关键字仅在weblogic.jar包中的weblogic.rjvm.ConnectionManagerServer类中出现。
2.2 对比新版本修复的代码
分别反编译WebLogic 8.1 sp6、9.2 mp3两个版本的weblogic.rjvm.ConnectionManagerServer类,得到源文件,然后通过文本对比工具找到enableProtocolSwitch变量相关的代码,核心代码如下:
compare
通过拿WebLogic Server 8.1的源代码分析比较,发现上面反编译出来的ConnectionManagerServer类的源文件中的变量已经被替换并改名(portMatches变量已被替换为flag1),上面的源文件不能修改再使用。接着修改WebLogic Server 8.1的源代码中的ConnectionManagerServer.java:
if (enableProtocolSwitch)
portMatches = thisRJVM == null && id.isBootstrapping() || portMatches;
然后添加”-Dweblogic.rjvm.enableprotocolswitch=true”属性的读取代码:
static {
String s = System.getProperty("weblogic.rjvm.enableprotocolswitch");
if ("true".equals(s)){
enableProtocolSwitch = true;
}
}
最后添加此变量的声明等代码即可。
使用WebLogic Server 8.1核心类库文件以及对应版本的JDK,编译修改过的ConnectionManagerServer.java:
compile
3.使用Ant生成补丁
3.1 准备Ant打包环境
在本地任意磁盘分区下(例如:C盘),新建一个目录(例如:generate patch),进入该目录后,在该目录下创建一个名为classes的目录,作为Ant脚本打包读取资源的源目录。将上一步编译生成的weblogic目录(里面包含修复后的ConnectionManagerServer.class)放置在classes目录下,放置后的目录结构如下:
C:\>tree /f "generate patch
"C:\GENERATE PATCH
└─classes
└─weblogic
└─rjvm
ConnectionManagerServer.class
3.2 编写Ant构建文件build.xml
在C:\generate patch下创建build.xml构建文件,内容如下(版面有限,已删除XML原有格式):
在C:\generate patch下创建build.xml构建文件,内容如下(版面有限,已删除XML原有格式):
<?xml version="1.0" encoding="UTF-8"?>
<project default="jar" name="generate patch" basedir=".">
<!-- 创建build.time属性,并格式化输出时间 -->
<tstamp>
<format property="build.time" pattern="EEE MMM d HH:mm:ss z yyyy" timezone="GMT+8:00" locale="en,US"/>
</tstamp>
<!-- 创建jar.file属性,指定补丁的输出文件名 -->
<property name="jar.file" value="CR287375_81sp6.jar"/>
<!-- classes.dir属性,指定打包时读取资源的源目录 -->
<property name="classes.dir" value="classes"/>
<!-- 设置补丁包档案文件相关属性 -->
<property name="implementation.title" value="WebLogic Temporary Patch for CR287375 ${build.time}"/>
<property name="author.name" value="tdy218@gmail.com"/>
<property name="implementation.version" value="8.1.6.0"/>
<property name="implementation.vendor" value="BEA Systems"/>
<target name="jar">
<jar destfile="${jar.file}" basedir="${classes.dir}/">
<manifest>
<attribute name="Implementation-Title" value="${implementation.title}"/>
<attribute name="Author-Name" value="${author.name}"/>
<attribute name="Implementation-Version" value="${implementation.version}"/>
<attribute name="Implementation-Vendor" value="${implementation.vendor}"/>
</manifest>
</jar>
</target>
</project>
3.3 生成补丁包
设置Ant以及JDK 1.4.2的环境变量,然后在命令行执行下面的命令生成补丁包:
C:\generate patch> ant -buildfile build.xml
Buildfile: C:\generate patch\build.xml
jar:[jar] Building jar: C:\generate patch\CR287375_81sp6.jar
BUILD SUCCESSFUL
Total time: 0 seconds
查看生成的补丁包:
patch
使用压缩文件打开CR287375_81sp6.jar文件(即我们需要的补丁包),点击META-INF目录下的MANIFEST.MF文件,即可看到此补丁的一些属性,而且与Oracle官方发布的补丁属性基本一致:
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.3
<span style="font-size:16px;">Created-By: 1.4.2_19-b04 (Sun Microsystems Inc.)
</span><span style="font-size:16px;">Implementation-Title: WebLogic Temporary Patch for CR287375 Wed Dec 18 10:24:48 GMT+08:00 2013
</span><span style="font-size:16px;">Author-Name: tdy218@gmail.com
</span><span style="font-size:16px;">Implementation-Version: 8.1.6.0
Implementation-Vendor: BEA Systems</span>
4.准备测试环境
4.1 准备一个包含两个被管Server的WebLogic域,部署EJB服务
创建一个域,包含一个管理Server和两个被管Server,在两个被管Server上部署EJB服务,如下图所示:
ejb cluster
4.2 给WebLogic打补丁,并在两个被管Server启动脚本添加参数
给WebLogic Server软件打补丁,并在两个被管Server启动脚本添加支持端口转换的参数:
-Dweblogic.rjvm.enableprotocolswitch=true
4.3 在F5 BIG-IP中创建一个Pool和一个Virtual Server
在F5 BIG-IP中创建一个Pool,并添加上面两个被管Server的监听地址及端口:
virtual server
池名称:ejb_pool,包含两个成员:
192.168.136.218:7003
192.168.136.219:7005
创建一个Virtual Server,并关联上一步创建的Pool,如下图所示:
pool
虚拟机服务器名称:ejb_virtualserver,监听地址和端口:192.168.136.220:8080,该地址就是EJB客户端连接代码里需要添加的地址。
5.测试验证
5.1 查看补丁信息
启动一个已经打过补丁的WebLogic Server实例,在启动脚本的标准输出窗口,打印WebLogic Server版本信息时,即会打印出我们编写的补丁信息:
patch info
随后打开WebLogic Server的Console控制台,查看版本信息,也能看到我们这个补丁:
version
也只有这一个补丁是2013年份的,其他补丁均为2009(WebLogic 8.1基础服务终止年份)及之前年份的。
5.2 测试客户端连接
在EJB客户端程序代码中指定Context.PROVIDER_URL属性值为F5 BIG-IP中设置的Virtual Server(ejb_virtualserver)的监听地址和端口:
env.put(Context.PROVIDER_URL, "t3://192.168.136.220:8080");
然后运行,在标准输出中输入任意字符串:
client invoke
输出初始化上下文以及查询JNDI名字的时间、输入字符串的大写回显。
说明补丁可用,同时解决了使用F5 BIG-IP做代理时因为Virtual Server监听端口与WebLogic被管Server监听端口不一致时连接报错的问题。
--转自