解决JMX 采用hessian协议在NAT网络环境下的通信问题

       项目中采用了JMX进行客户端与服务端的通信。JMX采用了MX4J。通信协议采用了hessian。但是在NAT的网络环境下,由于JMX agent端(即JMX服务端)采用的是NAT网络内的私有IP,而NAT之外的JMX client端是采用公网IP访问JMX agent.这样访问时,JMX之间的连接建立失败。启动JMX agent后,在外网用agent对应的公网IP及端口进行telnet ,是可以成功的,看来问题出现在JMX建立连接的机制上。

      在网络上查阅相关资料,有讨论解决rmi协议下的NAT网络通信问题。rmi协议下,可采用设置系统变量System.setProperty("java.rmi.server.hostname", your public IP)的方式解决问题。而hessian采用了http协议进行通信,没有走rmi的解决途径。网络上关于JMX 用hessian协议进行NAT网络通信的讨论信息很少。看来只好看源码了。
    MX4J中,当采用hessian协议通信时,MX4J会在内部启动一个jetty的容器,然后将HessianServlet发布到这个容器中。客户端访问时,通过http://IP:port/hessian的 形式的URL访问该servlet。 通过实验,当在NAT网络下部署一个jetty容器并发布后,在外网用公网IP访问目标主机的jetty是可以访问到的,这说明NAT网络本身是可以处理公网IP与私网IP的IP映射的,MX4J建立连接失败应该在于建立连接时 对IP进行的某种校验。
    一步步跟代码,最后发现了问题所在。采用hessian后,启动jmx 服务,MX4J会返回一个HTTPConnectorServer来处理通信问题。该类继承于AbstractJMXConnectorServer。在启动完jetty,发布servlet,创建connectionManager后,HTTPConnectorServer会将该connectionManager注册到一个名为instances的Map中,该value对应的key即为启动时的JMXServiceURL。当客户端访问到来时,HttpService类会根据客户端请求的URL组建一个新的JMXServiceURL,用该URL去instances的MAP中取connectionManager。当NAT网络环境下时,客户端传来的URL请求中的IP是外网公共IP,而注册在Map中的URL里的IP是内部IP。key不一样,自然取不到connectionManager,于是在客户端我们才会收到Could not find ConnectionManager. Make sure a HTTPConnectorServer is in classloader scope and bound at this address的报错。

     问题找到了,我们只要将客户端请求的URL中的IP映射为内网IP,NAT的通信问题即可解决了。可是该如何去实现这样的修改呢。RMI貌似在处理请求时,设置了内外网IP的转换接口,我们才可以修改java.rmi.server.hostname这个系统变量值来实现NAT下不同IP的映射。回归到hessian的代码,并没有看到类似的接口。看来我们只能重新写一套处理新协议的通信机制了。而最终目的就是能把客户端传来的IP替换为内网IP.

 

     在启动jmx时,JMXConnectorServer connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url, null, server);该方法会根据传入的不同协议,返回不同的JXMConnectorServer.进入到该方法的内部,最后发现,MX4J还是提供了扩展新协议的接口。JMXConnectorServerFactory内 通过ServiceLoader类来动态加载不同协议的ServerProvider。MX4J中,对应每一类通信协议,都定义了一个处理客户端的ClientProvider和处理服务端的ServerProvider。他们负责返回对应的Connector和ConnectorServer。例如我们定义一个新的协议nathessian,那如果希望JMX服务端能够识别该协议,我们需要写一个ServerProvider,该类实现了JMXConnectorServerProvider接口,并返回一个处理通信的JMXConnectorServer实现类。ServiceLoader类规定我们需要在类路径下添加META-INF/services/javax.management.remote.JMXConnectorServerProvide文件,在该文件中,指定我们定义的ServerProvider类的全路径(可查看mx4j的其他协议的这个文件,如hessian,如rmi)。这样,ServerProvider类可通过ClassLoader.getSystemResources(fullName)自动将我们定义的provider加载进来。

    下面如何添加处理nathessian的ConnectorServer。

    MX4J处理hessian的HTTPConnectorServer的doStart方法中,会根据协议类型,获取一个ConnectionResolver。查看mx4j的tools-remote的jar包,每一个基于http的协议下都对应一个Resolver类,该类继承自HTTPResolver,处理相关的创建web容器、部署等事项。子类中的getServletClassName()重写后,负责获取处理hessian请求的HessianServlet类名。这个Servlet会发布到Jetty容器中,通过Servlet里创建的HTTPService处理来自客户端的请求,并调用ConnetorServer里的find的方法,从instances的Map中根据URL获取之前注册的connectionMananger。我们需要自己写一套处理nathessian的ConnectorServer类,Resolver类 ,Servlet类, HTTPService类,分别继承已有的处理hessian的对应的类,只需重写少量几个方法,最终实现在根据URL查询map之前,将客户端的公网IP替换为内网IP;将客户端的hessian协议端替换为我们自定义的nathessian。

     代码都写好后,我们在服务端用nathessian作为协议类型,启动jmx,客户端不需改变,仍用hessian.在NAT环境下测试,OK了,通信正常了。

 

     总结一下,我们在服务端新写了 ServerProvider , HTTPNATConnectorServer, Resolver,NATHessianServlet,NATHTTPService几个子类来处理自定义的nathessian协议

 

   

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值