为什么学习Hessian RPC?
- 存粹的RPC,只解决PRC的四个核心问题(1.网络通信2.协议 3.序列化 4.代理)
- Java写的
- HessianRPC落伍了,但是它的序列化方式还保存着,被Dubbo(Hessian Lite)使用。
被落伍,只是因为Hessian RPC出的比较早,当时没有注册中心,熔断,限流等。所以当我们学习PRC时,最好学一个简单的,存粹的,没有其他的额外功能。
Hessian 的概念
Hession 是Resin服务器的伴生产品。基于Java编程语言设计的RPC框架,只支持Java编程语言使用,现在新出的gRPC Thrift,支持多语言。Hessian的序列化是二进制的。
Hessian设计思想
Hessian使用的resin服务器,类似于Tomcat,使用的是HTTP协议,由于使用的是JDK做代理,所以必须要用接口定义调用类。对象的数据类型,必须要实现Serliazble。服务的发布需要使用HessanServlet进行配置,在web.xml进行配置。
那么如何进行调用配置的服务,客户端进行代理,HessianProxyFactory做代理服务。
RPC的开发
引入依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.9</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.38</version>
</dependency>
</dependencies>
开发服务端
1. 开发Service
2. DAO --- Mybaits
注意:一定要定义Service的接口,自定义的数据类型实现Serliazliable
模拟一下登录功能:
实体类
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private String password;
}
Service
public interface UserService {
public boolean login(String username,String password);
public void register(User user);
}
ServrceImpl
@Slf4j
public class UserServiceImpl implements UserService {
@Override
public boolean login(String username, String password) {
log.debug("login method invoke name {} password {}",username,password);
return false;
}
@Override
public void register(User user) {
log.debug("register method invoke user {}",user);
}
}
配置web.xml
<servlet>
<servlet-name>userServiceRPC</servlet-name>
<servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>
<init-param>
<param-name>home-api</param-name>
<param-value>com.liu.service.UserService</param-value>
</init-param>
<init-param>
<param-name>home-class</param-name>
<param-value>com.liu.service.UserServiceImpl</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>userServiceRPC</servlet-name>
<url-pattern>/userServiceRPC</url-pattern>
</servlet-mapping>
客户端开发
@Slf4j
public class HessianRPCClient {
public static void main(String[] args) throws MalformedURLException {
// TODO
HessianProxyFactory factory = new HessianProxyFactory();
String urlName = "http://localhost:8080/rpc_hessian/userServiceRPC";
UserService userService = (UserService) factory.create(UserService.class, urlName);
boolean login = userService.login("liu", "123456");
log.debug("userService value is {}",login);
}
}
HessianRPC核心源码分析
1. HessianRPC client创建代理的方式 JDK Proxy.newProxyInstance()
public Object create(Class<?> api, URL url, ClassLoader loader){
if (api == null)
throw new NullPointerException("api must not be null for HessianProxyFactory.create()");
InvocationHandler handler = null;
handler = new HessianProxy(url, this, api);
return Proxy.newProxyInstance(loader,
new Class[] { api,
HessianRemoteObject.class },
handler);
}
2. 代理中
通过网络 Http请求 连接 远端RPC服务
通过流 数据写出去了。
HessianProxy#invoke()
1.通过 URLConnection 进行网络连接
2.解析协议 传递数据
Hessian序列化
前面说了,Hessian已经落伍了,但他的序列化方式还保留着,我们看看怎么使用的。
// 1. 序列化
OutputStream outputStream = new FileOutputStream("D:\\StudyCodes\\rpc_lession\\rpc_hessian\\test");
Hessian2Output hessian2Output = new Hessian2Output(outputStream);
hessian2Output.writeObject(new User("liu", "123456"));
hessian2Output.flush();
outputStream.close();
// 2. 反序列化
InputStream inputStream = new FileInputStream("D:\\StudyCodes\\rpc_lession\\rpc_hessian\\test");
Hessian2Input hessian2Input = new Hessian2Input(inputStream);
User user = (User) hessian2Input.readObject();
System.out.println(user);
inputStream.close();
log.info("user: {}", user);