RPC中Hessian请求转发

由于产品开发需要,最近研究了一下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();
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值