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 调用.

模型

大型分布式网络架构设计与实践笔记01. RPC-LMLPHP

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 请求与响应

  1. 浏览器根据所使用的 HTTP 协议,解析出 url 对应的域名。
  2. 通过 DNS 域名解析,查询出改域名对应的 IP 地址。
  3. 通过 url 解析出端口号(默认80)。
  4. 浏览器发起并建立该 IP 的 该端口的连接。
  5. 浏览器向服务器发起 GET/POST 请求。
  6. 服务器响应浏览器的请求,浏览器读取响应,渲染网页。
  7. 浏览器关闭与服务器的连接。

通过 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&timestamp=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。

03-27 17:38