Hessian RPC示例和基于Http请求的Hessian序列化对象传输

简介:

本文主要介绍两个案例,第一个是使用Hessian来实现远程过程调用,第二个是通过Hessian提供的二进制RPC协议进行和Servlet进行数据交互,Hessian本身即是基于Http的RPC实现。


  案例一:

  1.准备工作

    这里建立一个Maven项目,其中包含四个模块,父模块(仅用来聚合其它模块,不做实际使用),服务器端模块,客户端模块,API模块(远程过程接口,供服务器和客户端使用)。目录结构见下图:

    wKiom1SwmzSRUDR1AAHOPhvwsXI742.jpg

 2.添加Hessian的依赖

   由于客户端和服务器都要依赖Hessian的包,这里可以添加到父模块的pom.xml中去。

   

1
2
3
4
5
< dependency >
             < groupId >com.caucho</ groupId >
             < artifactId >hessian</ artifactId >
             < version >4.0.38</ version >
         </ dependency >


   关于其它的具体依赖配置,这里不做多余展示,本文末尾附有完整的下载地址。


 3.在hessian-api模块定义过程接口

   

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package  secondriver.hessian.api;
 
import  java.io.InputStream;
import  java.util.List;
 
import  secondriver.hessian.api.bean.Person;
 
public  interface  HelloHessian {
 
     public  String sayHello();
 
     public  String sayHello(String name);
 
     public  List<Person> getPersons();
 
     public  Person getPersonById( int  id);
 
     public  boolean  uploadFile(String fileName, InputStream data);
 
     public  byte [] downloadFile(String fileName);
}

   

  上面的接口中定义的六个方法,有重载方法,有获取集合,有获取对象,有上传文件,下载文件等。需要注意的是在Hessian中定义过程接口的方法时输入输出流对象参数应该放置到方法的最后一个参数。另外在实际操作中发现对于流的处理通过远程调用还是会出现一个莫名其妙的问题,加上Hessian缺乏完整的文档(除Hessian的序列号协议)。


  关于下载文件这个方法的定义使用了返回byte数组,这将受限与虚拟机的内存或其他因素。网上有说关于文件下载Hessian不支持(未能证实。引申案例二的方式,传输二进制就可以实现下载,不过这将与Hessian的RPC实现无关,而是应用到Hessian的序列号协议),这个可以算是一种策略,So,关于远程文件下载,谁会使用这样的方式呢?!!


 4.服务器端实现具体功能


   服务器端实现接口

   

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package  secondriver.hessian.server.bo;
 
import  java.io.File;
import  java.io.FileInputStream;
import  java.io.FileNotFoundException;
import  java.io.IOException;
import  java.io.InputStream;
import  java.util.Arrays;
import  java.util.Calendar;
import  java.util.Date;
import  java.util.List;
import  java.util.Random;
 
import  org.apache.commons.io.FileUtils;
import  org.apache.commons.io.IOUtils;
 
import  secondriver.hessian.api.HelloHessian;
import  secondriver.hessian.api.bean.Person;
 
public  class  HelloHessianImpl  implements  HelloHessian {
 
     private  static  Person[] persons =  new  Person[ 5 ];
 
     static  {
         Random random =  new  Random();
         for  ( int  i =  0 , l = persons.length; i < l; i++) {
             persons[i] =  new  Person();
             persons[i].setId(i);
             persons[i].setGender(random.nextBoolean());
             persons[i].setName( "name-"  + i);
             persons[i].setPhone(random.nextLong());
             persons[i].setHeight(random.nextDouble());
             persons[i].setWeight(random.nextFloat());
             persons[i].setAddress( new  String[] {  "Address"  + random.nextInt(),
                     "Address"  + random.nextInt() });
 
             Calendar c = Calendar.getInstance();
             c.set(Calendar.DATE, i +  1 );
             persons[i].setBrithday(c.getTime());
         }
     }
 
     @Override
     public  String sayHello() {
         return  "Hello Hession "  new  Date().toString();
     }
 
     @Override
     public  String sayHello(String name) {
         return  "Welcome "  + name;
     }
 
     @Override
     public  List<Person> getPersons() {
         return  Arrays.asList(persons);
     }
 
     @Override
     public  Person getPersonById( int  id) {
         for  (Person p : persons) {
             if  (p.getId() == id) {
                 return  p;
             }
         }
         return  null ;
     }
 
