手把手,嘴对嘴教你写一个基本的Rpc框架

推荐阅读:

看完点个赞呗,啊呜~

手把手,嘴对嘴教你写一个基本的Rpc框架


Rpc框架是个啥?

“老王,Rpc是个啥,俺怎么没听说过?” 小王一脸疑惑的问向老王。

“Rpc你都不知道?亏你还是我带出来的。” 老王一脸鄙视的望向小王。

“有那个时间我早去跟学妹深入交流了。” 小王小声嘀咕道。

“好吧,好吧,今天我就跟你讲讲Rpc是个啥,你可得给我好好听。”

“Rpc(Remote Procedure Call)是远程过程调用的意思。举个栗子,部署在两台服务器的服务A1,A2。此时A1想调用A2的服务,采用传统的调用方式是无法调用的。然而Rpc框架解决了这个问题,它可以在不同的系统之间进行通讯。”

“原来如此,老王,那Rpc是咋调用的?” 小王再次问向老王。

“你急个啥,让我慢慢给你道来。”

“Rpc框架实现服务的提供方和消费方。服务的提供方把服务提供出去,让消费方去消费。这里还涉及到服务的发现与注册,这以后再谈”

“至于调用吗,听的再多不如教你写一次”

“老王威武,童颜巨......” 小王对老王夸赞道。 “那是,隔壁老王的称号可不是白叫的”。老王自豪的抬起头,摸了摸头上仅剩的几根头发。

手写基本Rpc

先写暴露服务的方法

暴露服务的方法是将该服务暴露出去,已便于调用

1.1 创建一个暴露服务的方法,这里需要接受两个参数,一个是服务对象,一个是端口号
<code> 

public

static

void

exportService

(

final

Object service,

int

port)

复制代码

/<code>
1.2 判断参数是否合法
<code> 

if

(service==

null

){

throw

new

IllegalArgumentException(

"service is null"

); }

if

(port<=

0

|| port>

65535

){

throw

new

IllegalArgumentException(

"Invalid Error, port is not access "

); } 复制代码/<code>
1.3 创建Socket对象
<code>ServerSocket serverSocket = 

new

ServerSocket(port); System.

out

.println(

"服务已经启动----"

); 复制代码/<code>
1.4 获取socket,采用线程,创建I/O流
<code> 

final

Socket socket = serverSocket.accept();

new

Thread(

new

Runnable() { ObjectInputStream objectInputStream =

null

; ObjectOutputStream objectOutputStream =

null

;

public

void

run

()

{ } } }).start(); 复制代码/<code>
1.5 将对象存入到I/O流中
<code>

try

{ objectInputStream =

new

ObjectInputStream(socket.getInputStream());

String

methodName = objectInputStream.readUTF(); Class>[] paramTypes = (Class>[])objectInputStream.readObject();

Object

[]

arguments

= (

Object

[])objectInputStream.readObject(); Method method = service.getClass().getMethod(methodName, paramTypes);

Object

result = method.invoke(service,

arguments

); objectOutputStream =

new

ObjectOutputStream(socket.getOutputStream()); objectOutputStream.writeObject(result); }

catch

(Exception e) { e.printStackTrace(); }

finally

{

try

{ objectOutputStream.close(); objectInputStream.close(); socket.close(); }

catch

(Exception e) { e.printStackTrace(); } } 复制代码/<code>

“老王,为什么这里是while(true)?这不是死循环了吗?” 小王问老王。 “这你就不懂了吧,这里并不是死循环,而是代表暴露服务的方法一直运行着,每当一次调用请求进来的时候才会执行。” “这里我们可以用一个requestNum来记录调用的情况”

完整的暴露服务代码

<code>  
     

public

static

void

exportServer

(

final

Object service,

int

port)

throws

Exception

{

if

(service==

null

){

throw

new

IllegalArgumentException(

"service is null"

); }

if

(port<=

0

|| port>

65535

){

throw

new

IllegalArgumentException(

"Invalid Error, port is not access "

); } ServerSocket serverSocket =

new

ServerSocket(port); System.out.println(

"服务已经启动----"

);

while

(

true

){

final

Socket socket = serverSocket.accept();

new

Thread(

new

Runnable() { ObjectInputStream objectInputStream =

null

; ObjectOutputStream objectOutputStream =

null

;

public

void

run

()

{

try

{ requestNum++; System.out.println(

"端口:"

+port+

"接收请求"

+requestNum+

"次"

); objectInputStream =

new

ObjectInputStream(socket.getInputStream()); String methodName = objectInputStream.readUTF(); Class>[] paramTypes = (Class>[])objectInputStream.readObject(); Object[] arguments = (Object[])objectInputStream.readObject(); Method method = service.getClass().getMethod(methodName, paramTypes); Object result = method.invoke(service, arguments); objectOutputStream =

new

ObjectOutputStream(socket.getOutputStream()); objectOutputStream.writeObject(result); }

catch

(Exception e) { e.printStackTrace(); }

finally

{

try

{ objectOutputStream.close(); objectInputStream.close(); socket.close(); }

catch

(Exception e) { e.printStackTrace(); } } } }).start(); } } 复制代码/<code>

