再说RPC之前,先说一下同类的通信方式。
从单机走向分布式,产生了很多分布式的通信方式。
- 最古老也是最有效,且永远不过时的,TCP/UDP的二进制传输。事实上所有的通信方式归根结底都是TCP/UDP。
- CORBA Common Object Request Broker Architecture。古老而复杂,支持面向对象的通信协议
- Web Service(SOA SOAP RDDI WSDL...协议):基于http + xml的标准化Web API。
XML指的是接口文档以XML的形式进行展现,http指的是通信协议。
- RestFul(Representational State Transfer):回归简单化本源的Web API的事实标准 http://mashibing.com/product/java
通信协议是http,以json方式进行数据传出。
- RMI Remote Method Invocation:java内部的分布式通信协议,就是Java内部封装的RPC,不过名称叫做RMI摆了。
- JMS Java Message Service:JavaEE中的消息框架标准,为很多MQ所支持。
- RPC(Remote Procedure Call):远程过程调用,就是调用远程的方法。
远程方法调用,这只是一个统称,重点在于方法调用(不支持对象的概念),具体实现甚至可以用RMI RestFul等去实现,但一般不用,因为RMI不能跨语言,而RestFul传输效率过低。
RPC多用于服务器集群间的通信,因此常使用更加有效、短小精悍的传出模式以提高效率,如Thrift,Hessian,gooogle protobuf。
RPC根据序列化方式的不同,出现了多样的RPC序列化框架
注意RPC序列化框架并不是RPC框架,RPC框架还包括服务发现,服务注册、服务治理等。。。
- java.io.Serializable
java原生序列化,不好,序列化时间长,且序列化后的字节也非常大。
- Hessian
- google protobuf
- facebook Thrift
- kyro
- fst
- json序列化框架
1 jackson
2 google gson
3 Ali FastJson
- xmlrpc(stream)
RPC通信协议
- http
- http2.0(gRPC)
- TCP
同步/异步/阻塞/非阻塞
- WebService
根据序列化方式和RPC通信协议方式的组合,可以产生多样的RPC使用方式。
分布式通信最基本最核心的部分:两台/多台服务器之间的二进制数据的传出。
以下是单机的二进制数据传送与接收实例。
公共部分代码:
public interface IUserService {
public User findUserById(Integer id);
}
public class User implements Serializable {
private static final long serialVersionUID = 1;
private Integer id;
private String name;
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
服务端代码:
public class Server {
private static boolean running = true;
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(8888);
while(running){
Socket s = ss.accept();
process(s);
s.close();
}
ss.close();
}
private static void process(Socket s)throws Exception{
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
DataInputStream dis = new DataInputStream(in);
DataOutputStream dos = new DataOutputStream(out);
/*
* 获取客户端的数据
* */
int id = dis.readInt();
IUserService service = new UserServiceImpl();
/*
* 如何将对象写入到客户端呢?
* 把对象的基本类型写到客户端。
* */
User user = service.findUserById(id);
dos.writeInt(user.getId());
dos.writeUTF(user.getName());
dos.flush();
}
}
User实现类
public class UserServiceImpl implements IUserService {
@Override
public User findUserById(Integer id) {
return new User(id,"hello world");
}
}
客户端代码:
/*
向外写的过程:
客户端的数据需要转换成二进制的过程,
通过DataOutputStream的写入操作将123数值转成二进制的表示,然后
通过调用toByteArray方式转成字节数组.
*/
public class client {
public static void main(String[] args) throws Exception {
Socket s = new Socket("127.0.0.1",8888);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeInt(123);
s.getOutputStream().write(baos.toByteArray());
s.getOutputStream().flush();
/*上方的操作进行写出操作,下方读取服务器返回的数据,然后
* 将基本类型进行拼接成User对象*/
DataInputStream dis = new DataInputStream(s.getInputStream());
int id = dis.readInt();
String name = dis.readUTF();
User user = new User(id,name);
System.out.println(user);
dos.close();
dis.close();
}
}
笔记参考:Java讲解RPC的基本实现
这里仅是粗浅理解性,对于RPC而言,其中动态代理扮演了比较重要的角色。