     @Override
     public  boolean  uploadFile(String fileName, InputStream data) {
         List<String> temp;
         try  {
             temp = IOUtils.readLines(data);
             String filePath = System.getProperty( "user.dir" ) +  "/temp/"
                     + fileName;
             FileUtils.writeLines( new  File(filePath), temp);
             System.out.println( "Upload file to "  + filePath);
             return  true ;
         catch  (IOException e) {
             e.printStackTrace();
             return  false ;
         }
     }
 
     @Override
     public  byte [] downloadFile(String fileName) {
         String filePath = System.getProperty( "user.dir" ) +  "/temp/"  + fileName;
         InputStream data =  null ;
         try  {
             data =  new  FileInputStream(filePath);
             int  size = data.available();
             byte [] buffer =  new  byte [size];
             IOUtils.read(data, buffer);
             return  buffer;
         catch  (FileNotFoundException e) {
             e.printStackTrace();
             return  null ;
         catch  (IOException e) {
             e.printStackTrace();
             return  null ;
         finally  {
             IOUtils.closeQuietly(data);
         }
     }
}


 5.服务器端配置远程服务地址


   Hessian是Remote On Http工具,服务端是典型的Java Web应用,我们需要在web.xml中配置服务的请求地址。

  

1
2
3
4
5
6
7
8
9
10
11
12
< servlet >
         < servlet-name >HelloHessian</ servlet-name >
         < servlet-class >com.caucho.hessian.server.HessianServlet</ servlet-class > <!-- RPC HessianServlet处理类 -->
         < init-param >
             < param-name >home-class</ param-name > <!-- 远程服务实现类 -->
             < param-value >secondriver.hessian.server.bo.HelloHessianImpl</ param-value >
         </ init-param >
         < init-param >
             < param-name >home-api</ param-name > <!-- 远程服务接口 -->
             < param-value >secondriver.hessian.api.HelloHessian</ param-value >
         </ init-param >
     </ servlet >


 6.客户端调用远程对象方法

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package  secondriver.hessian.client;
 
import  java.io.BufferedInputStream;
import  java.io.FileInputStream;
import  java.io.FileNotFoundException;
import  java.io.FileWriter;
import  java.io.IOException;
import  java.io.InputStream;
import  java.net.MalformedURLException;
import  java.util.List;
 
import  org.apache.commons.io.IOUtils;
 
import  secondriver.hessian.api.HelloHessian;
import  secondriver.hessian.api.bean.Person;
 
import  com.caucho.hessian.client.HessianProxyFactory;
 
/**
  * Hessian RPC
  */
public  class  HelloHessianClient {
 
     public  static  String urlName =  "http://localhost:8080/hessian-server/HelloHessian" ;
 
     public  static  void  main(String[] args)  throws  MalformedURLException {
 
         HessianProxyFactory factory =  new  HessianProxyFactory();
         // 开启方法重载
         factory.setOverloadEnabled( true );
 
         HelloHessian helloHession = (HelloHessian) factory.create(
                 HelloHessian. class , urlName);
 
         // 调用方法
         System.out.println( "call sayHello():"  + helloHession.sayHello());
         System.out.println( "call sayHello(\"Tom\"):"
                 + helloHession.sayHello( "Tom" ));
         System.out.println( "call getPersons():" );
 
         // 调用方法获取集合对象
         List<Person> persons = helloHession.getPersons();
         if  ( null  != persons && persons.size() >  0 ) {
             for  (Person p : persons) {
                 System.out.println(p.toString());
             }
         else  {
             System.out.println( "No person." );
         }
 
         // 通过参数调用方法获取对象
         int  id =  2 ;
         System.out.println(String.format( "call getPersonById(%d)" , id));
         Person person = helloHession.getPersonById(id);
         if  ( null  != person) {
             System.out.println(person.toString());
         else  {
             System.out.println( "Id is "  + id +  " person not exist." );
         }
 
         // 上传文件
         String fileName =  "upload.txt" ;
         String filePath = System.getProperty( "user.dir" ) +  "/temp/"  + fileName;
         InputStream data =  null ;
         try  {
             data =  new  BufferedInputStream( new  FileInputStream(filePath));
             if  (helloHession.uploadFile(fileName, data)) {
                 System.out.println( "Upload file "  + filePath +  " succeed." );
             else  {
                 System.out.println( "Upload file "  + filePath +  " failed." );
             }
         catch  (FileNotFoundException e) {
             e.printStackTrace();
         finally  {
             IOUtils.closeQuietly(data);
         }
 
         // 下载文件
         fileName =  "download.txt" ;
         filePath = System.getProperty( "user.dir" ) +  "/temp/"  + fileName;
         try  {
 
             byte [] temp = helloHession.downloadFile(fileName);
             if  ( null  != temp) {
                 FileWriter output =  new  FileWriter(filePath);
                 IOUtils.write(temp, output,  "UTF-8" );
                 System.out.println( "Download file "  + filePath +  " succeed." );
                 output.close();
             else  {
                 System.out.println( "Download file "  + filePath +  " failed." );
             }
         catch  (IOException e) {
             e.printStackTrace();
         }
     }
}


  通过客户端调用可以看出,远程方法的具体实现对于客户端来说是透明的。


7.运行程序

  7.1启动服务器

  7.2运行客户端程序

     客户端请求远程服务对象方法调用结果:

1
2
3
4
5
6
7
8
9
call sayHello():Hello Hession Sat Jan 10 12:31:21 CST 2015
call sayHello("Tom"):Welcome Tom
call getPersons():
Person [id=0, gender=true, name=name-0, brithday=Thu Jan 01 12:31:21 CST 2015, height=0.04756537639163749, weight=0.24559122, phone=1359341198036930408, address=[Address2145973789, Address486043733]]
....省略
call getPersonById(2)
Person [id=2, gender=false, name=name-2, brithday=Sat Jan 03 12:31:21 CST 2015, height=0.07169451440704722, weight=0.8515671, phone=2732762596557481818, address=[Address8744397, Address-1690316438]]
Upload file F:\__eclipse\tomsworkspace\hessian-L-parent\hessian-client/temp/upload.txt succeed.
Download file F:\__eclipse\tomsworkspace\hessian-L-parent\hessian-client/temp/download.txt succeed.

  


案例二:


1. 客户端通过Hessian序列号协议序列化对象,通过Http Post方式提交到服务器端,然后通过反序列化服务器端响应数据。

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package  secondriver.hessian.data;
 
import  java.io.ByteArrayOutputStream;
import  java.io.InputStream;
import  java.util.Date;
 
import  org.apache.http.HttpEntity;
import  org.apache.http.client.methods.CloseableHttpResponse;
import  org.apache.http.client.methods.HttpPost;
import  org.apache.http.entity.ByteArrayEntity;
import  org.apache.http.entity.ContentType;
import  org.apache.http.impl.client.CloseableHttpClient;
import  org.apache.http.impl.client.HttpClients;
 
import  secondriver.hessian.api.bean.Person;
 
import  com.caucho.hessian.io.Hessian2Input;
import  com.caucho.hessian.io.Hessian2Output;
 
public  class  Test3 {
 
     public  static  String urlName =  "http://localhost:8080/hessian-server/PostDataServlet" ;
 
     public  static  void  main(String[] args)  throws  Throwable {
 
         // 序列化
         ByteArrayOutputStream os =  new  ByteArrayOutputStream();
         Hessian2Output h2o =  new  Hessian2Output(os);
 
         h2o.startMessage();
         h2o.writeObject(getPerson());
         h2o.writeString( "I am client." );
         h2o.completeMessage();
         h2o.close();
 
         byte [] buffer = os.toByteArray();
         os.close();
 
         ByteArrayEntity byteArrayEntity =  new  ByteArrayEntity(buffer,
                 ContentType.create( "x-application/hessian" "UTF-8" ));
         
         CloseableHttpClient client = HttpClients.createDefault();
         HttpPost post =  new  HttpPost(urlName);
         post.setEntity(byteArrayEntity);
         CloseableHttpResponse response = client.execute(post);
 
         System.out.println( "response status:\n"
                 + response.getStatusLine().getStatusCode());
         HttpEntity body = response.getEntity();
         InputStream is = body.getContent();
         Hessian2Input h2i =  new  Hessian2Input(is);
         h2i.startMessage();
 
         Person person = (Person) h2i.readObject();
         System.out.println( "response:\n"  + person.toString());
         System.out.println(h2i.readString());
 
         h2i.completeMessage();
         h2i.close();
         is.close();
     }
 
     public  static  Person getPerson() {
         Person person =  new  Person();
         person.setAddress( new  String[] {  "Beijing" "TaiWan" "GuangZhou"  });
         person.setBrithday( new  Date());
         person.setGender( false );
         person.setHeight( 168 .5D);
         person.setId( 300 );
         person.setName( "Jack" );
         person.setPhone( 188888888 );
         person.setWeight( 55 .2F);
         return  person;
     }
}


2.服务器端通过Hessian序列化协议反序列化对象,通过HttpServletResponse对请求进行响应,响应数据是Hessian序列化的二进制结果。

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package  secondriver.hessian.server.servlet;
 
import  java.io.ByteArrayOutputStream;
import  java.io.IOException;
import  java.util.Date;
 
import  javax.servlet.ServletException;
import  javax.servlet.ServletInputStream;
import  javax.servlet.ServletOutputStream;
import  javax.servlet.http.HttpServlet;
import  javax.servlet.http.HttpServletRequest;
import  javax.servlet.http.HttpServletResponse;
 
import  secondriver.hessian.api.bean.Person;
 
import  com.caucho.hessian.io.Hessian2Input;
import  com.caucho.hessian.io.Hessian2Output;
 
public  class  PostDataServlet  extends  HttpServlet {
 
     private  static  final  long  serialVersionUID = -4461061053732328507L;
 
     @Override
     protected  void  doPost(HttpServletRequest req, HttpServletResponse resp)
             throws  ServletException, IOException {
         // 处理请求
         ServletInputStream sis = req.getInputStream();
         Hessian2Input h2i =  new  Hessian2Input(sis);
 
         h2i.startMessage();
         Person person = (Person) h2i.readObject();
         System.out.println( "receive:\n"  + person.toString());
         System.out.println(h2i.readString());
         h2i.completeMessage();
         h2i.close();
         sis.close();
 
         // 发送响应
         resp.setCharacterEncoding( "UTF-8" );
         resp.setContentType( "x-application/hessian" );
 
         ByteArrayOutputStream bos =  new  ByteArrayOutputStream();
         Hessian2Output h2o =  new  Hessian2Output(bos);
 
         h2o.startMessage();
         h2o.writeObject(getPerson());
         h2o.writeString( "I am server." );
         h2o.completeMessage();
         h2o.close();
 
         ServletOutputStream sos = resp.getOutputStream();
         sos.write(bos.toByteArray());
         sos.flush();
         bos.close();
         sos.close();
 
     }
 
     public  static  Person getPerson() {
         Person person =  new  Person();
         person.setAddress( new  String[] {  "ShangHai" "ShenZhen" "ChengDu"  });
         person.setBrithday( new  Date());
         person.setGender( true );
         person.setHeight( 178 .5D);
         person.setId( 301 );
         person.setName( "Tom" );
         person.setPhone( 188218888 );
         person.setWeight( 55 .2F);
         return  person;
     }
}

 

3.在web.xml中配置PostDataServlet,此处略去具体配置信息,可以参考上面HelloHessian Servlet配置。

4.运行程序

  4.1启动服务器

  4.2运行客户端程序Test3.

  

  运行结果:

  服务器端输出:

  

1
2
3
4
[INFO] Restart completed at Sat Jan 10 12:34:51 CST 2015
receive:
Person [id=300, gender=false, name=Jack, brithday=Sat Jan 10 12:35:03 CST 2015, height=168.5, weight=55.2, phone=188888888, address=[Beijing, TaiWan, GuangZhou]]
I am client.

  

  客户端输出:

1
2
3
4
5
response status:
200
response:
Person [id= 301 , gender= true , name=Tom, brithday=Sat Jan  10  12 : 35 : 03  CST  2015 , height= 178.5 , weight= 55.2 , phone= 188218888 , address=[ShangHai, ShenZhen, ChengDu]]
I am server.


  分析输出结果可见Hessian序列化对象通过Http的方式传输,并成功反序列化。

  关于Hessian序列号协议可以参见:http://hessian.caucho.com/doc/hessian-serialization.html


 

  写在最后:Hessian的相关资料网上还是比较少,而且例子不尽相同,而且官方文档错误之处清晰可见(仔细阅读方可一览无余),本文从Hessian的RPC使用和Hessian序列化和反序列化对象两个方面提供了示例,完整示例下载地址可见本文附件:HessionRPC示例(Eclipse Luna版工程,需要Mavn支持),另外带有源码的Hessian的API文档下载地址:http://down.51cto.com/data/1973896 。


  一次不太愉快的Hessian体验使得对RPC的理解更加深刻,无论何种框架,对象的序列化和反序列化,数据的传输协议都是实现RPC的工作重点。



本文转自 secondriver 51CTO博客,原文链接:http://blog.51cto.com/aiilive/1601574,如需转载请自行联系原作者

相关文章
|
11月前
|
XML JSON API
识别这些API接口定义(http,https,api,RPC,webservice,Restful api ,OpenAPI)
本内容介绍了API相关的术语分类,包括传输协议(HTTP/HTTPS)、接口风格(RESTful、WebService、RPC)及开放程度(API、OpenAPI),帮助理解各类API的特点与应用场景。
|
Dubbo 安全 应用服务中间件
Apache Dubbo 正式发布 HTTP/3 版本 RPC 协议,弱网效率提升 6 倍
在 Apache Dubbo 3.3.0 版本之后,官方推出了全新升级的 Triple X 协议,全面支持 HTTP/1、HTTP/2 和 HTTP/3 协议。本文将围绕 Triple 协议对 HTTP/3 的支持进行详细阐述,包括其设计目标、实际应用案例、性能测试结果以及源码架构分析等内容。
1324 128
|
XML JSON 网络协议
利用HTTP POST协议实现简单的RPC协议:WireShark抓包分析
通过这种方式,我们可以使用HTTP POST实现简单的RPC协议,并使用WireShark进行抓包分析。这不仅可以帮助我们理解RPC协议的工作原理,也可以帮助我们调试和优化我们的代码。
751 30
|
JSON 数据格式
利用HTTP POST协议实现简单的RPC协议,并使用WireShark进行抓包分析
通过这种方式,我们可以利用HTTP POST实现简单的RPC协议,并使用WireShark进行抓包分析。这种方式简单易懂,实用性强,可以应用于各种网络编程场景。
625 16
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第22天】在Java的世界里,对象序列化和反序列化是数据持久化和网络传输的关键技术。本文将带你了解如何在Java中实现对象的序列化与反序列化,并探讨其背后的原理。通过实际代码示例,我们将一步步展示如何将复杂数据结构转换为字节流,以及如何将这些字节流还原为Java对象。文章还将讨论在使用序列化时应注意的安全性问题,以确保你的应用程序既高效又安全。
|
JSON 前端开发 数据格式
前端的全栈之路Meteor篇(五):自定义对象序列化的EJSON介绍 - 跨设备的对象传输
EJSON是Meteor框架中扩展了标准JSON的库,支持更多数据类型如`Date`、`Binary`等。它提供了序列化和反序列化功能,使客户端和服务器之间的复杂数据传输更加便捷高效。EJSON还支持自定义对象的定义和传输,通过`EJSON.addType`注册自定义类型,确保数据在两端无缝传递。
389 1
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第3天】在Java编程的世界里,对象序列化与反序列化是实现数据持久化和网络传输的关键技术。本文将深入探讨Java序列化的原理、应用场景以及如何通过代码示例实现对象的序列化与反序列化过程。从基础概念到实践操作,我们将一步步揭示这一技术的魅力所在。
|
存储 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第9天】在Java的世界里,对象序列化是连接数据持久化与网络通信的桥梁。本文将深入探讨Java对象序列化的机制、实践方法及反序列化过程,通过代码示例揭示其背后的原理。从基础概念到高级应用,我们将一步步揭开序列化技术的神秘面纱,让读者能够掌握这一强大工具,以应对数据存储和传输的挑战。
|
存储 缓存 NoSQL
一篇搞懂!Java对象序列化与反序列化的底层逻辑
本文介绍了Java中的序列化与反序列化,包括基本概念、应用场景、实现方式及注意事项。序列化是将对象转换为字节流,便于存储和传输;反序列化则是将字节流还原为对象。文中详细讲解了实现序列化的步骤,以及常见的反序列化失败原因和最佳实践。通过实例和代码示例,帮助读者更好地理解和应用这一重要技术。
994 0
|
负载均衡 Java 开发者
Spring Cloud 远程调用:为何选择 HTTP 而非 RPC?
【10月更文挑战第1天】在微服务架构中,远程服务调用是一个核心环节。面对HTTP和RPC(Remote Procedure Call,远程过程调用)这两种通信协议,Spring Cloud 选择了HTTP作为其主要通信手段。本文将深入探讨Spring Cloud选择HTTP而非RPC的原因,以及这一选择在实际工作中的优势。
635 0