项目介绍

最近在一个互联网跨境支付项目组,所使用的技术比较老,代码是写于2006年的,整个系统采用服务的架构模式,连接件使用hessian进行同步调用,使用MQ进行异步调用。
跨境系统的服务分类上,主要有两类,一类是线上的,比如交易,网关,出金,入金等,一类是线下的,比如对账,核算等。这个分类方法,有点像之前金融市场业务功能分成前中后台子系统。言归正传,本文主要是讲一下,hessian在该项目中的使用。

划分模块

整个工程分为两个WEB模块:客户调用模块client,服务处理模块handler。两个基本jar组件:服务注册组件register,hessian工具组件hessianutil.

register : 只有一个枚举,用于注册服务,一个服务一个枚举。是不是瞬间感觉低端了。
hessianutil : 提供了hessian操作的工具类套件。之所以将这两个分开,是因为hessianutil基本不变,而注册类就经常变动了
client : 服务调用者
handler : 服务提供者

代码

register:

public enum SerCode {
    SIMPLE_CALL_RETURN_STRING("000000","简单调用"),
    SIMPLE_CALL_RETURN_MAP("000001","返回字典");

    private String code;
    private String desc;

    SerCode(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public String getCode() {
        return code;
    }

    public String getDesc() {
        return desc;
    }
}

client:

注意在hessian的service中传入了handler的服务调用地址,hessian会创建代理,来实现RPC调用

public void makeSimpleCall(){
        Map<String, String> paraMap = new HashMap<String,String>();
        String reqMsg = JSonUtil.toJSonString(paraMap);
        HessianInvokeParam param = HessianInvokeHelper.processRequest(reqMsg);
        String sysTraceNo = SysTraceNoService
                .generateSysTraceNo(SystemCodeEnum.WEBGATE.getCode());
        String result = clientHessianService.invoke(
                SerCode.SIMPLE_CALL_RETURN_STRING.getCode(), sysTraceNo,
                SystemCodeEnum.WEBGATE.getCode(),
                SystemCodeEnum.TXNCORE.getCode(),
                SystemCodeEnum.TXNCORE.getVersion(), param.getDataLength(),
                param.getMsgCompress(), param.getDataMsg());
        param.parse(result);
        HessianInvokeHelper.processResponse(param);
        result = param.getDataMsg();
        System.out.println("result:"+result);
    }
     <bean id="client-txnCoreService"
          class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
        <property name="serviceUrl" value="http://localhost:8080/handler/service/invoke" />
        <property name="serviceInterface" value="com.mvp.hessian.service.HessianInvokeService" />
    </bean>

handler:

public class SimpleHandler implements EventHandler {
    public String handle(String dataMsg) throws HessianInvokeException {
        return "this is from simple handler";
    }
}
<!--bean配置-->
<bean id="txncoreService" class="com.mvp.hessian.service.HessianService">
        <property name="eventHandlerMap" ref="eventHandlerMap" />
</bean>
<bean id="eventHandlerMap" class="java.util.HashMap">
    <constructor-arg>
        <map>
            <entry key="000000" value-ref="simpleHandler" />
        </map>
    </constructor-arg>
</bean>
<bean id="simpleHandler" class="com.mvp.hessian.handler.SimpleHandler">
</bean>
<!--servlet配置-->
<bean name="/invoke" class="org.springframework.remoting.caucho.HessianServiceExporter">
        <property name="serviceInterface" value="com.mvp.hessian.service.HessianInvokeService"/>
        <property name="service" ref="txncoreService"/>
</bean>
<!--web.xml-->
<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath*:context/**/*.xml
        </param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>remoting</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                classpath*:remote/**/*.xml
            </param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>remoting</servlet-name>
        <url-pattern>/service/*</url-pattern>
    </servlet-mapping>

通过如上配置,客户端就可以通过访问http://localhost:8080/handler...和对应的code来访问handler了。

hessianutil

封装了hessian的功能,便于调用。里面的类就不一一做介绍了。主要包括枚举类和帮助类。

//所有handler必须实现的接口
public interface EventHandler {
    String handle(String dataMsg) throws HessianInvokeException;
}
//hessian调用时用的接口
public interface HessianInvokeService {
    /**
     * Hessian通讯服务接口
     *
     * @param serCode
     *            服务代码
     * @param sysTraceNo
     *            系统跟踪号
     * @param originNo
     *            源系统编号
     * @param targetNo
     *            目的系统编号
     * @param versionNo
     *            接口版本号 格式:1.0.0
     * @param dataLength
     *            消息正文长度
     * @param msgCompress
     *            消息正文是否压缩
     * @param dataMsg
     *            消息正文
     * @return
     */
    String invoke(String serCode, String sysTraceNo, String originNo,
                  String targetNo, String versionNo, int dataLength, int msgCompress,
                  String dataMsg);

}
//调用的主要方法类
public class HessianService implements HessianInvokeService {

