RMI结合HAProxy的实际应用及问题解决的思路

假设客户端机器IP为IP1,HAProxyIP为IP2,后台的RMI服务器有2台,分别为IP3和IP4

IP1--->IP2------>IP3

               |_____>IP4

正常情况下,IP1的客户端代码指向IP2,IP2做分流到IP3(负载均衡),IP3返回的真实服务地址为IP3.

则响应通过IP2回到IP1后,IP1直接连接了IP3.这就是要解决的问题。

解决方案:

修改上一篇的软件代码如下:

client端代码:

import java.rmi.RemoteException;
import java.util.Enumeration;
 
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameClassPair;
import javax.naming.NamingException;
 
public class WarehouseClient {
 
public static void main(String[] args) throws NamingException,
RemoteException {
Context namingContext = new InitialContext();
/*
Enumeration<NameClassPair> e = namingContext.list("rmi://192.168.243.109:8080/");
while (e.hasMoreElements()) {
System.out.println(e.nextElement().getName());
}
*/
//
String url = "rmi://IP2:8080/central_warehouse";
Warehouse centralWarehouse = (Warehouse) namingContext.lookup(url);
if(null==centralWarehouse){
System.out.println("fail to find remote RMI server...");
return;
}
String descr = "Blackwell Toaster";
double price = centralWarehouse.getPrice(descr);
System.out.println(descr + ": " + price);
 
}
}

 

服务器端代码为:

import java.rmi.registry.LocateRegistry;
import java.rmi.server.RMISocketFactory;
import javax.naming.Context;
import javax.naming.InitialContext;
public class WarehouseServer {
 public static void main(String[] args) {
  //在服务器端的容器内注册此对象
  //注意:当服务器有多个IP时,client连接时会有问题。典型的服务器有多个 ip 引起的 rmi 连接问题。
  //参考:http://blog.csdn.net/model_cz/article/details/6525029
  //
  try {
   //指定服务器的设置
   System.setProperty("java.rmi.server.hostname" , "IP2" );
   //System.setProperty("java.rmi.server.hostname" , "1.2.3.4" );
   LocateRegistry.createRegistry(8080);    
   //指定数据连接端口
   RMISocketFactory.setSocketFactory(new WareFactory()); 
   
   WarehoseImpl centralWarehouse = new WarehoseImpl();
   Context namingContext = new InitialContext();
   namingContext.bind("rmi://IP3:8080/central_warehouse", centralWarehouse);
  } catch (Exception e) {
   System.out.println(e.toString());
   return;
  }
  System.out.println("waiting for the client to connect...");
 }
}

       解决思路:

 参考了http://blog.csdn.net/multiarrow/article/details/8551298

to("---------------------------------");下面的代码可以获取这个远程引用的IP和端口,不够目前我们用不上这些代码
  /*
  RemoteObjectInvocationHandler roih = (RemoteObjectInvocationHandler)Proxy.getInvocationHandler(centralWarehouse);  
  o(roih.getRef().remoteToString());  
  sun.rmi.server.UnicastRef ref = (sun.rmi.server.UnicastRef)roih.getRef();  
  LiveRef liveRef = ref.getLiveRef();  
  Channel c = liveRef.getChannel();
  if(c instanceof TCPChannel ){
   o("big congratulations...");
   TCPChannel tc =(TCPChannel)c;
   
  }else{
   o(c.toString());
  }
  //o("endpoint "+(liveRef.getChannel().getEndpoint());  
  o("port "+liveRef.getPort());
  */
  o("---------------------------------");

 这样就获取了[IP:Port],其实我们要的就是Port.

由于控制链已经基于负载均衡,

所以数据链通过HAProxy直连这个[IP:Port],的话,

也是某种程度的负载均衡,不是吗?

-----------

背景知识:

控制链是短连接。

数据链是长连接。

PS:看过HADOO的RPC框架源码再理解RMI毫无压力。

==================================

然后,问题就转化为HAProxy的源码修改了。

需要增加的功能:

client--->HAProxy--->RMI SERVER

本质上就是在HAProxy源码中添加对RMI协议的分析。

所以,第一个问题是去找到RMI的RFC文档学习RMI协议。

发现找了半天没找到JRMP协议的RFC文档,于是我放弃了解析协议。

转而考虑依靠IP和端口提供线索。

经过抓包分析RMI的工作流后,我最终给出的思路如下:

一个HAProxy代理后面配置若干RMI服务器,大致的配置就是如下:

 ========================================

IP1--->IP2------>IP3

               |_____>IP4

IP1是RMI_client.   IP2是HAProxy,  IP3&IP4都是RMI_Server.

(所以需要保证ip3的控制端口80,数据端口为81,服务器代码返回的RMI对象的ip:port为  IP2 : 81)

(所以需要保证ip4的控制端口80,数据端口为82,服务器代码返回的RMI对象的ip:port为  IP2 : 82)

那么配置就是:

HAProxy:

 listen 0.0.0.0:80

server ip3 ip3:80

server ip4 ip4:80

这样的话,控制端仍然保持负载均衡。

 

剩下的关键在于数据端口

Haproxy:

listen 0.0.0.0:81

 server ip3 ip3:81

listen 0.0.0.0:82

  server ip4 ip4:82

这样就解决问题了。

不过haproxy的IP就暴露给了rmi server.

个人认为这是性价比最高的一种方案。

----------------------------------------

方案2是为技术狂准备的。

每个RMI服务器的数据端口都一样,

控制连的数据为RMI协议,修改haproxy源码做RMI协议(JRMP?)应用层协议分析。

提取(有用的信息比如objectid等 , ip)映射。

这个映射表可根据需要长期存在或者动态删除。

然后数据端建立连接时解析RMI协议的objectid,查找哈希表映射的IP,连接。

这个理论上很完美,但是因为要

1RMI应用层协议分析

2 修改haproxy源码。

所以性价比略低于方案1,而且在多台haproxy时无法共享哈希表映射关系(提取出来放在公共的缓存服务器?)。

------

从维护和劳动工作量的角度,个人偏向方案1,需改配置和服务器的数据端口即可。

你可能感兴趣的:(haproxy,rmi)