什么是RPC? 它是干什么的? 有什么用? 怎么用?
RPC概念
RPC(remote Procedure Call)
, 远程过程调用. 来实现不同机器上的系统之间的调用. 就像访问本地资源一样, 通过网络传输访问远端系统资源.
RPC架构实现原理
架构中的几个重点
-
Client Code:
- 客户端代码, 负责发起RPC调用, 为调用方用户提供使用API.
-
Serialization/Deserialization:
- 负责对RPC调用通过网络传输的内容进行序列化反/序列化, 不同的RPC有着不同的实现机制. 主要分为文本与二进制两大类. 文本类别主要有xml和json两种格式.
-
Sub Proxy:
- 可以看作一种代理对象, 屏蔽RPC调用过程中复杂的网络处理逻辑, 使得RPC调用透明化, 能够保持与本地调用风格一致
-
Transport:
- 作为RPC框架底层的通信传输模块, 一般通过Socket在客户端与服务端之间传递请求与应答消息
-
Server Code:
- 服务端业务逻辑具体实现.
抛转引玉 - RMI
Java RMI(Remote Method Invocation)
基于java的远程方法调用技术, 是java特有的一种RPC实现. 它能使不同主机上的java对象进行通信及方法调用.
RMI 代码示例
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface HelloService extends Remote {
String sayHello(String someone) throws RemoteException;
}
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class HelloServiceImpl extends UnicastRemoteObject implements HelloService {
protected HelloServiceImpl() throws RemoteException {
super();
}
@Override
public String sayHello(String someone) throws RemoteException {
System.out.println("sayHello inpute someone : " + someone);
return "hello : " + someone;
}
}
RMI 穿透防火墙
RMI的通信端口是随机产生的, 因此有可能会被防火墙拦截. 为防止防火墙拦截, 需要强制指定RMI的通信端口 示例:MySocketFactory.java
public class MySocketFactory extends RMISocketFactory {
@Override
public Socket createSocket(String host, int port) throws IOException {
return new Socket(host, port);
}
@Override
public ServerSocket createServerSocket(int port) throws IOException {
if (0 == port) {
port = 8802;
}
System.out.println("rmi nofity port: " + port);
return new ServerSocket(port);
}
}
public class ClientMain {
public static void main(String[] args) throws Exception {
HelloService helloService = (HelloService) Naming.lookup("rmi://localhost:8801/helloService");
for (int i = 1; i < 9; i++) {
System.out.println("RMI 服务调用返回结果: " + helloService.sayHello("hahahh" + i));
}
}
}
public class ServerMain {
public static void main(String[] args) throws Exception {
LocateRegistry.createRegistry(8801);
//指定通讯端口, 防止被防火墙拦截
RMISocketFactory.setSocketFactory(new MySocketFactory());
HelloService helloService = new HelloServiceImpl();
Naming.bind("rmi://localhost:8801/helloService", helloService);
System.out.println("ServerMain provide RPC service now.");
}
}