手写一个rpc框架,实现思路利用netty框架+jdk动态代理+自定义协议+自定义编解码器+网络传输

技术难点:TCP粘包拆包问题,解决思路:客户端在往服务端发送消息时(编码),先要将消息的长度(一个int)写入与channel关联的buffer中。客户端在接受消息时,先判断消息的长度是不是够一个int,如果不够,等待下一次事件触发,如果够,判断可读的消息的长度是不是等于客户端传过来的长度,如果不够,继续等待下一次事件触发。自定义编解码器代码如下:
自定义编码器:

package codec;

import com.alibaba.fastjson.JSON;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

import java.util.Objects;

public class RpcEncoder extends MessageToByteEncoder {
    private Class<?> clazz;

    public RpcEncoder(Class<?> clazz) {
        this.clazz = clazz;
    }

    /**
     * 自定义编码方法实现 java--》byte
     * @param channelHandlerContext
     * @param o
     * @param byteBuf
     * @throws Exception
     */
    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, Object o, ByteBuf byteBuf) throws Exception {
        if(Objects.nonNull(clazz)&&clazz.isInstance(o)){
            System.out.println("编码方法被调用..encode");
            byte[] bytes = JSON.toJSONBytes(o);
            int length = bytes.length;
            byteBuf.writeInt(length);
            byteBuf.writeBytes(bytes);
        }
    }
}

自定义解码器:

package codec;

import com.alibaba.fastjson.JSON;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.util.List;

public class RpcDecoder extends ByteToMessageDecoder {
    private Class<?> clazz;

    public RpcDecoder(Class<?> clazz) {
        this.clazz = clazz;
    }
    /**
     * 自定义解码器解码方法实现
     * @param channelHandlerContext
     * @param byteBuf
     * @param list
     * @throws Exception
     */
    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
        System.out.println("解码方法被调用..decode");
        if(byteBuf.readableBytes()<4){
            return;
        }
        //标记一下读指针位置,此时没有过读操作,读指针处于初始位置
        byteBuf.markReaderIndex();
        //读取一个int长度 此时读指针会后移4个位置
        int length = byteBuf.readInt();
        if(byteBuf.readableBytes()<length){
        	//读指针回到刚才标记的初始位置
            byteBuf.resetReaderIndex();
            return;
        }
        byte[] bytes = new byte[length];
        byteBuf.readBytes(bytes);
        Object o = JSON.parseObject(bytes, clazz);
        list.add(o);
    }
}

客户端代码(包括获取动态代理对象与初始化客户端):

package rpc.consumer;

import codec.RpcDecoder;
import codec.RpcEncoder;
import rpc.handler.ClientHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import protocol.RpcRequest;
import protocol.RpcResponse;
import java.lang.reflect.Proxy;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NettyClient {
    public static ExecutorService executor = Executors.newFixedThreadPool(4);
    public static boolean clientOk = false;
    private static ClientHandler clientHandler;
    public static Object getBean(final Class<?> serviceClass){
        /**
         * 该方法返回目标对象的代理对象
         */
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{serviceClass}, (proxy, method, args) -> {
            System.out.println("客户端invoke方法执行..hello");
            if(null==clientHandler){
                initClient();
            }
            RpcRequest rpcRequest = new RpcRequest();
            rpcRequest.setRequestId(UUID.randomUUID().toString());
            rpcRequest.setInterfaceName(method.getDeclaringClass().getName());
            rpcRequest.setMethodName(method.getName());
            rpcRequest.setParamTypes(method.getParameterTypes());
            rpcRequest.setPatams(args);
            clientHandler.setRpcRequest(rpcRequest);
            return executor.submit(clientHandler).get();
        });
    }

    /**
     * 该方法初始化netty客户端
     */
    public static void initClient(){
        clientHandler= new ClientHandler();
        NioEventLoopGroup executors = new NioEventLoopGroup();
        try{
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(executors)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY,true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast(new RpcEncoder(RpcRequest.class));
                            pipeline.addLast(new RpcDecoder(RpcResponse.class));
                            pipeline.addLast(clientHandler);
                        }
                    });
            System.out.println("客户端启动完毕");
            clientOk=true;
            bootstrap.connect("127.0.0.1", 9999).sync();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

客户端handler处理器:

package rpc.handler;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import protocol.RpcRequest;
import protocol.RpcResponse;

import java.util.concurrent.Callable;

public class ClientHandler extends SimpleChannelInboundHandler<RpcResponse> implements Callable {

    private ChannelHandlerContext ctx;
    private RpcResponse rpcResponse;
    private RpcRequest rpcRequest;
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("客户端与服务端建立连接..");
        this.ctx=ctx;
    }

    @Override
    protected synchronized void channelRead0(ChannelHandlerContext channelHandlerContext, RpcResponse rpcResponse) throws Exception {
        System.out.println("服务器返回数据..."+rpcResponse);
        this.rpcResponse=rpcResponse;
        notify();
    }
    //被代理对象调用,发送数据给服务器,等待被唤醒wait
    @Override
    public synchronized Object call() throws Exception {
        System.out.println("call 方法执行..");
        ctx.writeAndFlush(rpcRequest);
        wait();
        return rpcResponse.getData();
    }

    public void setRpcRequest(RpcRequest rpcRequest) {
        this.rpcRequest = rpcRequest;
    }
}

