您的位置:首页 > 运维架构 > 网站架构

应用架构之RPC架构

2017-06-27 08:27 127 查看

RPC框架简介

什么叫RPC?

RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

为什么会出现RPC?

应用之间的交互不可避免,将核心业务抽取出来,作为独立的服务。同时将通用的API抽取出来,供其他系统调用者使用。应用拆分之后会按照模块独立部署,接口调用本地接口变成了跨进程的远程调用,这个时候就出现了RPC框架。

说白了RPC是一种进程间的通信方式,允许像调用本地方法一样调用远程服务

RPC框架原理

RPC框架的目标是是远程服务调用更加简单,透明。它屏蔽了底层的传输方式(TCP或者UDP),序列化方式(XML/JSON/二进制)和通信细节。其实框架的作用就是屏蔽了底层的操作细节,让我们程序员更加方便高效的编程。但是了解底层的实现对我们熟练使用框架是有很大的帮助的。下图简述RPC框架的原理:



运行时,一次客户机对服务器的RPC调用,其内部操作大致有如下十步:

1.调用客户端句柄;执行传送参数

2.调用本地系统内核发送网络消息

3.消息传送到远程主机

4.服务器句柄得到消息并取得参数

5.执行远程过程

6.执行的过程将结果返回服务器句柄

7.服务器句柄返回结果,调用远程系统内核

8.消息传回本地主机

9.客户句柄由内核接收消息

10.客户接收句柄返回的数据

简单的PRC框架的实现

下面通过Java原生的序列化,Socket通信,动态代理和反射机制实现最简单的RPC框架。

组成

服务提供者,运行在服务端,负责提供服务接口定义和服务实现类。

服务发布者,运行在RPC服务端,负责将本地服务发布成远程服务,供消费者使用。

本地服务代理,运行在RPC客户端通过代理调用远程的服务提供者,然后将结果进行封装返还给本地消费者。

实现

服务端接口定义

public interface EchoService {
String echo( String ping );
}


服务端接口实现类

public class EchoServiceImpl implements EchoService {

@Override
public String echo(String ping) {
return ping != null ? ping + " -->I am ok." : " I am ok.";
}

}


RPC服务端服务发布者代码

//ObjectInputStream 对象输入流,可以将序列化过的对象反序列化到内存中,对应的还有个ObjectOutputStream 可以将一个对象序列化到本地!
public class RpcExporter {

static Executors executors = (Executors) Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

public static void exporter( String hostName, int port ) throws Exception {
ServerSocket server = new ServerSocket();
server.bind( new InetSocketAddress( hostName, port ) );
}

private static class ExporterTask implements Runnable {

Socket client = null;

public ExporterTask ( Socket client ) {
this.client = client;
}

@Override
public void run() {
ObjectInputStream input = null;
ObjectOutputStream output = null;

try {

input = new ObjectInputStream( client.getInputStream() );
String interfaceName = input.readUTF();
Class<?> service = Class.forName( interfaceName );
String methodName = input.readUTF();
Class< ? > parameterTypes = (Class<?>) input.readObject();
Object[] arguments = (Object[]) input.readObject();
Method method = service.getMethod(methodName, parameterTypes);
Object result = method.invoke( service.newInstance() , arguments );
output = new ObjectOutputStream( client.getOutputStream() );
output.writeObject( result );

} catch (Exception e) {
e.printStackTrace();
} finally {
if( output != null ) {
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if( input != null ) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if( client != null ) {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
4000

}
}
}
}
}


服务发布者的主要职责:

做为服务器,监听客户端的TCP连接,接收到新的客户端连接后,将其封装成Task,由线程池执行。

将客户端发送的码反序列化成对象,反射调用服务实现者,获得执行结果。

将执行结果对象反序列化,通过Socket发送给客户端。

远程调用完成之后,释放Socket等连接资源,防止句柄泄漏。

RPC客户端本地服务代理代码

public class RpcImporter<S> {
public S importer ( final Class<?> serviceClass ,final InetSocketAddress addr ) {
return (S) Proxy.newProxyInstance(serviceClass.getClassLoader(), new Class<?>[] { serviceClass.getInterfaces()[0] },
new InvocationHandler() {

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = null;
ObjectOutputStream output = null;
ObjectInputStream input = null;

try {
socket = new Socket();
socket.connect(addr);
output = new ObjectOutputStream(socket.getOutputStream());
output.writeUTF(serviceClass.getName());
output.writeUTF(method.getName());
output.writeObject(method.getParameterTypes());
output.writeObject(args);
input = new ObjectInputStream(socket.getInputStream());//同步阻塞等待
} finally {
if( socket != null ) {
socket = null;
}
if( output != null ) {
output = null;
}
if( input != null ) {
input = null;
}
}
return input.readObject();
}
});
}
}


本地服务代理的职责

将本地接口调用转化成JDK的动态代理,在代理类中实现接口的远程调用。

创建Socket客户端,根据指定地址连接远程服务提供者

将远程服务调用所需的接口和方法名等编码后发送给服务提供者。

同步阻塞等待服务器返回应答,获取应答之后返回。

测试代码

public class RpcTest {
public static void main(String[] args) {
//用于接受PRC客户端的请求
new Thread(new Runnable() {

@Override
public void run() {
try {
RpcExporter.exporter("127.0.0.1", 8080);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
RpcImporter<EchoService> importer = new RpcImporter< EchoService >();
EchoService echo = importer.importer( EchoServiceImpl.class, new InetSocketAddress("127.0.0.1", 8080) );
System.out.println( echo.echo("Are you OK?") );
}
}


RPC的不足

纯粹的RPC框架服务与治理能力都不健全,当应用大规模服务化之后会面临许多服务治理方面的挑战,要解决这些问题,必须通过服务框架 + 服务治理来完成,单凭RPC框架无法解决服务治理问题。

什么是服务治理
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息