由于产品开发需要,最近研究了一下RPC中请求转发技术,现先介绍RPC协议和Hessian基础知识如下:
(1)RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。详情见百度百科:RPC远程过程调用协议
(2)Hessian是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能。 相比WebService,Hessian更简单、快捷。采用的是二进制RPC协议,因为采用的是二进制协议,所以它很适合于发送二进制数据。详情见百度百科:Hessian
主要思路考虑在【Server服务1】端把【Client】的请求由原来的本地服务重新映射到【Server】服务,而这个的关键就在于如何在不改动具体的函数体(太多了也无法一一改动)情况下把本地服务能够重新进行映射为远程服务,其中包括请求参数等问题。好在产品对【Client】的hessian请求采用了统一入口的方式,让这种思考成为可能。具体过程如下(涉及公司产品,提供改动后的示例代码):
1、【Client】发送请求服务
public static void main(String[] args) {
IBankcheckBillService bankcheckBillService = (IBankcheckBillService) create(IBankcheckBillService.class, "bankcheckBillService");
String billStatus = bankcheckBillService.getBillStatus(String billNo);
System.out.println("单据状态:"+billStatus);
}
/**
* client产生HessianProxyFactory请求服务
* @param serviceClass Hessian接口class
* @param serviceName 调用服务端的服务标识
* @return
*/
public static Object create(Class serviceClass, String serviceName) {
Object service = null;
try {
String server1_url = "http://localhost:7001";
String url = server1_url + "/hessianService/" + serviceName + "?meta=" + URLEncoder.encode(metaInfo, "UTF-8");
HessianProxyFactory hessianFactory = new HessianProxyFactory();
hessianFactory.setOverloadEnabled(true);
service = hessianFactory.create(serviceClass, url);
} catch (Exception ex) {
throw new RuntimeException(ex.getMessage(), ex);
}
return service;
}
2、Server1服务端代码
(1)配置web.xml映射服务Server1本地服务
<servlet>
<servlet-name>hessianService</servlet-name>
<servlet-class>com.ufgov.fm.server.web.HessianServletExt</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hessianService</servlet-name>
<url-pattern>/hessianService/*</url-pattern>
</servlet-mapping>
(2)在HessianServletExt类中截取serviceName做判断映射本地服务还是Server服务2
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Calendar;
import java.util.Enumeration;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.anyi.gp.context.ApplusContext;
import com.caucho.hessian.client.HessianProxyFactory;
import com.caucho.hessian.io.AbstractHessianInput;
import com.caucho.hessian.io.AbstractHessianOutput;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;
import com.caucho.hessian.io.SerializerFactory;
import com.caucho.hessian.server.HessianSkeleton;
import com.ufgov.fm.common.service.IBankcheckBillService;
import com.ufgov.fm.common.service.ICommonService;
import com.ufgov.fm.common.service.IFmBatchPayBillService;
import com.ufgov.fm.common.service.IFmInterestSolutionService;
import com.ufgov.fm.common.service.IFmSinglePayBillService;
import com.ufgov.fm.common.util.ObjectUtil;
public class HessianServletExt extends HttpServlet implements Servlet {
private static boolean hasGetFrontProcessorSchem = false;
private static final long serialVersionUID = -1658078591921872934L;
public HessianServletExt() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
/**
* 请求转移到server服务2
* 根据客户端serviceName返回对应的前置机Hessian接口class;如果返回null代表不转移到前置机服务
*/
private Class getServiceClassForFP(String serviceName) throws Exception {
Class serviceClass = null;
if ("fmBatchPayBillService".equalsIgnoreCase(serviceName)) {
serviceClass = IFmBatchPayBillService.class;
} else if ("fmSinglePayBillService".equalsIgnoreCase(serviceName)) {
serviceClass = IFmSinglePayBillService.class;
}
return serviceClass;
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String pathInfo = request.getPathInfo();
String serviceName = pathInfo.substring(1);
AbstractHessianInput input = null;
AbstractHessianOutput output = null;
try {
Object service = ApplusContext.getBean(serviceName);
// 请求转移到server服务2
Class serviceClass = getServiceClassForFP(serviceName);
if (serviceClass != null) {
//获得所有参数并重新组织参数URL
StringBuffer paraUrlBuff = new StringBuffer();
Enumeration paramNames = request.getParameterNames();
while (paramNames.hasMoreElements()) {
String paramName = (String) paramNames.nextElement();
String[] paramValues = request.getParameterValues(paramName);
if (paramValues.length == 1) {
String paramValue = paramValues[0];
if (paramValue.length() != 0) {
if (paraUrlBuff.length() > 0) {
paraUrlBuff.append("&");
}
paraUrlBuff.append(paramName).append("=").append(URLEncoder.encode(paramValue, "UTF-8"));
}
}
}
//从数据库获得server2服务器url
ICommonService commonService = (ICommonService) ApplusContext.getBean("commonService");
if (!hasGetFrontProcessorSchem) {
hasGetFrontProcessorSchem = true;
frontProcessorSchem = commonService.getOptVal("" + Calendar.getInstance().get(Calendar.YEAR), "*", "OPT_FM_FRONTPROCESSOR_SCHEM");
}
String url = frontProcessorSchem + "/FM/HessianServiceForFP/" + serviceName;
if (paraUrlBuff.length() > 0) {
url += "?" + paraUrlBuff.toString();
}
HessianProxyFactory hessianFactory = new HessianProxyFactory();
hessianFactory.setOverloadEnabled(true);
service = hessianFactory.create(serviceClass, url);
}
HessianSkeleton homeSkeleton = new HessianSkeleton(service, service.getClass());
InputStream is = request.getInputStream();
OutputStream os = response.getOutputStream();
response.setContentType("application/x-hessian");
int code = is.read();
int major;
int minor;
if (code == 'H') {
major = is.read();
minor = is.read();
if (major != 0x02 || minor != 0x00)
throw new IOException("Version " + major + "." + minor + " is not understood");
input = new Hessian2Input(is);
output = new Hessian2Output(os);
input.readCall();
} else if (code == 'c') {
major = is.read();
minor = is.read();
input = new HessianInput(is);
if (major >= 2)
output = new Hessian2Output(os);
else
output = new HessianOutput(os);
} else {
throw new IOException("expected 'H' (Hessian 2.0) or 'c' (Hessian 1.0) in hessian input at " + code);
}
SerializerFactory serializerFactory = new SerializerFactory();
input.setSerializerFactory(serializerFactory);
output.setSerializerFactory(serializerFactory);
//映射具体的sping类
homeSkeleton.invoke(input, output);
} catch (Exception e) {
e.printStackTrace();
} finally {
output.close();
input.close();
}
}
}
2、Server2服务端代码
Server2端代码和Server1端代码类似,只是在 HessianServletExtForFP类中不再有转发服务代码了。
(1)web.xml配置
<servlet>
<servlet-name>HessianServiceForFP</servlet-name>
<servlet-class>com.ufgov.fm.server.web.HessianServletExtForFP</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HessianServiceForFP</servlet-name>
<url-pattern>/HessianServiceForFP/*</url-pattern>
</servlet-mapping>
(2)在HessianServletExtForFP类中映射本地服务
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String pathInfo = request.getPathInfo();
String serviceName = pathInfo.substring(1);
AbstractHessianInput input = null;
AbstractHessianOutput output = null;
Object service = null;
try {
service = ApplusContext.getBean(serviceName);
HessianSkeleton homeSkeleton = new HessianSkeleton(service, service.getClass());
InputStream is = request.getInputStream();
OutputStream os = response.getOutputStream();
response.setContentType("application/x-hessian");
int code = is.read();
int major;
int minor;
if (code == 'H') {
major = is.read();
minor = is.read();
if (major != 0x02 || minor != 0x00)
throw new IOException("Version " + major + "." + minor + " is not understood");
input = new Hessian2Input(is);
output = new Hessian2Output(os);
input.readCall();
} else if (code == 'c') {
major = is.read();
minor = is.read();
input = new HessianInput(is);
if (major >= 2)
output = new Hessian2Output(os);
else
output = new HessianOutput(os);
} else {
throw new IOException(
"expected 'H' (Hessian 2.0) or 'c' (Hessian 1.0) in hessian input at "
+ code);
}
SerializerFactory serializerFactory = new SerializerFactory();
input.setSerializerFactory(serializerFactory);
output.setSerializerFactory(serializerFactory);
homeSkeleton.invoke(input, output);
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException(ex.getMessage(), ex);
} finally{
if(output!=null)
output.close();
if(input!=null)
input.close();
}
}