距离写的代码的时间比较久了,在这里只是简单的总结介绍一下。
简单实现了基于Netty的RPC框架并将其注册到Nacos,介绍内容包含序列化自定义协议负载均衡算法Nacos相关服务
文末有源码链接。
著名的分布式服务框架Dubbo使用Dubbo协议进行节点间通信,而Dubbo协议默认使用Netty作为基础通信组件。还有Zookeeper,RocketMQ等底层rpc通讯也使用的是Netty。因此学习Netty对掌握这些框架原理还是比不可少的。
序列化:
为实现传输数据和通信,序列化是比不可少的,选择合适的序列化算法是非常重要的,通常要选择性能高,生成字节数少的算法。这里我准备了以下实现:JDK自带的,Json ,还有一个貌似当初因为时间关系没实现的Protobuf,以后有时间补上。

/**
     * JDK原生序列化
     */
    Java{
        @Override
        public <T> T deserialize(Class<T> clazz, byte[] bytes) {
            try {
                ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
                return (T) ois.readObject();
            } catch (IOException | ClassNotFoundException e) {
                log.error("Failed to serialize deserialize java");
                e.printStackTrace();
            }
            return null;
        }

        @Override
        public <T> byte[] serialize(T object) {

            try {
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(bos);
                oos.writeObject(object);
                return bos.toByteArray();
            } catch (IOException e) {
                log.error("Failed to serialize serialize java");
                e.printStackTrace();
            }
            return null;
        }
    },
    /**
     * Json序列化
     */
    Json{
        @Override
        public <T> T deserialize(Class<T> clazz, byte[] bytes) {
            Gson gson = new GsonBuilder().registerTypeAdapter(Class.class, new SerializeType.ClassCodec()).create();
            //Gson gson=new Gson();
            String json = new String(bytes, StandardCharsets.UTF_8);
            log.debug("deserialize--{}",json);
            System.out.println("json--deserialize--"+json);
            return gson.fromJson(json, clazz);
        }

        @Override
        public <T> byte[] serialize(T object) {
            //Gson gson = new GsonBuilder().registerTypeAdapter(Class.class, new SerializeType.ClassCodec()).create();
            Gson gson=new Gson();
            String json = null;
            try {
                json = gson.toJson(object);
            } catch (Exception e) {
                e.printStackTrace();
            }
            log.debug("serialize--{}",json);
            System.out.println("json--serialize--"+json);
            return json.getBytes(StandardCharsets.UTF_8);
        }
    },
    /**
     * google Protobuf 序列化
     */
    Protobuf{
        @Override
        public <T> T deserialize(Class<T> clazz, byte[] bytes) {
            return null;
        }

        @Override
        public <T> byte[] serialize(T object) {
            return new byte[0];
        }
    };


    /**
     * 自定义类型转换器,解决 Gson String序列化
     */
    class ClassCodec implements JsonSerializer<Class<?>>, JsonDeserializer<Class<?>> {

        @Override
        public Class<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            try {
                String str = json.getAsString();
                return Class.forName(str);
            } catch (ClassNotFoundException e) {
                throw new JsonParseException(e);
            }
        }

        @Override             //   String.class
        public JsonElement serialize(Class<?> src, Type typeOfSrc, JsonSerializationContext context) {
            // class -> json
            return new JsonPrimitive(src.getName());
        }
    }

自定义的协议