调用服务的方法

该方法主要用来调用对应的服务

2.1 创建一个调用服务方法,这里需要接受三个参数,接口类型,ip地址,端口号
<code>

public

static

T

referServer

(

final

Class interfaceClass,

final

String host,

final

int

port)

复制代码

/<code>
2.2 判断参数合法性
<code>

if

(interfaceClass ==

null

){

throw

new

IllegalArgumentException(

"interfaceClass is null"

); }

if

(host ==

null

){

throw

new

IllegalArgumentException(

"host is null"

); }

if

(port<=

0

|| port>

65535

){

throw

new

IllegalArgumentException(

"Invalid port :"

+ port); }

if

(!interfaceClass.isInterface()){

throw

new

IllegalArgumentException(

"interfaceClass is not interface class "

); } 复制代码/<code>
2.3 采用jdk的动态代理返回服务对象
<code>

return

(T)Proxy.newProxyInstance(interfaceClass.getClassLoader(),

new

Class>[]{interfaceClass},

new

InvocationHandler() {

public

Object

invoke(

Object

proxy, Method method,

Object

[] args) throws Throwable { Socket socket =

new

Socket(host,port); System.out.println(

"主机ip地址:"

+host+

",端口号:"

+port); ObjectOutputStream objectOutputStream =

null

; ObjectInputStream objectInputStream =

null

;

try

{ objectOutputStream =

new

ObjectOutputStream(socket.getOutputStream()); objectOutputStream.writeUTF(method.getName()); objectOutputStream.writeObject(method.getParameterTypes()); objectOutputStream.writeObject(args); objectInputStream =

new

ObjectInputStream(socket.getInputStream());

Object

result = objectInputStream.readObject();

if

(result

instanceof

Throwable) {

throw

(Throwable)result; }

return

result; }

catch

(Exception e){ e.printStackTrace(); }

finally

{ objectInputStream.close(); objectOutputStream.close(); socket.close(); }

return

null

; } }); 复制代码/<code>

完整的调用服务代码

<code> 
    

public

static

T

referServer

(

final

Class interfaceClass,

final

String host,

final

int

port)

throws

Exception

{

if

(interfaceClass ==

null

){

throw

new

IllegalArgumentException(

"interfaceClass is null"

); }

if

(host ==

null

){

throw

new

IllegalArgumentException(

"host is null"

); }

if

(port<=

0

|| port>

65535

){

throw

new

IllegalArgumentException(

"Invalid port :"

+ port); }

if

(!interfaceClass.isInterface()){

throw

new

IllegalArgumentException(

"interfaceClass is not interface class "

); }

return

(T)Proxy.newProxyInstance(interfaceClass.getClassLoader(),

new

Class>[]{interfaceClass},

new

InvocationHandler() {

public

Object

invoke

(Object proxy, Method method, Object[] args)

throws

Throwable

{ Socket socket =

new

Socket(host,port); System.out.println(

"主机ip地址:"

+host+

",端口号:"

+port); ObjectOutputStream objectOutputStream =

null

; ObjectInputStream objectInputStream =

null

;

try

{ objectOutputStream =

new

ObjectOutputStream(socket.getOutputStream()); objectOutputStream.writeUTF(method.getName()); objectOutputStream.writeObject(method.getParameterTypes()); objectOutputStream.writeObject(args); objectInputStream =

new

ObjectInputStream(socket.getInputStream()); Object result = objectInputStream.readObject();

if

(result

instanceof

Throwable) {

throw

(Throwable)result; }

return

result; }

catch

(Exception e){ e.printStackTrace(); }

finally

{ objectInputStream.close(); objectOutputStream.close(); socket.close(); }

return

null

; } }); } 复制代码/<code>

测试手写的Rpc框架

“小王啊,我们来测试一下吧”

创建服务接口以及实现类

<code>

public

interface

MyInterface

{

String

hello

()

; }

public

class

MyInterfaceImpl

implements

MyInterface

{

public

String

hello

()

{

return

"HELLO WORLD"

; } } 复制代码/<code>

启动两个main方法进行测试

<code>

public

class

MyTest

{

public

static

void

main

(String[] args)

throws Exception

{ MyInterfaceImpl server =

new

MyInterfaceImpl(); MyRpcFramework.

export

(server,

6000

); } }

public

class

MyTest2

{

public

static

void

main

(String[] args)

throws Exception

{ MyInterface server = MyRpcFramework.refer(MyInterface.class,

"127.0.0.1"

,

6000

);

for

(

int

i =

0

;i<

10

;i++){ System.out.println(server.hello()+

"--"

+i); Thread.sleep(

1000

); } } } 复制代码/<code>

测试结果


手把手,嘴对嘴教你写一个基本的Rpc框架


手把手,嘴对嘴教你写一个基本的Rpc框架


总结

  以上便是手写基本Rpc框架的全部内容,作者希望采用小王和老王的对话来更加生动的描写出技术原理以及实现,感谢大家的阅读。天高地远,我们江湖再见!


作者:武当王也
链接:https://juejin.im/post/5e96e572518825739d4086c0
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


分享到:


相關文章: