文档章节

Hessian体验与分析

ksfzhaohui
 ksfzhaohui
发布于 2017/05/07 16:09
字数 2039
阅读 725
收藏 30
点赞 1
评论 0

简介
Hessian是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能;相比WebService,Hessian更简单、快捷。
官网地址:http://hessian.caucho.com/index.xtp
下面主要针对Hessian入门级使用,以及进行部门源码分析。

简单使用
提供三个模拟块,分别模拟client,server以及被依赖的jar;对应的模块名称分别是:hessianClient,hessianServer以及hessianJar
代码结构如下图所示:

1.hessianJar介绍
hessianJar主要提供被hessianClient和hessianServer依赖的公共类,这里主要提供了接口类IHessianService和pojo对象Bean
IHessianService类:

public interface IHessianService {
     
    public String getString(String value);
     
    public Bean getBean();
}

对象Bean:

public class Bean implements Serializable {
    private static final long serialVersionUID = 1L;
    private String value;
 
    public Bean(String value) {
        this.value = value;
    }
 
    public String getValue() {
        return value;
    }
 
    public void setValue(String value) {
        this.value = value;
    }
}

2.hessianSever介绍
hessianServer主要用来对外提供服务的,因为hessian本身是基于http协议的,所以可以直接部署到web容器中比如tomcat,http协议解析可以直接交给web容器;当然这里hessianServer本身是一个web项目,除了对外提供的服务类HessianServiceImpl,还需要配置web.xml文件。
web.xml:

<web-app>
    <display-name>Archetype Created Web Application</display-name>
 
    <servlet>
        <servlet-name>hessianService</servlet-name>
        <servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>
        <init-param>
            <param-name>home-class</param-name>
            <param-value>zh.hessian.hessianServer.HessianServiceImpl</param-value>
        </init-param>
        <init-param>
            <param-name>home-api</param-name>
            <param-value>zh.hessian.hessianJar.IHessianService</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>hessianService</servlet-name>
        <url-pattern>/hessianService</url-pattern>
    </servlet-mapping>
</web-app>

服务类HessianServiceImpl:

public class HessianServiceImpl implements IHessianService {
 
    @Override
    public String getString(String value) {
        return "REQ + " + value;
    }
 
    @Override
    public Bean getBean() {
        return new Bean("value");
    }
}

3.hessianClient介绍
hessianClient模拟客户端的调用,对hessianSever发起请求,并且接受回复;提供HessianClient类
HessianClient:

public class HessianClient {
 