@ChannelHandler.Sharable
public class MessageCodec extends MessageToMessageCodec<ByteBuf, Message>        {
    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, Message message, List<Object> outList) throws Exception {
        ByteBuf out = channelHandlerContext.alloc().buffer();
        //魔数 5字节
        out.writeBytes(new byte[]{'b','a','i','y','e'});
        //版本号 1字节
        out.writeByte(1);
        //序列化方式 1字节
        out.writeByte(Config.SERIALIZER.ordinal());
        //消息类型 1字节
        out.writeByte(message.getMessageType());
        // 消息序列号 8字节
        out.writeBytes(Longs.toByteArray(message.getSequenceId()));
        //序列化后的消息内容
        byte[] bytes = new byte[0];
        try {
            bytes = Config.SERIALIZER.serialize(message);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //消息长度
        out.writeInt(bytes.length);
        //写入消息内容
        out.writeBytes(bytes);
        outList.add(out);
    }

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
        byte[] magic=new byte[5];
        //魔数 5字节
        byteBuf.readBytes(magic,0,magic.length);
        //版本号 1字节
        byte version=byteBuf.readByte();
        //序列化方式 1字节
        byte serializationType = byteBuf.readByte();
        //消息类型 1字节
        byte messageType=byteBuf.readByte();
        //消息序列号 8字节
        long sequenceId=byteBuf.readLong();
        //消息长度
        int length=byteBuf.readInt();
        //消息具体内容
        byte[] bytes = new byte[length];
        byteBuf.readBytes(bytes, 0, length);
        //反序列化后的消息内容
        SerializeType type = SerializeType.values()[serializationType];
        Class<? extends Message> messageClass = Message.getMessageClass(messageType);
        Message message = null;
        try {
            message = type.deserialize(messageClass, bytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        list.add(message);

    }
}

负载均衡算法:实现了两个分别是:随机和轮换

public interface LoadBalancer {
    Instance getInstance(List<Instance> instances);
}
public class RandomLoadBalance implements LoadBalancer{
    private static final Random random = new Random();
    @Override
    public Instance getInstance(List<Instance> instances) {
        return instances.get(random.nextInt(instances.size()));
    }
}

public class RoundRobinLoadBalance implements LoadBalancer{
    private AtomicInteger atomicInteger=new AtomicInteger(0);
    @Override
    public Instance getInstance(List<Instance> instances) {
        int current;
        int next;
        do{
            current=atomicInteger.get();
            next = current >= Integer.MAX_VALUE ? 0 : current + 1;
        }while (!atomicInteger.compareAndSet(current,next));

        return instances.get(next%instances.size());

    }
}

解决粘包/拆包的方法

Netty对粘包和拆包问题的处理
Netty对解决粘包和拆包的方案做了抽象,提供了一些解码器(Decoder)来解决粘包和拆包的问题。如:

LineBasedFrameDecoder:以行为单位进行数据包的解码;
DelimiterBasedFrameDecoder:以特殊的符号作为分隔来进行数据包的解码;
FixedLengthFrameDecoder:以固定长度进行数据包的解码;
LenghtFieldBasedFrameDecode:适用于消息头包含消息长度的协议(最常用);

主要思想是添加一个长度字段,用于记录偏移量。

public class ProcotolFrameDecoder extends LengthFieldBasedFrameDecoder {
    public ProcotolFrameDecoder() {
        this(1024, 16, 4, 0, 0);
    }
    public ProcotolFrameDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip) {
        super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip);
    }
}

关于在Nacos获取服务的部分:
自动扫描注册

public static void registerTogNacos(){
        Set<Class<?>> classes = ServicesFactory.map.keySet();
        for (Class clazz:classes
             ) {
            try {
                NacosRegistryCentre.namingService.registerInstance(clazz.getName(),Config.IP,Config.PORT);
            } catch (NacosException e) {
                log.error("{} 注册nacos异常",clazz.getName());
                e.printStackTrace();
            }
        }

    }


public class NacosRegistryCentre implements RegistryCentre{
    /** dubbo代码 内存中的缓存notified是ConcurrentHashMap里面又嵌套了一个Map,外层Map的key是消费者的URL,内层Map的key是分类,包含providers、consumers、coutes、configurators四种。
     * value则是对应的服务列表,对于没有服务提供者提供服务的URL,它会以特殊的empty://前缀开头。
       服务实例缓存,初始化时从注册中心初始化一次 */
    private static ConcurrentHashMap<URL, Map<String,List<URL>>> notified = new ConcurrentHashMap<>();

    /** 如同dubbo的缓存               服务名       分组      实例 */
    private static ConcurrentHashMap<String, Map<String,List<URL>>> instancesCache = new ConcurrentHashMap<>();

    private final RoundRobinLoadBalance roundRobinLoadBalance=new RoundRobinLoadBalance();

    public static NamingService namingService;
    static {
        try {
            namingService = NamingFactory.createNamingService(Config.NACOS_SERVER_ADDR);
        } catch (NacosException e) {
            log.error("Failed to connect to nacos");
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    @Override
    public Instance getServices(String serviceName, String GroupName) {
        try {
            List<Instance> allInstances = namingService.getAllInstances(serviceName, GroupName);
            return roundRobinLoadBalance.getInstance(allInstances);
        } catch (NacosException e) {
            e.printStackTrace();
        }
        throw new RuntimeException("no services efficient");
    }

    @Override
    public Instance getServices(String serviceName) {
        try {
            List<Instance> allInstances = namingService.getAllInstances(serviceName);
            return roundRobinLoadBalance.getInstance(allInstances);
        } catch (NacosException e) {
            e.printStackTrace();
        }
        throw new RuntimeException("no services efficient");

    }
}

源码:https://gitee.com/alice-175/netty-rpc