    private final Log logger = LogFactory.getLog(HessianService.class);
    private Map<String, EventHandler> eventHandlerMap;
    public void setEventHandlerMap(Map<String, EventHandler> eventHandlerMap) {
        this.eventHandlerMap = eventHandlerMap;
    }
    @SuppressWarnings("unchecked")
    @Override
    public String invoke(String serCode, String sysTraceNo, String originNo,
                         String targetNo, String versionNo, int dataLength, int msgCompress,
                         String dataMsg) {

        logger.info("requet auth system:" + "serCode:" + serCode
                + "sysTraceNo:" + sysTraceNo + "originNo:" + originNo
                + "targetNo:" + targetNo + "versionNo:" + versionNo);

        if(logger.isDebugEnabled()){
            logger.info("dataMsg:" + dataMsg);
        }

        Map<String, String> result = new HashMap<String, String>();
        try {

            // 验证请求参数
            HessianInvokeHelper.validateReqParam(serCode, sysTraceNo, originNo,
                    targetNo, versionNo, dataLength, msgCompress, dataMsg);

            // 验证请求服务代码是否正确
            EventHandler handler = eventHandlerMap.get(serCode);
            if (handler == null) {
                throw new HessianInvokeException(
                        ResponseCodeEnum.UNDEFINED_SERVICE.getCode(),
                        ResponseCodeEnum.UNDEFINED_SERVICE.getDesc());
            }

            // 验证目标系统编码
            HessianInvokeHelper.validateTargetNo(targetNo,
                    SystemCodeEnum.TXNCORE.getCode());

            // 验证请求消息正文内容长度
            HessianInvokeHelper.validateDataMsgSize(dataLength, dataMsg);

            String reqMsg = dataMsg;
            // 判断是否需要解压请求消息正文内容
            if (msgCompress == 1) {
                try {
                    reqMsg = ZipUtil.uncompress(dataMsg);
                } catch (IOException e) {
                    throw new HessianInvokeException(
                            ResponseCodeEnum.UNCOMPRESS_FAILURE.getCode(),
                            ResponseCodeEnum.UNCOMPRESS_FAILURE.getDesc(), e);
                }
            }

            if(logger.isDebugEnabled()){
                logger.info("reqMsg:" + reqMsg);
            }

            Map<String, Object> map = JSonUtil.toObject(reqMsg, Map.class);
            map.put("sysTraceNo", sysTraceNo);
            String rsp = handler.handle(JSonUtil.toJSonString(map));

            return HessianInvokeHelper.buildResponse(serCode, sysTraceNo,
                    SystemCodeEnum.TXNCORE.getCode(), originNo, versionNo, rsp);

        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            logger.error(e.getMessage(), e);
            result.put("responseCode",
                    ResponseCodeEnum.UNDEFINED_ERROR.getCode());
            result.put("responseDesc",
                    ResponseCodeEnum.UNDEFINED_ERROR.getDesc());
        }
        return HessianInvokeHelper.buildResponse(serCode, sysTraceNo,
                SystemCodeEnum.TXNCORE.getCode(), originNo, versionNo,
                JSonUtil.toJSonString(result));
    }
}

沈子平
183 声望17 粉丝

慢慢积累,一切都不会太晚.