Nacos是我们常用的技术栈,在微服务架构中承担着服务注册发现、配置中心等重要功能。除了使用Nacos之外,我们还需要了解Nacos的基本原理。
我们本章主要熟悉Nacos的服务注册的基本流程。现在的后端项目工程一般都是SpringCloud或者SpringCloudAlibaba微服务架构,在这种架构方式下启动服务提供者的时候,会注册服务到NacosServer。
service注册基本流程:
注意:本章我们只熟悉基本的service注册基本流程,其他的非注册主流程例如:心跳处理、service推送、service订阅等等,我们暂时先忽略,后期我们在回头熟悉。每章的流程尽量简单明了,突出重心。
一、自动加载NacosAutoServiceRegistration这个bean
我们日常开发中,在服务提供者会添加下图中Nacos依赖。nacos依赖里面的spring.factories配置中定义了service注册自动配置类NacosServiceRegistryAutoConfiguration。
这个类在SpringBoot启动的时候会自动加载NacosAutoServiceRegistration这个bean
@Configuration
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class,
AutoServiceRegistrationAutoConfiguration.class,
NacosDiscoveryAutoConfiguration.class })
public class NacosServiceRegistryAutoConfiguration {
...
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosAutoServiceRegistration nacosAutoServiceRegistration(
NacosServiceRegistry registry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
return new NacosAutoServiceRegistration(registry,
autoServiceRegistrationProperties, registration);
}
}
二、监听web初始化事件
NacosAutoServiceRegistration得到继承关系图如下。
service注册类实现事件监听器接口
我们看到此类继承了抽象类AbstractAutoServiceRegistration,而AbstractAutoServiceRegistration实现了ApplicationListener事件监听器接口。
AbstractAutoServiceRegistration类中监听到web服务初始化完成事件,执行这里。
public abstract class AbstractAutoServiceRegistration<R extends Registration>
implements AutoServiceRegistration, ApplicationContextAware,
ApplicationListener<WebServerInitializedEvent> {
// 监听到web服务初始化完成事件,执行这里
public void onApplicationEvent(WebServerInitializedEvent event) {
bind(event);
}
public void bind(WebServerInitializedEvent event) {
...
this.start();
}
public void start() {
...
// 只要运行标记为false,则执行下面逻辑,防止重复执行下面的注册
if (!this.running.get()) {
...
// 注册逻辑
register();
// 把运行标记置为true
this.running.compareAndSet(false, true);
}
}
}
执行register()方法具体的逻辑,由NacosAutoServiceRegistration执行。
这是一种模板方法设计模式,由抽象类定义通用模板流程,由具体子类负责实现子流程逻辑。
public class NacosAutoServiceRegistration
extends AbstractAutoServiceRegistration<Registration> {
protected void register() {
// 进行一些配置判断
if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
log.debug("Registration disabled.");
return;
}
if (this.registration.getPort() < 0) {
this.registration.setPort(getPort().get());
}
super.register();
}
}
回到抽象类AbstractAutoServiceRegistration来执行register方法。
protected void register() {
this.serviceRegistry.register(getRegistration());
}
三、调用NacosServer的接口注册service实例
由NacosServiceRegistry 执行register方法。
public class NacosServiceRegistry implements ServiceRegistry<Registration> {
public void register(Registration registration) {
...
// 封装服务实例对象
Instance instance = getNacosInstanceFromRegistration(registration);
// 注册服务实例
namingService.registerInstance(serviceId, group, instance);
}
}
3.1 封装的服务实例对象
主要是服务提供者的ip、端口等信息。
封装一个服务实例对象
3.2 调用NacosServer接口注册service实例
调用NacosServer的注册接口/nacos/v1/ns/instance去注册服务实例。
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
final Map<String, String> params = new HashMap<String, String>(16);
params.put(CommonParams.NAMESPACE_ID, namespaceId);
params.put(CommonParams.SERVICE_NAME, serviceName);
params.put(CommonParams.GROUP_NAME, groupName);
params.put(CommonParams.CLUSTER_NAME, instance.getClusterName());
params.put("ip", instance.getIp());
params.put("port", String.valueOf(instance.getPort()));
params.put("weight", String.valueOf(instance.getWeight()));
params.put("enable", String.valueOf(instance.isEnabled()));
params.put("healthy", String.valueOf(instance.isHealthy()));
params.put("ephemeral", String.valueOf(instance.isEphemeral()));
params.put("metadata", JacksonUtils.toJson(instance.getMetadata()));
// 调用接口
reqApi(UtilAndComs.nacosUrlInstance, params, HttpMethod.POST);
这个是传递的参数
四、NacosServer接口处理
@PostMapping
public String register(HttpServletRequest request) throws Exception {
...
getInstanceOperator().registerInstance(namespaceId, serviceName, instance);
}
4.1 生成service对象
由namespaceId, serviceName, ephemeral等信息生成service对象。
@Override
public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {
...
String clientId = IpPortBasedClient.getClientId(instance.toInetAddr(), ephemeral);
...
// 生成service对象
Service service = getService(namespaceId, serviceName, ephemeral);
// 注册服务实例
clientOperationService.registerInstance(service, instance, clientId);
}
registerInstance方法:
public void registerInstance(Service service, Instance instance, String clientId) throws NacosException {
...
Service singleton = ServiceManager.getInstance().getSingleton(service);
...
InstancePublishInfo instanceInfo = getPublishInfo(instance);
client.addServiceInstance(singleton, instanceInfo);
4.2 获取单例service
从单例缓存中获取service对象,没有则创建保存到map缓存并返回。
注意:为什么这里是获取单例的service对象???是为了保证相同namespaceId, serviceName只有一个service对象,这样在注册service实例的时候,才能保证service实例关联的是同一个service对象。所以必须保证service对象的唯一性。
public Service getSingleton(Service service) {
Service result = singletonRepository.computeIfAbsent(service, key -> {
...
return service;
});
// 这里的singleton其实是上面的service对象
client.addServiceInstance(singleton, instanceInfo);
4.3 缓存service实例到map
public boolean addServiceInstance(Service service, InstancePublishInfo instancePublishInfo) {
...
publishers.put(service, instancePublishInfo)
}
把service对象->instance实例的映射关系保存到ConcurrentHashMap中。publishers实际上是ConcurrentHashMap