在RpcServer关闭的时候,要清除nacos中对应的服务信息。由于不知道服务器什么什么时候关闭,所以需要钩子函数。执行在某些事件之后的函数就叫钩子函数,把注销服务的方法写入系统关闭的钩子函数就可以起到善后的工作。实现nacos的一个工具类:NacosUtil,今后nacos服务注册和服务发现都要借助这个工具类中,所有的服务名都会被存在NacosUtil的成员变量serviceNames中,当RpcServer关闭的时候,遍历这个set,调用 namingService.deregisterInstance,清除nacos中所有的服务
public class NacosUtil { private static final Logger logger = LoggerFactory.getLogger(NacosUtil.class); private static final NamingService namingService; private static final Set<String> serviceNames = new HashSet<>(); private static InetSocketAddress address; private static final String SERVER_ADDR = "127.0.0.1:8848"; static { namingService = getNacosNamingService(); } public static NamingService getNacosNamingService() { try { return NamingFactory.createNamingService(SERVER_ADDR); } catch (NacosException e) { logger.error("连接到Nacos时有错误发生: ", e); throw new RpcException(RpcError.FAILED_TO_CONNECT_TO_SERVICE_REGISTRY); } } public static void registerService(String serviceName, InetSocketAddress address) throws NacosException { namingService.registerInstance(serviceName, address.getHostName(), address.getPort()); NacosUtil.address = address; serviceNames.add(serviceName); } public static List<Instance> getAllInstance(String serviceName) throws NacosException { return namingService.getAllInstances(serviceName); } public static void clearRegistry() { if(!serviceNames.isEmpty() && address != null) { String host = address.getHostName(); int port = address.getPort(); Iterator<String> iterator = serviceNames.iterator(); while(iterator.hasNext()) { String serviceName = iterator.next(); try { namingService.deregisterInstance(serviceName, host, port); } catch (NacosException e) { logger.error("注销服务 {} 失败", serviceName, e); } } } } }
接下来就是钩子,钩子在RpcServer关闭的时候调用,在 addClearAllHook 中,Runtime 对象是 JVM 虚拟机的运行时环境,调用其 addShutdownHook 方法增加一个钩子函数,创建一个新线程调用 clearRegistry 方法完成注销工作。这个钩子函数会在 JVM 关闭之前被调用。
public class ShutdownHook { private static final Logger logger = LoggerFactory.getLogger(ShutdownHook.class); private static final ShutdownHook shutdownHook = new ShutdownHook(); public static ShutdownHook getShutdownHook() { return shutdownHook; } public void addClearAllHook() { logger.info("关闭后将自动注销所有服务"); Runtime.getRuntime().addShutdownHook(new Thread(() -> { NacosUtil.clearRegistry(); ThreadPoolFactory.shutDownAll(); })); } }
在Rpcserver启动的时候注册这个钩子:
@Override public void start() { ShutdownHook.getShutdownHook().addClearAllHook(); EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .option(ChannelOption.SO_BACKLOG, 256) .option(ChannelOption.SO_KEEPALIVE, true) .childOption(ChannelOption.TCP_NODELAY, true) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS)) .addLast(new CommonEncoder(serializer)) .addLast(new CommonDecoder()) .addLast(new NettyServerHandler()); } }); ChannelFuture future = serverBootstrap.bind(host, port).sync(); future.channel().closeFuture().sync(); } catch (InterruptedException e) { logger.error("启动服务器时有错误发生: ", e); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } }
负载均衡则是 则是Rpcclient从nacos中获取所有的服务列表的时候选择服务的标准,最简单的负载均衡就是轮询,定义负载均衡算法的接口:
public interface LoadBalancer { Instance select(List<Instance> instances); }
轮询实现:
public class RoundRobinLoadBalancer implements LoadBalancer { private int index = 0; @Override public Instance select(List<Instance> instances) { if(index >= instances.size()) { index %= instances.size(); } return instances.get(index++); } }
集成到服务发现类中:
public class NacosServiceDiscovery implements ServiceDiscovery { private static final Logger logger = LoggerFactory.getLogger(NacosServiceDiscovery.class); private final LoadBalancer loadBalancer; public NacosServiceDiscovery(LoadBalancer loadBalancer) { if(loadBalancer == null) this.loadBalancer = new RandomLoadBalancer(); else this.loadBalancer = loadBalancer; } @Override public InetSocketAddress lookupService(String serviceName) { try { List<Instance> instances = NacosUtil.getAllInstance(serviceName); Instance instance = loadBalancer.select(instances); return new InetSocketAddress(instance.getIp(), instance.getPort()); } catch (NacosException e) { logger.error("获取服务时有错误发生:", e); } return null; } }