Netty手写微服务注册中心
主流的序列化框架
Google 的 Protobuf
Facebook 的 Thrift
Jboos 的 Marshaling
使用编码器和解码器能够让我接收到的数据直接就是一个java对象。 而不需要json
Marshaling编码器
maven依赖
<dependency> <groupId>org.jboss.marshalling</groupId> <artifactId>jboss-marshalling-serial</artifactId> <version>1.4.11.Final</version> </dependency>
实体类
/** * 存放地址信息的实体类视图 */ public class Address implements Serializable { // 生产的连接 private String address; private ChannelHandlerContext ctx; //todo 想要实现上下线 可以增加state字段 这里因为时间关系不再拓展 public Address(String address, ChannelHandlerContext ctx) { this.address = address; this.ctx = ctx; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public ChannelHandlerContext getCtx() { return ctx; } public void setCtx(ChannelHandlerContext ctx) { this.ctx = ctx; } @Override public String toString() { return "Address{" + "address='" + address + '\'' + '}'; } }
package com.shanheyongmu.netty.server.entity; import java.io.Serializable; /** * 返回给消费者的实体类视图 * 需要去除敏感信息 */ public class AddressDto implements Serializable { private String address; public AddressDto(String address) { this.address = address; } public AddressDto() { } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "AddressDto{" + "address='" + address + '\'' + '}'; } }
package com.shanheyongmu.netty.server.entity; import java.io.Serializable; /** * 生产者消费者约定实体类 */ public class Agreement implements Serializable { // 0为生产者 1为消费者 private Integer type; // 服务id private String serviceId; // 服务地址 private String address; public Agreement(Integer type, String serviceId, String address) { this.type = type; this.serviceId = serviceId; this.address = address; } public Integer getType() { return type; } public String getServiceId() { return serviceId; } public String getAddress() { return address; } public void setType(Integer type) { this.type = type; } public void setServiceId(String serviceId) { this.serviceId = serviceId; } public void setAddress(String address) { this.address = address; } }
package com.shanheyongmu.netty.server.entity; import java.io.Serializable; import java.util.List; /** * 响应 */ public class RespEntity implements Serializable { private Integer code; private String msg; private Object data; public RespEntity(Integer code, String msg) { this.code = code; this.msg = msg; } public RespEntity(Integer code, String msg, Object data) { this.code = code; this.msg = msg; this.data = data; } @Override public String toString() { return "RespEntity{" + "code=" + code + ", msg='" + msg + '\'' + ", data=" + data + '}'; } }
Marshalling 编解码器工厂
package com.shanheyongmu.netty; import io.netty.handler.codec.marshalling.*; import org.jboss.marshalling.MarshallerFactory; import org.jboss.marshalling.Marshalling; import org.jboss.marshalling.MarshallingConfiguration; /** *Marshalling 编解码器工厂 */ public final class MarshallingCodeCFactory { /** * 创建Jboss Marshalling解码器MarshallingDecoder * * @return MarshallingDecoder */ public static MarshallingDecoder buildMarshallingDecoder() { // 首先通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。 final MarshallerFactory marshallerFactory = Marshalling .getProvidedMarshallerFactory("serial"); // 创建了MarshallingConfiguration对象,配置了版本号为5 final MarshallingConfiguration configuration = new MarshallingConfiguration(); configuration.setVersion(5); // 根据marshallerFactory和configuration创建provider UnmarshallerProvider provider = new DefaultUnmarshallerProvider( marshallerFactory , configuration); // 构建Netty的MarshallingDecoder对象,俩个参数分别为provider和单个消息序列化后的最大长度 return new MarshallingDecoder(provider , 1024 * 1024 * 1); } /** * 创建Jboss Marshalling编码器MarshallingEncoder * * @return MarshallingEncoder */ public static MarshallingEncoder buildMarshallingEncoder() { final MarshallerFactory marshallerFactory = Marshalling .getProvidedMarshallerFactory("serial"); final MarshallingConfiguration configuration = new MarshallingConfiguration(); configuration.setVersion(5); MarshallerProvider provider = new DefaultMarshallerProvider( marshallerFactory , configuration); // 构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组 return new MarshallingEncoder(provider); } }
注册中心server
package com.shanheyongmu.netty.server; 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 org.springblade.merchant.netty.server.entity.Address; import org.springblade.merchant.netty.server.entity.AddressDto; import org.springblade.merchant.netty.MarshallingCodeCFactory; import org.springblade.merchant.netty.server.entity.Agreement; import org.springblade.merchant.netty.server.entity.RespEntity; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; /** * 注册中心RegistryServer * */ public class RegistryServer { public void bind(int port) throws InterruptedException { EventLoopGroup boss = new NioEventLoopGroup(); EventLoopGroup worker = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(boss, worker) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1280) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { // 新增Marshaling编码器 ch.pipeline().addLast( MarshallingCodeCFactory.buildMarshallingDecoder() ); ch.pipeline().addLast( MarshallingCodeCFactory.buildMarshallingEncoder() ); ch.pipeline().addLast( new ServerHandler() ); } }); ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync(); } finally { boss.shutdownGracefully(); worker.shutdownGracefully(); } } public static void main(String[] args) throws InterruptedException { int port = 8006; new RegistryServer().bind(port); } public static final int type_producer = 0; public static final int type_consumer = 1; // key 服务的名称 value 多个生产者集群地址列表 private static ConcurrentHashMap<String, List<Address>> keyAddres = new ConcurrentHashMap<>(); //key 为连接 value 服务名称 private static ConcurrentHashMap<ChannelHandlerContext, String> ctxs = new ConcurrentHashMap<>(); @ChannelHandler.Sharable public class ServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { Agreement agreement = (Agreement) msg; // 1.判断通讯协议类型: 生产还是消费者 switch (agreement.getType()) { case type_producer: producer(agreement, ctx); ctxs.put(ctx, agreement.getServiceId()); break; case type_consumer: consumer(agreement, ctx); // 应该存放消费者连接 break; } } /** * 生产者 * * @param agreement * @param ctx */ private void producer(Agreement agreement, ChannelHandlerContext ctx) { String serviceId = agreement.getServiceId(); // 根据服务名称查找之前是否有缓存地址数据 List<Address> listAddres = keyAddres.get(serviceId); if (listAddres == null) { listAddres = new ArrayList<>(); keyAddres.put(serviceId, listAddres); } // 注册中心插入服务地址信息 String address = agreement.getAddress(); listAddres.add(new Address(address, ctx)); } /** * 消费者 */ private void consumer(Agreement agreement, ChannelHandlerContext ctx) { String serviceId = agreement.getServiceId(); List<Address> addresEntities = keyAddres.get(serviceId); if (addresEntities == null || addresEntities.size() == 0) { ctx.writeAndFlush(new RespEntity(500, "未获取接口地址列表")); return; } // 数据格式转换 ArrayList<AddressDto> addresDtos = new ArrayList<>(); addresEntities.forEach((t) -> { addresDtos.add(new AddressDto(t.getAddress())); }); ctx.writeAndFlush(new RespEntity(200, "接口地址列表", addresDtos)); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); // 如果客户端主动断开连接的时候 走该方法 那个连接主动断开 // 服务名称 缓存3个 每个服务名称对应缓存三个集群地址列表 // private static ConcurrentHashMap<String, List<AddresEntity>> keyAddres = new ConcurrentHashMap<>(); // 多个服务名称 对应对个 服务地址集群理列表 //ChannelHandlerContext ctx==对应地址 String serviceId = ctxs.get(ctx); // 根据服务名称查找缓存地址 List<Address> addresEntities = keyAddres.get(serviceId); // 遍历缓存地址剔除 addresEntities.forEach((t) -> { if (t.getCtx() == ctx) { //剔除 addresEntities.remove(t); } }); // 注册中心通知消费者删除失效地址 // 循环遍历所有的消费者连接,通知 服务名称 那个地址 应该从内存删除 ctx.close(); } } }
编写生产者客户端
package com.shanheyongmu.netty.client; 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 org.springblade.merchant.netty.MarshallingCodeCFactory; /** * 生产者客户端 */ public class ProducerClient { public void connect(int port, String host) throws InterruptedException { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .option(ChannelOption.TCP_NODELAY, true) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { // 设置 Marshalling 编码 ch.pipeline().addLast( MarshallingCodeCFactory.buildMarshallingDecoder() ); ch.pipeline().addLast( MarshallingCodeCFactory.buildMarshallingEncoder() ); ch.pipeline().addLast(new ClientHandler()); } }); ChannelFuture f = b.connect(host, port).sync(); f.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } public static final int type_producer = 0; public static void main(String[] args) throws InterruptedException { int port = 8006; String host = "127.0.0.1"; new ProducerClient().connect(port, host); } // 客户端Handler public class ClientHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { // 发送数据给我们服务器端 ctx.writeAndFlush(new Agreement(type_producer, "shanhe-member", "192.168.110.111")); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } } }
消费者客户端
package com.shanheyongmut.netty.client; 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 org.springblade.merchant.netty.MarshallingCodeCFactory; /** * 消费者客户端 */ public class ConsumerClient { public void connect(int port, String host) throws InterruptedException { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .option(ChannelOption.TCP_NODELAY, true) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { // 设置 Marshalling 编码 ch.pipeline().addLast( MarshallingCodeCFactory.buildMarshallingDecoder() ); ch.pipeline().addLast( MarshallingCodeCFactory.buildMarshallingEncoder() ); ch.pipeline().addLast(new ConsumerClientHandler()); } }); ChannelFuture f = b.connect(host, port).sync(); f.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } public static final int type_consumer= 1; public static void main(String[] args) throws InterruptedException { int port = 8006; String host = "127.0.0.1"; new ConsumerClient().connect(port, host); } // 客户端Handler public class ConsumerClientHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { //type =1 代表消费者 Agreement agreementEntity = new Agreement(type_consumer, "shanhe-member", null); ctx.writeAndFlush(agreementEntity); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } /** * 客户端读取到服务器端数据 * * @param ctx * @param msg * @throws Exception */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { RespEntity respEntity = (RespEntity) msg; System.out.println("消费者读取数据:" + respEntity.toString()); } } }
早年同窗始相知,三载瞬逝情却萌。年少不知愁滋味,犹读红豆生南国。别离方知相思苦,心田红豆根以生。