前言:
本文内容的主体基于陈康贤先生的《大型分布式网站架构》,在写本文时,笔者提炼了书里的关键内容,并在一些地方加上了自己的见解,同时书中的关键代码或伪代码付诸实践。如有不足之处,欢迎指正。在有引用到其他书籍时,笔者做了标记,以助读者自行扩展阅读。同时也欢迎大家阅读陈康贤先生的这本书,站在巨人的肩膀上,看待如今的大型分布式网站。
内容提纲:
1、RPC简介
2、序列化与反序列化简介
3、基于TCP协议的一个RPC实例
正文:
一、RPC简介
如果你去一家公司面试,面试官上来问你,什么是RPC,你不知道,可能就会失去这个入职的机会。不过也不用妄自菲薄,面试官问你关于RPC的知识,可能是他们项目组正在做分布式大型网站。如果你的专长不在这方面,自然也就不适合这个职位了,也并不是说你不够强。话虽这样,但自从2008年淘宝网改造成一个大型分布式网站之后,分布式越来越走向潮流,不仅如此,所谓的“云计算”、“大数据”好多也是基于分布式系统的。分布式意味着面对并发、业务复用、安全防御等方面的更高挑战。相信对大多数程序员来说,掌握这方面体系的知识,也是更高的挑战和荣誉。
不论如何,分布式应用所要解决的首要问题,还是实现应用间的远程调用,这就是RPC(Remote Process Call),字面上理解,就是不局限于本地方法去调用远程机器上的方法或进程。RPC的意义在于突破了单台服务器的处理能力,将本地调用转变为远程过程调用,从而给系统处理能力与吞吐量带来了无限制提升的可能。其实现方案现大多已成熟并得到广泛应用,代表性的包括RMI,WebService等。
关于RMI和WebService,请参见林昊先生的《分布式java应用》第一章,里面有详细的讲解。
二、序列化与反序列化简介
无论是何种数据,想实现远程传输,都要转化成二进制流。对象也是如此。
定义:将对象转化为二进制流的过程称为对象的序列化;将二进制流恢复为对象的过程称为对象的反序列化。
序列化和反序列化是十分基础的内容,大学的java课程中一般都有介绍。但话虽如此,序列化与反序列化是一块比较成体系的知识,有时间我会专门写一个这方面的博客来介绍和总结。在这之前,如果读者想具体了解,请参见《java与模式》一书中“javabean的冷藏与解冻”一章。
序列化和反序列化实现方式比较简单,可通过Java内置的序列化方式或使用Hessian实现,后者需要引入第二方包hessian.jar。此处只提供给读者搜索的关键字,不赘述代码。
三、基于TCP协议的一个RPC实例
下面是这个实例的全部代码:
SayHelloService接口:
public interface SayHelloService {
/**
* 问好的接口
* @param helloArg 参数
* @return
* */
public String sayHello(String helloArg);
}
具体的服务类:
public class SayHelloServiceImpl implements SayHelloService {
@Override
public String sayHello(String helloArg) {
if(helloArg.equals("hello")){
return "hello";
}else{
return "bye bye";
}
}
}
服务消费者(客户端)代码:
public class Consumer {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, UnknownHostException, IOException, ClassNotFoundException {
String interfacename = SayHelloService.class.getName();
Method method = SayHelloService.class.getMethod("sayHello", java.lang.String.class);
Object[] arguments = {"hello"};
Socket socket = new Socket("127.0.0.1",1234);
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
output.writeUTF(interfacename);
output.writeUTF(method.getName());
output.writeObject(method.getParameterTypes());
output.writeObject(arguments);
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
Object result = input.readObject();
System.out.println("客户端接收到的消息:"+result.toString());
}
}
服务提供者(服务器)代码:
public class Provider {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
ServerSocket server = new ServerSocket(1234);
SayHelloService ser = new SayHelloServiceImpl();
Map
services.put("com.shuai.helloservice.SayHelloService", ser);
while(true){
Socket socket = server.accept();
//读取信息
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
String interfancename = input.readUTF(); //接口名称
String methodName = input.readUTF(); //方法名称
Class>[] parameterTypes = (Class>[])input.readObject(); //参数类型
Object[] arguments = (Object[])input.readObject();
System.out.println("服务器端收到的消息:"+arguments[0]);
//执行调用
Class serviceinterfaceclass = Class.forName(interfancename);
Object service = services.get(interfancename); //获取服务实现的对象
Method method = serviceinterfaceclass.getMethod(methodName, parameterTypes);
Object result = method.invoke(service, arguments);
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
output.writeObject(result);
System.out.println(result.toString()+"已发送给客户端");
}
}
}
运行结果:
服务器:
客户端:
注:本实例简化的内容主要包括
1、通过Socket发送数据时,本例采用阻塞式I/O,但实际多使用非阻塞式I/O,以提供更大的吞吐量。关于阻塞与非阻塞式I/O,参见林昊先生的《分布式java应用》第一章。
2、代码中services这个map用于存储事先准备好的服务实例,这里简化了这个过程,但实际上这一过程也有一套体系性知识点。之后介绍。
3、很明显,并没有处理并发等情况。
4、异常全部用throw方式,也是为了简洁。
下一章分享基于HTTP协议的RPC,希望我能坚持下来吧~~~~