RPC
定义
RPC, Remote Process Call, 远程方法调用.
RPC 将本地调用转变为远程服务器上的调用,给系统的处理能力和吞吐量无限制提升提供了可能,是实现分布式计算的基础。
服务 Provider
扩容、服务隔离与分组 => 服务路由和负载均衡(router & load balance)
服务 consumer 通过 服务 provider的分组信息和地址信息进行路由,通常服务 provider 是一个集群,需要采用一定负载均衡策略,选取其中一台进行调用。
对象的序列化
序列化与反序列化
序列化: 将对象转化为二进制流的过程。
反序列化: 将二进制流转化为对象的过程。
常用的解决方案
Java build-in、Google's Protocal Buffers、Hessian; JSON, XML。
- Protocal Buffers: 高性能,跨平台;需要编写 proto 文件,无法直接使用 Java 对象。
- Hessian: 对各种语言的支持,性能稳定。
- Java build-in: 无需引入第三方 jar、简单;但是性能不高。
- JSON/XML: 跨平台,移动互联网领域广泛使用.
基于 TCP 协议实现 RPC
可以基于 Java 的 Socket API,实现一个简单 RPC 调用.
模型
Demo
|- ISayHello
|-- SayHelloService
|-- TCPRPCConsumer
|-- TCPRPCProvider
package com.land.rpc;
public interface ISayHello {
public String sayHello(String arg);
}
package com.land.rpc;
public class SayHelloService implements ISayHello {
public String sayHello(String arg) {
if ("hello".equals(arg)) {
return "hello";
}
return "bye";
}
}
package com.land.rpc;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.Socket;
public class TCPRPCConsumer {
public static void main(String[] args) throws NoSuchMethodException, IOException, ClassNotFoundException {
final String interfaceName = ISayHello.class.getName();
Method method = ISayHello.class.getMethod("sayHello", java.lang.String.class);
Object[] methodArgs = {"hello"};
Socket socket = new Socket("127.0.0.1", 4080);
// 对象传输协议
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
output.writeUTF(interfaceName);
output.writeUTF(method.getName());
output.writeObject(method.getParameterTypes());
output.writeObject(methodArgs);
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
// 阻塞式 I/O
String result = (String)input.readObject();
System.out.println(result);
}
}
package com.land.rpc;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class TCPRPCProvider {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Map<String, Object> services = new HashMap<String, Object>();
services.put("com.land.rpc.ISayHello", new SayHelloService());
final ServerSocket server = new ServerSocket(4080);
while (true) {
Socket socket = server.accept();
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
String interaceName = input.readUTF();
String methodName = input.readUTF();
Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
Object[] methodArgs = (Object[]) input.readObject();
Object service = services.get(interaceName);
Class<?> serviceInterfaceClass = Class.forName(interaceName);
Method method = serviceInterfaceClass.getMethod(methodName, parameterTypes);
Object result = method.invoke(service, methodArgs);
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
output.writeObject(result);
}
}
}
基于 HTTP 协议实现 RPC
HTTP 请求与响应
- 浏览器根据所使用的 HTTP 协议,解析出 url 对应的域名。
- 通过 DNS 域名解析,查询出改域名对应的 IP 地址。
- 通过 url 解析出端口号(默认80)。
- 浏览器发起并建立该 IP 的 该端口的连接。
- 浏览器向服务器发起 GET/POST 请求。
- 服务器响应浏览器的请求,浏览器读取响应,渲染网页。
- 浏览器关闭与服务器的连接。
通过 HttpClient 发送 HTTP 请求
如果使用 Socket API 来处理 HTTP 请求,一方面工作量比较大,效果还不一定好(底层的流处理,并发控制 etc)。 HttpClient 提供了一个成熟的解决方案。
###RPC: HTTP vs. TCP
TCP | 处于协议栈的下层,可以对协议字段进行定制,减少网络开销字节数,降低网络开销,提高性能,实现更大的吞吐量和并发数 | 实现成本高(需要考虑多线程并发、锁、I/O etc 复杂的底层细节),难以跨平台 |
HTTP | 很多成熟的容器已经很好处理, like. Tomcat, Apache, Jboss; 成熟的响应格式,like. JSON, XML | 处于协议栈的上层,同等内容信息比 TCP的字节数多,效率相对要低 => gzip 改善 |
RESTful & RPC URL 链接风格
两种主流的 URL:
RPC | http://hostname/provider.do?service=com.http.sayhello&format=json×tamp=2016-09-05-20-36-00&arg1=arg1&arg2=arg2 | hostname/format/timestamp/args |
RESTful | POST http://hostname/people GET http://hostname/people/land PUT http://hostname/people/land DELETE http://hostname/people/land | RESTful = Representational State Transfer, 表现层状态转换. 利用了 HTTP 协议里的几种操作. POST: 创建,GET: 返回,PUT: 更新,DELETE: 删除 |
变通 | POST arg1=hello arg2=123 URL http://hostname/provider/sayhelloservice/2016-09-05-20-36-00.json | provider=服务提供方;sayhelloservice=服务接口名称;.json=服务端返回的数据格式;2016-09-05-20-36-00=客户端访问的时间戳 |
实现 HTTP RPC
可以使用 Servlet、Struts、Spring MVC 实现 HTTP RPC。