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,如需转载请自行联系原作者

相关文章
|
6月前
|
JSON 监控 API
掌握使用 requests 库发送各种 HTTP 请求和处理 API 响应
本课程全面讲解了使用 Python 的 requests 库进行 API 请求与响应处理,内容涵盖环境搭建、GET 与 POST 请求、参数传递、错误处理、请求头设置及实战项目开发。通过实例教学,学员可掌握基础到高级技巧,并完成天气查询应用等实际项目,适合初学者快速上手网络编程与 API 调用。
660 130
|
7月前
|
XML JSON API
识别这些API接口定义(http,https,api,RPC,webservice,Restful api ,OpenAPI)
本内容介绍了API相关的术语分类,包括传输协议(HTTP/HTTPS)、接口风格(RESTful、WebService、RPC)及开放程度(API、OpenAPI),帮助理解各类API的特点与应用场景。
|
7月前
HTTP协议中请求方式GET 与 POST 什么区别 ?
GET和POST的主要区别在于参数传递方式、安全性和应用场景。GET通过URL传递参数,长度受限且安全性较低,适合获取数据;而POST通过请求体传递参数,安全性更高,适合提交数据。
685 2
|
8月前
|
Go 定位技术
Golang中设置HTTP请求代理的策略
在实际应用中,可能还需要处理代理服务器的连接稳定性、响应时间、以及错误处理等。因此,建议在使用代理时增加适当的错误重试机制,以确保网络请求的健壮性。此外,由于网络编程涉及的细节较多,彻底测试以确认代理配置符合预期的行为也是十分重要的。
323 8
|
7月前
|
JSON JavaScript API
Python模拟HTTP请求实现APP自动签到
Python模拟HTTP请求实现APP自动签到
|
7月前
|
数据采集 JSON Go
Go语言实战案例:实现HTTP客户端请求并解析响应
本文是 Go 网络与并发实战系列的第 2 篇,详细介绍如何使用 Go 构建 HTTP 客户端,涵盖请求发送、响应解析、错误处理、Header 与 Body 提取等流程,并通过实战代码演示如何并发请求多个 URL,适合希望掌握 Go 网络编程基础的开发者。
|
8月前
|
缓存 JavaScript 前端开发
Vue 3 HTTP请求封装导致响应结果无法在浏览器中获取,尽管实际请求已成功。
通过逐项检查和调试,最终可以定位问题所在,修复后便能正常在浏览器中获取响应结果。
318 0
|
8月前
|
Go
如何在Go语言的HTTP请求中设置使用代理服务器
当使用特定的代理时,在某些情况下可能需要认证信息,认证信息可以在代理URL中提供,格式通常是:
559 0
|
SQL Web App开发 前端开发
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
在运行一个group by的sql时,抛出以下错误信息: Task with the most failures(4):  -----Task ID:  task_201411191723_723592_m_000004URL:  http://DDS0204.
1137 0
|
Web App开发 前端开发 Java
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
ZooKeeper 保证了数据的强一致性,  zk集群中任意节点(一个zkServer)上的相同znode下的数据一定是相同的。
919 0