Netty实现简单RPC调用
我们知道Dubbo是一个RPC框架,那RPC框架需要实现什么?需要实现的是调用远程服务和本地服务一样方便,同时提高调用远程服务的性能。而服务端和客户端之间的关系,其实就是一个生产和消费的关系。更多教程请访问码农之家
客户端与服务端交互关系图
1.服务消费方以本地调用方式调用服务
2.client stub 接收到调用后负责将方法、参数等封装成能够进行网络传输的消息体
3.client stub将消息进行编码并发送到服务端
4.server stub 根据解码结果调用本地的服务
5.server stub将返回导入结果进行编码并发送至消费方
6.本地服务执行并将结果返回给server stub
7.server stub将返回导入结果进行编码并发送至消费方
8.client stub接收到消息并进行解码
9.服务消费方(client)得到结果
RPC的目标是将2-8步骤进行封装,用户无需关系这些细节,也即实现远程调用和调用本地方法一样。
Server服务提供方
server
/**
* 服务提供方
* @author Administrator
*
*/
public interface HelloNetty {
String hello();
}
/**
* 实现HelloNetty接口
* @author Administrator
*
*/
public class HelloNettyImpl implements HelloNetty {
@Override
public String hello() {
return "hello,netty";
}
}
/**
* 服务提供方
* @author Administrator
*
*/
public interface HelloRPC {
String hello(String name);
}
/**
* HelloRPC接口的实现
* @author Administrator
*
*/
public class HelloRPCImpl implements HelloRPC {
@Override
public String hello(String name) {
return "hello,"+name;
}
}
Server Stub
封装需要传递的消息实体类
/**
* 封装类信息,实体类用来封装消费方发起远程调用时传给服务方的数据
* @author Administrator
*
*/
//封装类信息
public class ClassInfo implements Serializable {
private static final long serialVersionUID = 1L;
private String className; //类名
private String methodName;//方法名
private Class<?>[] types; //参数类型
private Object[] objects;//参数列表
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;
}
}
服务器端业务处理
/**
* 服务器端业务处理类
* @author Administrator
*
*/
public class InvokeHandler extends ChannelInboundHandlerAdapter{
//得到某接口下某个实现类的名字
private String getImplClassName(ClassInfo classInfo) throws Exception{
//服务方接口和实现类所在的包路径
String interfacePath="com.study.nettyRpc.server";
int lastDot = classInfo.getClassName().lastIndexOf(".");
String interfaceName=classInfo.getClassName().substring(lastDot);
Class superClass=Class.forName(interfacePath+interfaceName);
Reflections reflections = new Reflections(interfacePath);
//得到某接口下的所有实现类
Set<Class> ImplClassSet=reflections.getSubTypesOf(superClass);
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 clazz = Class.forName(getImplClassName(classInfo)).newInstance();
Method method = clazz.getClass().getMethod(classInfo.getMethodName(), classInfo.getTypes());
//通过反射调用实现类的方法
Object result = method.invoke(clazz, classInfo.getObjects());
ctx.writeAndFlush(result);
}
}
网络处理服务器Server端
/**
* 网络处理服务器
* @author Administrator
*
*/
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 ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//编码器
pipeline.addLast("encoder", new ObjectEncoder());
//解码器
pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
//服务器端业务处理类
pipeline.addLast(new InvokeHandler());
}
});
ChannelFuture future = serverBootstrap.bind(port).sync();
System.out.println("......server is ready......");
future.channel().closeFuture().sync();
} catch (Exception e) {
//优雅关闭
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new NettyRPCServer(9999).start();
}
}
Client客户端
client stub
客户端代理类
/**
* 客户端代理类
* @author Administrator
*
*/
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 b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>(){
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//编码器
pipeline.addLast("encoder",new ObjectEncoder());
//解码器
pipeline.addLast("decoder",new ObjectDecoder(Integer.MAX_VALUE,ClassResolvers.cacheDisabled(null)));
//客户端业务处理类
pipeline.addLast("handler",resultHandler);
}
});
ChannelFuture future = b.connect("127.0.0.1",9999).sync();
future.channel().writeAndFlush(classInfo).sync();
future.channel().closeFuture().sync();
}finally{
group.shutdownGracefully();
}
return resultHandler.getResponse();
}
});
}
}
客户端业务处理类
/**
* 客户端业务处理类
* @author Administrator
*
*/
public class ResultHandler extends ChannelInboundHandlerAdapter{
private Object response;
public Object getResponse(){
return response;
}
//读取服务器端返回的数据(远程调用的结果)
@Override
public void channelRead(ChannelHandlerContext ctx,Object msg){
response = msg;
ctx.close();
}
}
客户端调用
/**
* 服务调用方
*
* @author Administrator
*
*/
public class TestNettyRPC {
public static void main(String[] args) {
//第1次远程调用
HelloNetty helloNetty=(HelloNetty) NettyRPCProxy.create(HelloNetty.class);
System.out.println(helloNetty.hello());
//第2次远程调用
HelloRPC helloRPC = (HelloRPC) NettyRPCProxy.create(HelloRPC.class);
System.out.println(helloRPC.hello("RPC"));
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~