一、概述
RPC(remote procedure call),即远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络实现的技术。
常见的RPC框架有:阿里的Dubbo、Spring cloud 、Google 出品的 grpc 等等。
- 服务消费方(Client)调用本地方法
- client stub 接收到调用后负责将方法、参数等信息封装成能够进行网络传输的消息体
- client stub 将消息体进行编码发送到服务端
- server stub 收到消息后解码
- server stub 根据解码结果调用本地服务
- 本地服务执行并将结果返回给server stub
- server stub 将结果封装并编码发送给客户端
- client stub 接收到消息解码
- client stub 解码成功得到结果
RPC的目标就是将 2-8步骤 都封装起来,用户无需关系这些细节,就像可以调用本地接口一样。
二、设计和实现
2.1 结构设计
- client (客户端) :创建两个接口 HelloRPC、HelloNetty,还有一个测试类TestNettyRPC
- client stub : 一个客户端代理类 NettyRPCProxy、一个客户端业务处理类 ResultHandler
- Server(服务端): 创建两个名称和客户端一样的接口HelloRPC、HelloNetty,并且实现这个接口里面的方法。
- Server stub :一个网络处理器InvokeHandler、一个服务器的业务处理器NettyRPCServer
注意:客户端的接口必须和服务端的接口保持一致。
这里设计到一个jar,主要作用是获取接口实现类
<!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
</dependency>
client.HelloNetty
/*
* Copyright (C), 2013-2019, 天津大海云科技有限公司
*/
package com.jikang.rpc.client;
/**
* @author yangjikang
* @date 2019/7/17 15:36
* @modified By yangjikang
*/
public interface HelloNetty {
String hello();
}
cilent.HelloRPC
/*
* Copyright (C), 2013-2019, 天津大海云科技有限公司
*/
package com.jikang.rpc.client;
/**
* @author yangjikang
* @date 2019/7/17 15:37
* @modified By yangjikang
*/
public interface HelloRPC {
String hello(String name);
}
client.TestNettyRPC
/*
* Copyright (C), 2013-2019, 天津大海云科技有限公司
*/
package com.jikang.rpc.client;
import com.jikang.rpc.clientStub.NettyRPCProxy;
/**
* @author yangjikang
* @date 2019/7/17 15:37
* @modified By yangjikang
*/
public class TestNettyRPC {
public static void main(String[] args) {
//第一次远程调用
HelloNetty helloNetty = (HelloNetty) NettyRPCProxy.create(HelloNetty.class);
System.out.println(helloNetty.hello());
//第二次远程调用
HelloRPC helloRPC = (HelloRPC) NettyRPCProxy.create(HelloRPC.class);
System.out.println(helloRPC.hello("RPC"));
}
}
clientStub.NettyRPCProxy
/*
* Copyright (C), 2013-2019, 天津大海云科技有限公司
*/
package com.jikang.rpc.clientStub;
import com.jikang.rpc.serverStub.ClassInfo;
import com.jikang.rpc.serverStub.InvokeHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
*
* 客户端代理类
*
* @author yangjikang
* @date 2019/7/17 15:38
* @modified By yangjikang
*/
public class NettyRPCProxy {
//根据接口创建代理对象
public static Object create (Class target){
return Proxy.newProxyInstance(target.getClassLoader(), new Class[]{target}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//封装ClassInfo
ClassInfo classInfo = new ClassInfo();
classInfo.setClassName(target.getName());
classInfo.setMethodName(method.getName());
classInfo.setObjects(args);
classInfo.setTypes(method.getParameterTypes());
//开始用Netty发送数据
EventLoopGroup group = new NioEventLoopGroup();
ResultHandler resultHandler = new ResultHandler();
try{
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast("encoder",new ObjectEncoder());//编码器
pipeline.addLast("decoder",new ObjectDecoder(Integer.MAX_VALUE,
ClassResolvers.cacheDisabled(null)));//解码器
pipeline.addLast(resultHandler);
}
});
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9999).sync();
channelFuture.channel().writeAndFlush(classInfo).sync();
channelFuture.channel().closeFuture().sync();
}catch (Exception e){
e.printStackTrace();
}finally {
group.shutdownGracefully();
}
return resultHandler.getResponse();
}
});
}
}
clientStub.ResultHandler
/*
* Copyright (C), 2013-2019, 天津大海云科技有限公司
*/
package com.jikang.rpc.clientStub;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
*
* 客户端业务处理类
*
* @author yangjikang
* @date 2019/7/17 15:38
* @modified By yangjikang
*/
public class ResultHandler extends ChannelInboundHandlerAdapter{
private Object response;
public Object getResponse() {
return response;
}
//读取客户端返回的数据(远程调用的结果)
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
response = msg;
ctx.close();
}
}
server.HelloNetty
/*
* Copyright (C), 2013-2019, 天津大海云科技有限公司
*/
package com.jikang.rpc.server;
/**
* @author yangjikang
* @date 2019/7/17 15:38
* @modified By yangjikang
*/
public interface HelloNetty {
String hello();
}
server.HelloNettyImpl
/*
* Copyright (C), 2013-2019, 天津大海云科技有限公司
*/
package com.jikang.rpc.server;
/**
* @author yangjikang
* @date 2019/7/17 15:39
* @modified By yangjikang
*/
public class HelloNettyImpl implements HelloNetty {
@Override
public String hello() {
return "hello,netty";
}
}
server.HelloRPC
/*
* Copyright (C), 2013-2019, 天津大海云科技有限公司
*/
package com.jikang.rpc.server;
/**
* @author yangjikang
* @date 2019/7/17 15:39
* @modified By yangjikang
*/
public interface HelloRPC {
String hello(String name);
}
server.HelloRPCImpl
/*
* Copyright (C), 2013-2019, 天津大海云科技有限公司
*/
package com.jikang.rpc.server;
/**
* @author yangjikang
* @date 2019/7/17 15:39
* @modified By yangjikang
*/
public class HelloRPCImpl implements HelloRPC {
@Override
public String hello(String name) {
return "hello,"+name;
}
}
serverStub.ClassInfo
/*
* Copyright (C), 2013-2019, 天津大海云科技有限公司
*/
package com.jikang.rpc.serverStub;
import java.io.Serializable;
/**
*
* 封装类信息
*
*
* @author yangjikang
* @date 2019/7/17 15:40
* @modified By yangjikang
*/
public class ClassInfo implements Serializable {
private static final long serialVersionUID = 2989221128581296303L;
private String className;//类名
private String methodName;//方法名
private Class<?>[] types;//参数类型
private Object[] objects;//参数列表
public static long getSerialVersionUID() {
return serialVersionUID;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Class<?>[] getTypes() {
return types;
}
public void setTypes(Class<?>[] types) {
this.types = types;
}
public Object[] getObjects() {
return objects;
}
public void setObjects(Object[] objects) {
this.objects = objects;
}
}
serverStub.InvokeHandler
/*
* Copyright (C), 2013-2019, 天津大海云科技有限公司
*/
package com.jikang.rpc.serverStub;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.reflections.Reflections;
import java.lang.reflect.Method;
import java.util.Set;
/**
*
* 服务器端业务处理类
*
* @author yangjikang
* @date 2019/7/17 15:40
* @modified By yangjikang
*/
public class InvokeHandler extends ChannelInboundHandlerAdapter {
/**
* 得到某个接口下某个实现类的名字
* @param classInfo
* @return
* @throws Exception
*/
private String getImplClassName(ClassInfo classInfo) throws Exception {
//服务方接口和实现类所在包路径
String interfacePath = "com.jikang.rpc.server";
int lastDot = classInfo.getClassName().lastIndexOf(".");
String interfaceName = classInfo.getClassName().substring(lastDot);
Class superaClass = Class.forName(interfacePath + interfaceName);
//得到某接口下所有实现类
Reflections reflections = new Reflections(interfacePath);
Set<Class> implClassSet = reflections.getSubTypesOf(superaClass);
if (implClassSet.size() == 0) {
System.out.println("未找到实现类");
return null;
}else if (implClassSet.size()>1){
System.out.println("找到多个实现类,未明确使用哪一个");
return null;
}else{
//把集合转化成数组
Class[] classes = implClassSet.toArray(new Class[0]);
return classes[0].getName();//得到实现类的名称
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ClassInfo classInfo = (ClassInfo) msg;
Object object = Class.forName(getImplClassName(classInfo)).newInstance();
Method method = object.getClass().getMethod(classInfo.getMethodName(), classInfo.getTypes());
//通过反射调用实现类的方法
Object result = method.invoke(object, classInfo.getObjects());
ctx.writeAndFlush(result);
}
}
serverStub.NettyRPCServer
/*
* Copyright (C), 2013-2019, 天津大海云科技有限公司
*/
package com.jikang.rpc.serverStub;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.serialization.ClassResolver;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
/**
*
* 网络处理服务器
*
* @author yangjikang
* @date 2019/7/17 15:40
* @modified By yangjikang
*/
public class NettyRPCServer {
private int port;
public NettyRPCServer(int port) {
this.port = port;
}
public void start(){
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try{
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,128)
.childOption(ChannelOption.SO_KEEPALIVE,true)
.localAddress(port)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast("encoder",new ObjectEncoder());//编码器
pipeline.addLast("decoder",new ObjectDecoder(Integer.MAX_VALUE,
ClassResolvers.cacheDisabled(null)));//解码器
pipeline.addLast(new InvokeHandler());
}
});
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
System.out.println("-------server is ready------");
channelFuture.channel().closeFuture().sync();
}catch (Exception e){
e.printStackTrace();
}finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
NettyRPCServer nettyRPCServer = new NettyRPCServer(9999);
nettyRPCServer.start();
}
}