在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 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 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 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() {
@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 instances);
}
轮询实现:
public class RoundRobinLoadBalancer implements LoadBalancer { private int index = 0; @Override public Instance select(Listinstances) { 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 instances = NacosUtil.getAllInstance(serviceName);
Instance instance = loadBalancer.select(instances);
return new InetSocketAddress(instance.getIp(), instance.getPort());
} catch (NacosException e) {
logger.error("获取服务时有错误发生:", e);
}
return null;
}
}