客户端controller(注意此处拿到的service是代理对象)


package rpc.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import rpc.consumer.NettyClient;
import rpcinterface.HelloService;
import rpcinterface.HiService;

import javax.annotation.PostConstruct;

@RestController
public class HelloController {

private HelloService helloService;
private HiService hiService;
@PostConstruct
private void init(){
    helloService = (HelloService) NettyClient.getBean(HelloService.class);
    hiService = (HiService)NettyClient.getBean(HiService.class);
}
@RequestMapping("/hello")
public String hello(String name){
    return helloService.hello(name);
}
@RequestMapping("/hi")
public String hi(String name){
    return hiService.hi(name);
}

}

自定义请求协议:
package protocol;


/**
 * 请求协议
 */
public class RpcRequest {

    private String requestId;
    private String interfaceName;
    private String methodName;
    private Class<?>[] paramTypes;
    private Object[] patams;

    public String getRequestId() {
        return requestId;
    }

    public void setRequestId(String requestId) {
        this.requestId = requestId;
    }

    public String getInterfaceName() {
        return interfaceName;
    }

    public void setInterfaceName(String interfaceName) {
        this.interfaceName = interfaceName;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Class<?>[] getParamTypes() {
        return paramTypes;
    }

    public void setParamTypes(Class<?>[] paramTypes) {
        this.paramTypes = paramTypes;
    }

    public Object[] getPatams() {
        return patams;
    }

    public void setPatams(Object[] patams) {
        this.patams = patams;
    }
}

自定义响应协议:

package protocol;

/**
 * 响应协议
 */
public class RpcResponse {

    private String requestId;
    private Object data;

    public String getRequestId() {
        return requestId;
    }

    public void setRequestId(String requestId) {
        this.requestId = requestId;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}

服务端初始化代码:

package provider;

import codec.RpcDecoder;
import codec.RpcEncoder;
import handler.NettyServerHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import protocol.RpcRequest;
import protocol.RpcResponse;

public class NettyServer {
    //nettyServer的初始化
    public static void startServer0(String hostName,int port){
        NioEventLoopGroup boss = new NioEventLoopGroup(1);
        NioEventLoopGroup worker = new NioEventLoopGroup(8);
        try{
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(boss,worker)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast(new RpcDecoder(RpcRequest.class));//解码器
                            pipeline.addLast(new RpcEncoder(RpcResponse.class));//编码器
                            pipeline.addLast(new NettyServerHandler());//业务处理器
                        }
                    });
            ChannelFuture channelFuture = serverBootstrap.bind(hostName, port).addListener(future -> {
                if(future.isSuccess()){
                    System.out.println("服务端启动成功,监听端口:"+port);
                }
            });
            channelFuture.channel().closeFuture().sync();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
}

服务端启动类:

package provider;

/**
 * 启动一个服务提供者,nettyServer
 */
public class ServerBootApplication {
    public static void main(String[] args) {
        NettyServer.startServer0("127.0.0.1",9999
        );
    }
}

服务端业务处理器Handler:

package handler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import protocol.RpcRequest;
import protocol.RpcResponse;
import util.InterfaceMapUtils;

import java.lang.reflect.Method;
import java.util.List;

public class NettyServerHandler extends SimpleChannelInboundHandler<RpcRequest> {

    @Override
    public void channelRead0(ChannelHandlerContext ctx, RpcRequest request) throws Exception {
        System.out.println("服务端收到请求");
        String requestId = request.getRequestId();
        String interfaceName = request.getInterfaceName();
        String methodName = request.getMethodName();
        Class<?>[] paramTypes = request.getParamTypes();
        Object[] prams = request.getPatams();
        Class<?> clazz = InterfaceMapUtils.interfaceMap.get(interfaceName);
        Object instance = clazz.newInstance();
        Method method = clazz.getMethod(methodName, paramTypes);
        Object invoke = method.invoke(instance, prams);
        RpcResponse response = new RpcResponse();
        response.setRequestId(requestId);
        response.setData(invoke);
        ctx.writeAndFlush(response);
        System.out.println("服务端处理请求完毕");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

服务端本地注册的容器

package util;

import rpcinterface.HelloService;
import rpcinterface.HiService;
import rpcinterface.impl.HelloServiceImpl;
import rpcinterface.impl.HiServiceImpl;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 服务端本地注册的容器
 */
public class InterfaceMapUtils {

    public static Map<String,Class<?>> interfaceMap = new ConcurrentHashMap<>();
    static{
        Class<HelloService> helloServiceClass = HelloService.class;
        String hello = helloServiceClass.getName();
        Class<HiService> hiServiceClass = HiService.class;
        String hi = hiServiceClass.getName();
        interfaceMap.put(hello, HelloServiceImpl.class);
        interfaceMap.put(hi, HiServiceImpl.class);
    }
}

服务端接口:

package rpcinterface;

public interface HelloService {
    String hello(String msg);
}

服务端实现类:

package rpcinterface.impl;

import rpcinterface.HelloService;

public class HelloServiceImpl implements HelloService {
    @Override
    public String hello(String msg) {
        System.out.println("收到客户端消息:"+msg);
        return "你好客户端,我已经收到你的消息:["+msg+"]";
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值