    public static void main(String[] args) {
        String url = "http://localhost:8080/hessianServer/hessianService";
        HessianProxyFactory factory = new HessianProxyFactory();
        try {
            IHessianService hessianService = (IHessianService) factory.create(IHessianService.class, url);
            System.out.println("getString:" + hessianService.getString("zhaohui"));
            Bean bean = hessianService.getBean();
            System.out.println("getBean:" + bean.getValue());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

部署测试
1.首先将hessianJar打包安装,maven命令:clean install
2.将hessianServer打包发布,maven命令:clean package,然后部署到tomcat中,启动tomcat
3.将hessianClient打包运行,maven命令:clean package,然后运行HessianClient类

结果如下:

getString:REQ + zhaohui
getBean:value

 

hessianClient流程分析
1.代理类HessianProxy
很多开源的RPC框架,用来实现远程方法调用,其实核心思想都是动态代理,代理类封装了对消息的序列化,远程的连接以及消息的接受,Hessian也不列外,提供了HessianProxy类,部分代码如下:

public class HessianProxy implements InvocationHandler, Serializable {
   public Object invoke(Object proxy, Method method, Object []args)
    throws Throwable
    {
      ......
      conn = sendRequest(mangleName, args);
      is = getInputStream(conn);
      ......
    }
    .....
}

HessianProxy实现了invoke方法,这样每次在调用方法(如:hessianService.getString(“zhaohui”))时,自动触发invoke方法,这样我们就可以封装方法和请求参数,建立http请求(sendRequest方法),接受响应(getInputStream方法)。

2.http请求类
既然Hessian本身是基于http协议的,对http的请求我们会想到HttpURLConnection类,Hessian也不例外,只是被包装成了HessianConnection类,代码结构如下:

public interface HessianConnection {
  /**
   * Adds HTTP headers.
   */
  public void addHeader(String key, String value);
   
  /**
   * Returns the output stream for the request.
   */
  public OutputStream getOutputStream()
    throws IOException;
 
  /**
   * Sends the query
   */
  public void sendRequest()
    throws IOException;
 
  /**
   * Returns the status code.
   */
  public int getStatusCode();
 
  /**
   * Returns the status string.
   */
  public String getStatusMessage();
   
  /**
   * Returns the content encoding
   */
  public String getContentEncoding();
   
 
  /**
   * Returns the InputStream to the result
   */
  public InputStream getInputStream()
    throws IOException;
 
  /**
   * Close/free the connection. If keepalive is allowed, it may be used.
   */
  public void close()
    throws IOException;
 
  /**
   * Shut the connection down.
   */
  public void destroy()
    throws IOException;
}

里面提供了一些常用的方法,添加http头文件addHeader(),获取输出流getOutputStream(),获取输入流getInputStream()等;

3.发送请求
有了HessianConnection就可以发送http请求了,HessianProxy中提供了sendRequest方法,部分代码如下:

protected HessianConnection sendRequest(String methodName, Object []args)
    throws IOException
  {
    HessianConnection conn = null;
    conn = _factory.getConnectionFactory().open(_url);
    boolean isValid = false;
    try {
      addRequestHeaders(conn);
      OutputStream os = null;
      try {
        os = conn.getOutputStream();
      } catch (Exception e) {
        throw new HessianRuntimeException(e);
      }
      ......
      AbstractHessianOutput out = _factory.getHessianOutput(os);
 
      out.call(methodName, args);
      out.flush();

首先在请求中添加了http请求头分别是:Content-Type,Accept-Encoding以及Authorization,如下所示:

protected void addRequestHeaders(HessianConnection conn)
{
  conn.addHeader("Content-Type", "x-application/hessian");
  conn.addHeader("Accept-Encoding", "deflate");
 
  String basicAuth = _factory.getBasicAuth();
  if (basicAuth != null)
    conn.addHeader("Authorization", basicAuth);
}

然后通过OutputStream向服务器发送请求,Hessian把OutputStream封装在HessianOutput中,所以可以查看HessianOutput中call方法,部分代码如下:

public void call(String method, Object []args)
  throws IOException
{
  int length = args != null ? args.length : 0;
   
  startCall(method, length);
   
  for (int i = 0; i < length; i++)
    writeObject(args[i]);
   
  completeCall();
}

首先是写入方法名称,如何写入方法参数,最后完成写入标识,

(1).startCall方法如下:

public void startCall(String method, int length)
  throws IOException
{
  os.write('c');
  os.write(_version);
  os.write(0);
 
  os.write('m');
  int len = method.length();
  os.write(len >> 8);
  os.write(len);
  printString(method, 0, len);
}

前三个自己分别写入了c major minor,后面写入自己m表示开始写入方法信息,包括长度和字符串信息;

(2).写入方法参数,遍历args[]使用writeObject写入,主要是对参数进行序列化操作,核心类Serializer,Hessian会根据参数的类型使用不同的序列化,具体有哪些类型可以查看包com.caucho.hessian.io中实现实现了Serializer的类;

(3).最后写入结束标识,写入了字符’z’。

发送请求通过out.flush()方法,至此大概是客户端请求的整个流程,下面介绍一下服务器接受消息,处理消息以及回复消息

4.服务器端接受消息
web.xml中配置了处理消息的servlet:HessianServlet,并且配置了真实处理业务逻辑的api和class类,HessianServlet继承了两个核心的方法init和service,分别用来初始化数据和处理业务逻辑。
init()方法
init方法中加载了配置在web.xml中的HessianServiceImpl和IHessianService,并且封装到HessianSkeleton中
service()方法
通过HttpServletRequest和HttpServletResponse对象分别获取了我们需要的输入和输出流,如下:
InputStream is = request.getInputStream();
OutputStream os = response.getOutputStream();
接下来把InputStream和OutputStream传入HessianSkeleton中,消息的读取和回复都交给HessianSkeleton来处理

HessianSkeleton的invoke方法:

public void invoke(InputStream is, OutputStream os,
                     SerializerFactory serializerFactory)
{
    ....
    AbstractHessianInput in;
    AbstractHessianOutput out;
    ...
    in = _hessianFactory.createHessianInput(is);
    out = _hessianFactory.createHessian2Output(os);
    ...
    invoke(_service, in, out);
}

又进一步的将Inputstream封装入HessianInput中,将Outputstream封装入Hessian2Output中;
最下面的invoke(_service, in, out)方法是服务器处理最重要的地方,按照Client中输出流的写入格式,通过Inputstream将Client写入的消息读取出来包括头字节信息,方法名,参数列表,部分代码如下:

public void invoke(Object service,
                     AbstractHessianInput in,
                     AbstractHessianOutput out)
    throws Exception
  {
    ServiceContext context = ServiceContext.getContext();
    in.skipOptionalCall();
    ......//省略
    String methodName = in.readMethod();
    int argLength = in.readMethodArgLength();
 
    Method method;
    method = getMethod(methodName + "__" + argLength);
    if (method == null)
      method = getMethod(methodName);
 
    ......//省略
 
    Class<?> []args = method.getParameterTypes();
 
    if (argLength != args.length && argLength >= 0) {
      out.writeFault("NoSuchMethod",
                     escapeMessage("method " + method + " argument length mismatch, received length=" + argLength),
                     null);
      out.close();
      return;
    }
 
    Object []values = new Object[args.length];
 
    for (int i = 0; i < args.length; i++) {
      // XXX: needs Marshal object
      values[i] = in.readObject(args[i]);
    }
 
    Object result = null;
 
    try {
      result = method.invoke(service, values);
    } catch (Exception e) {
      Throwable e1 = e;
      if (e1 instanceof InvocationTargetException)
        e1 = ((InvocationTargetException) e).getTargetException();
 
      log.log(Level.FINE, this + " " + e1.toString(), e1);
 
      out.writeFault("ServiceException", 
                     escapeMessage(e1.getMessage()), 
                     e1);
      out.close();
      return;
    }
 
    // The complete call needs to be after the invoke to handle a
    // trailing InputStream
    in.completeCall();
 
    out.writeReply(result);
 
    out.close();
  }

(1).首先通过方法skipOptionalCall()跳过了输入流中的前几个字节,接下来获取了MethodName,还有方法参数存放在了Object []values中;
(2).接下来就是result = method.invoke(service, values);反射调用,获取到我们需要的result
(3).out.writeReply(result),通过Hessian2Output回复消息,代码如下:

public void writeReply(Object o)
  throws IOException
{
  startReply();
  writeObject(o);
  completeReply();
}

回复消息也指定了对应的格式,消息头+消息体+消息结束标识,具体内容可以自行去查看。

5.Client接受服务器的回复
再次回到Client端,在介绍HessianProxy类的时候提到了两个主要的方法分别是:sendRequest发送消息,getInputStream获取输入流,通过输入流来获取服务器端的回复。
同样的Inputstream也被封装成了Hessian2Input对象,通过Hessian2Input的readReply获取回复,和服务器读取客户端消息类似,此处不在详细介绍,可以直接查看readReply方法

总结
本文通过hessianJar,hessianClient已经hessianServer三个模块,提供了简单的入门实例;然后从代码层面介绍了整个消息的流程,从创建代理类HessianProxy到提供http连接的HessianConnection,然后发送http请求到服务器端接受http请求,反射调用业务逻辑类获取处理结果,然后封装消息回复Client,最后Client接受消息,输出消息;当然代码层面的介绍还是比较笼统的,很多细节还需要进一步了解。

个人博客:codingo.xyz

© 著作权归作者所有

共有 人打赏支持
ksfzhaohui

ksfzhaohui

粉丝 296
博文 126
码字总数 153828
作品 3
南京
高级程序员
dubbo服务中的hessian序列化工厂使用hashmap加锁在高并发场景下的问题

1.问题描述 我们在对5个dubbo接口并发进行测试,总共线程数是64个,不停的调用这些接口。观察到的异常显现是TPS波动较大,时高时低。 我们观察线程的运行状况是这样的: 上图红色部分是线程阻...

杨武兵 ⋅ 2015/06/12 ⋅ 2

hessian隐式传参

背景 web应用的项目:分表现层portal端和业务服务层service端,使用dubbo框架,rpc使用hessian。dubbo里的dubbo协议因为是socket长连接可以attachment隐式带参数,hessian协议则不能attachm...

v1-alpha ⋅ 2016/03/07 ⋅ 0

Spring使用Hessian实现远程调用

1.Spring中除了提供HTTP调用器方式的远程调用,还对第三方的远程调用实现提供了支持,其中提供了对Hessian的支持。 Hessian是由Caocho公司发布的一个轻量级的二进制协议远程调用实现方案,H...

zhangchd ⋅ 2015/05/11 ⋅ 0

hessian服务压力测试

需求:现在的系统有一部分业务是通过hessian的远程服务提供的,为了应对不断的增长的服务器压力,需要对这个提供hessian服务的工程能承受的压力有一个比较详细的了解。 hessian走的http的协议...

伊人梦醉 ⋅ 2016/06/20 ⋅ 0

hession client server exception

RPC异常处理概述 RPC异常处理指是,当客户端调用远端的服务,如果服务执行过程中发生异常,这个异常能否序列到客户端? 如果服务在执行过程中可能发生异常,那么在服务接口的声明中,就该声明...

squanchao ⋅ 2016/08/11 ⋅ 0

java.io.IOException: Server returned HTTP response

java.io.IOException: Server returned HTTP response code: 500 for URL: http://physics.whu.edu.cn/show.asp?id=278 java.io.IOException: Server returned HTTP response code: 403 for ......

为梦而来 ⋅ 2014/03/14 ⋅ 0

Hessian——轻量级的二进制协议远程调用实现方案

最近要做一个工作流的系统来支持子系统的上传,审批,复审,会签等操作,而一般传统的项目间交流主要是通过http调用另外的服务器的地址,返回一个json或是字符串,这种方式效率低,并且极其消...

帅的不像男的 ⋅ 2016/07/01 ⋅ 2

几种通讯协议的比较RMI,Httpinvoker,Hessian,Burlap,webservice

一、综述 本文比较了RMI,Hessian,Burlap,Httpinvoker,web service等5种通讯协议的在不同的数据结构和不同数据量时的传输性能。RMI是java语言本身提供的通讯协议,稳定高效,是EJB的基础。...

o0无忧亦无怖 ⋅ 2015/10/15 ⋅ 0

枚举在hessian序列化和反序列化中的问题

枚举在hessian序列化和反序列化中的问题 无标题2016-05-22255 阅读 JAVA 一.概述 最近在拆分一个枚举类,但是该枚举类使用在一个RPC接口上,枚举类使用在RPC接口上,必然要考虑序列化和反序列...

无标题 ⋅ 2016/05/22 ⋅ 0

牛顿法 Newton Method

今天我们要介绍一种收敛速度更快的算法:Newton Method(或者叫 Newton’s Method)。 可能大家知道有两个算法同时叫做牛顿法,一个是用迭代法来求方程的根的方法,另一个是 optimization 里...

GarfieldEr007 ⋅ 2016/01/09 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Linux中的端口大全

1 被LANA定义的端口 端口 名称 描述 1 tcpmux TCP 端口服务多路复用 5 rje 远程作业入口 7 echo Echo 服务 9 discard 用于连接测试的空服务 11 systat 用于列举连接了的端口的系统状态 13 d...

寰宇01 ⋅ 11分钟前 ⋅ 0

Confluence 6 如何备份存储文件和页面信息

备份的 ZIP 文件包含有 entities.xml,这个 XML 文件包含有 Confluence 的所有页面内容和存储附件的目录。 备份 Zip 文件结构 页面的附件是存储在附件存储目录中的,通过页面和附件 ID 进行识...

honeymose ⋅ 14分钟前 ⋅ 0

【每天一个JQuery特效】根据状态确定是否滑入或滑出被选元素

主要效果: 本文主要采用slideToggle()方法实现以一行代码同时实现以展开或收缩的方式显示或隐藏被选元素。 主要代码如下: <!DOCTYPE html><html><head><meta charset="UTF-8">...

Rhymo-Wu ⋅ 18分钟前 ⋅ 0

度量.net framework 迁移到.net core的工作量

把现有的.net framework程序迁移到.net core上,是一个非常复杂的工作,特别是一些API在两个平台上还不能同时支持。两个类库的差异性,通过人工很难识别全。好在微软的工程师们考虑到了我们顾...

李朝强 ⋅ 23分钟前 ⋅ 0

请不要在“微服务”的狂热中迷失自我!

微服务在过去几年一直是一个非常热门的话题(附录1)。何为“微服务的疯狂”,举个例子: 众所周知,Netflix在DevOps上的表现非常棒。Netfix可以做微服务。因此:如果我做微服务,我也将非常...

harries ⋅ 24分钟前 ⋅ 0

oAuth2 升级Spring Cloud Finchley.RELEASE踩坑分享

背景 6.19号,spring团队发布了期待已久的 Spring Cloud Finchley.RELEASE 版本。 重要变化: 基于Spring Boot 2.0.X 不兼容 Spring Boot 1.5.X 期间踩过几个坑,分享出来给大伙,主要是关于...

冷冷gg ⋅ 54分钟前 ⋅ 0

OSChina 周一乱弹 —— 理发师小姐姐的魔法

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @冰冰棒- :分享田馥甄的单曲《My Love》 《My Love》- 田馥甄 手机党少年们想听歌,请使劲儿戳(这里) @Li-Wang :哎,头发又长了。。。又要...

小小编辑 ⋅ 今天 ⋅ 8

Kafka1.0.X_消费者API详解2

偏移量由消费者管理 kafka Consumer Api还提供了自己存储offset的功能,将offset和data做到原子性,可以让消费具有Exactly Once 的语义,比kafka默认的At-least Once更强大 消费者从指定分区...

特拉仔 ⋅ 今天 ⋅ 0

NEO智能合约之发布和升级(二)

接NEO智能合约之发布和升级(一),我们接下来说说智能合约的升级功能。 一 准备工作 合约的升级需要在合约内预先设置好升级接口,以方便在升级时调用。接下来我们对NEO智能合约之发布和升级...

红烧飞鱼 ⋅ 今天 ⋅ 0

个人博客的运营模式能否学习TMALL天猫质量为上?

心情随笔|个人博客的运营模式能否学习TMALL天猫质量为上? 中国的互联网已经发展了很多年了,记得在十年前,个人博客十分流行,大量的人都在写博客,而且质量还不错,很多高质量的文章都是在...

原创小博客 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部