因为Eureka的闭源,Nacos成为了现在Spring Cloud微服务注册中心的主流方案,那咱们废话不多说,直接开始读源码(基于2.2.0版本)。
首先我们知道Nacos是基于Springboot实现自动注入的,老规矩我们来到spring-cloud-alibaba-nacos-discovery包下面的META-INF\spring.factories配置文件:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration,\
com.alibaba.cloud.nacos.ribbon.RibbonNacosAutoConfiguration,\
com.alibaba.cloud.nacos.endpoint.NacosDiscoveryEndpointAutoConfiguration,\
com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration,\
com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration,\
com.alibaba.cloud.nacos.discovery.reactive.NacosReactiveDiscoveryClientConfiguration,\
com.alibaba.cloud.nacos.discovery.configclient.NacosConfigServerAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.alibaba.cloud.nacos.discovery.configclient.NacosDiscoveryClientConfigServiceBootstrapConfiguration
我们首先来到NacosDiscoveryAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
@ConditionalOnNacosDiscoveryEnabled
public class NacosDiscoveryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public NacosDiscoveryProperties nacosProperties() {
return new NacosDiscoveryProperties();
}
@Bean
@ConditionalOnMissingBean
public NacosServiceDiscovery nacosServiceDiscovery(
NacosDiscoveryProperties discoveryProperties) {
return new NacosServiceDiscovery(discoveryProperties);
}
}
这个自动配置类注入了两个Bean,第一个Bean是从配置文件中读spring.cloud.nacos.discovery前缀的配置项,然后是第二个Bean
public class NacosServiceDiscovery {
private NacosDiscoveryProperties discoveryProperties;
public NacosServiceDiscovery(NacosDiscoveryProperties discoveryProperties) {
this.discoveryProperties = discoveryProperties;
}
/**
* Return all instances for the given service.
* @param serviceId id of service
* @return list of instances
* @throws NacosException nacosException
*/
public List getInstances(String serviceId) throws NacosException {
String group = discoveryProperties.getGroup();
List instances = discoveryProperties.namingServiceInstance()
.selectInstances(serviceId, group, true);
return hostToServiceInstanceList(instances, serviceId);
}
/**
* Return the names of all services.
* @return list of service names
* @throws NacosException nacosException
*/
public List getServices() throws NacosException {
String group = discoveryProperties.getGroup();
ListView services = discoveryProperties.namingServiceInstance()
.getServicesOfServer(1, Integer.MAX_VALUE, group);
return services.getData();
}
public static List hostToServiceInstanceList(
List instances, String serviceId) {
List result = new ArrayList<>(instances.size());
for (Instance instance : instances) {
ServiceInstance serviceInstance = hostToServiceInstance(instance, serviceId);
if (serviceInstance != null) {
result.add(serviceInstance);
}
}
return result;
}
public static ServiceInstance hostToServiceInstance(Instance instance,
String serviceId) {
if (instance == null || !instance.isEnabled() || !instance.isHealthy()) {
return null;
}
NacosServiceInstance nacosServiceInstance = new NacosServiceInstance();
nacosServiceInstance.setHost(instance.getIp());
nacosServiceInstance.setPort(instance.getPort());
nacosServiceInstance.setServiceId(serviceId);
Map metadata = new HashMap<>();
metadata.put("nacos.instanceId", instance.getInstanceId());
metadata.put("nacos.weight", instance.getWeight() + "");
metadata.put("nacos.healthy", instance.isHealthy() + "");
metadata.put("nacos.cluster", instance.getClusterName() + "");
metadata.putAll(instance.getMetadata());
nacosServiceInstance.setMetadata(metadata);
if (metadata.containsKey("secure")) {
boolean secure = Boolean.parseBoolean(metadata.get("secure"));
nacosServiceInstance.setSecure(secure);
}
return nacosServiceInstance;
}
根据代码和注释可以看出,这个Bean的核心功能是根据配置文件里Nacos的相关配置,获取实例和实例对象转化的一些功能,然后是NacosDiscoveryEndpointAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Endpoint.class)
@ConditionalOnNacosDiscoveryEnabled
public class NacosDiscoveryEndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public NacosDiscoveryEndpoint nacosDiscoveryEndpoint(
NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosDiscoveryEndpoint(nacosDiscoveryProperties);
}
@Bean
@ConditionalOnEnabledHealthIndicator("nacos-discovery")
public HealthIndicator nacosDiscoveryHealthIndicator(
NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosDiscoveryHealthIndicator(
nacosDiscoveryProperties.namingServiceInstance());
}
}
这个类注入了两个Bean,第一个Bean NacosDiscoveryEndpoint 的作用是作为Nacos服务发现的端点,获取当前客户端的所有注册的服务。
第二个Bean NacosDiscoveryHealthIndicator的作用是通过向Nacos注册中心请求/operator/metrics 接口来确认健康状态。
接下来这个自动配置类NacosServiceRegistryAutoConfiguration比较核心
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
/*是否开启Nacos服务发现*/
@ConditionalOnNacosDiscoveryEnabled
/*是否开启自动注册,默认为True*/
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled",
matchIfMissing = true)
/*在以下几个配置类装配完成后才进行装配*/
@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class,
AutoServiceRegistrationAutoConfiguration.class,
NacosDiscoveryAutoConfiguration.class })
public class NacosServiceRegistryAutoConfiguration {
/*读取配置文件*/
@Bean
public NacosServiceRegistry nacosServiceRegistry(
NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosServiceRegistry(nacosDiscoveryProperties);
}
/*Nacos服务注册Bean*/
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosRegistration nacosRegistration(
NacosDiscoveryProperties nacosDiscoveryProperties,
ApplicationContext context) {
return new NacosRegistration(nacosDiscoveryProperties, context);
}
/*Nacos服务自动注册Bean*/
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosAutoServiceRegistration nacosAutoServiceRegistration(
NacosServiceRegistry registry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
return new NacosAutoServiceRegistration(registry,
autoServiceRegistrationProperties, registration);
}
}
该配置类装配了三个Bean,我们重点关注下第三个同来实现服务自动注册的Bean NacosAutoServiceRegistration
public class NacosAutoServiceRegistration
extends AbstractAutoServiceRegistration {
private static final Logger log = LoggerFactory
.getLogger(NacosAutoServiceRegistration.class);
private NacosRegistration registration;
public NacosAutoServiceRegistration(ServiceRegistry serviceRegistry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
super(serviceRegistry, autoServiceRegistrationProperties);
this.registration = registration;
}
@Deprecated
public void setPort(int port) {
getPort().set(port);
}
@Override
protected NacosRegistration getRegistration() {
if (this.registration.getPort() < 0 && this.getPort().get() > 0) {
this.registration.setPort(this.getPort().get());
}
Assert.isTrue(this.registration.getPort() > 0, "service.port has not been set");
return this.registration;
}
@Override
protected NacosRegistration getManagementRegistration() {
return null;
}
@Override
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();
}
@Override
protected void registerManagement() {
if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
return;
}
super.registerManagement();
}
@Override
protected Object getConfiguration() {
return this.registration.getNacosDiscoveryProperties();
}
@Override
protected boolean isEnabled() {
return this.registration.getNacosDiscoveryProperties().isRegisterEnabled();
}
@Override
@SuppressWarnings("deprecation")
protected String getAppName() {
String appName = registration.getNacosDiscoveryProperties().getService();
return StringUtils.isEmpty(appName) ? super.getAppName() : appName;
}
通过名字我们可以看出,这个Bean的核心是redister方法,该方法调用的是父抽象类AbstractAutoServiceRegistration中的实现,然后在父类中它又调用了接口ServiceRegistry的register方法,实际由NacosServiceRegistry实现,我们进入到NacosServiceRegistry实现的register方法
public void register(Registration registration) {
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for nacos client...");
return;
}
String serviceId = registration.getServiceId();
String group = nacosDiscoveryProperties.getGroup();
Instance instance = getNacosInstanceFromRegistration(registration);
try {
namingService.registerInstance(serviceId, group, instance);
log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,
instance.getIp(), instance.getPort());
}
catch (Exception e) {
log.error("nacos registry, {} register failed...{},", serviceId,
registration.toString(), e);
// rethrow a RuntimeException if the registration is failed.
// issue : https://github.com/alibaba/spring-cloud-alibaba/issues/1132
rethrowRuntimeException(e);
}
}
然后我们重点关注其中namingService.registerInstance方法,namingService是一个接口,由NacosNamingService实现,我们进入到方法实现
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
//首先判断该实例是临时还是持久的,默认是临时的
if (instance.isEphemeral()) {
//新建心跳对象
BeatInfo beatInfo = new BeatInfo();
//设置serviceName
beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName, groupName));
//设置IP
beatInfo.setIp(instance.getIp());
//设置端口号
beatInfo.setPort(instance.getPort());
//设置集群名
beatInfo.setCluster(instance.getClusterName());
//设置实例权重
beatInfo.setWeight(instance.getWeight());
beatInfo.setMetadata(instance.getMetadata());
beatInfo.setScheduled(false);
long instanceInterval = instance.getInstanceHeartBeatInterval();
//设置发送心跳时间间隔,默认5秒
beatInfo.setPeriod(instanceInterval == 0 ? DEFAULT_HEART_BEAT_INTERVAL : instanceInterval);
//放入定时线程池等待执行
beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo);
}
//进行服务注册
serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance);
}
该方法首先封装了一个心跳对象,并放入ScheduledThreadPoolExecutor线程池中,默认每5秒执行一次,然后调用注册中心代理serverProxy通过向注册中心/instance接口发送进行服务注册
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}",
namespaceId, serviceName, instance);
final Map params = new HashMap(9);
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", JSON.toJSONString(instance.getMetadata()));
reqAPI(UtilAndComs.NACOS_URL_INSTANCE, params, HttpMethod.POST);
}
我们来到注册中心的/instance接口,重点关注InstanceController的register方法
@CanDistro
@PostMapping
@TpsControl(pointName = "NamingInstanceRegister", name = "HttpNamingInstanceRegister")
@Secured(action = ActionTypes.WRITE)
public String register(HttpServletRequest request) throws Exception {
final String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID,
Constants.DEFAULT_NAMESPACE_ID);
final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
NamingUtils.checkServiceNameFormat(serviceName);
final Instance instance = HttpRequestInstanceBuilder.newBuilder()
.setDefaultInstanceEphemeral(switchDomain.isDefaultInstanceEphemeral()).setRequest(request).build();
getInstanceOperator().registerInstance(namespaceId, serviceName, instance);
NotifyCenter.publishEvent(new RegisterInstanceTraceEvent(System.currentTimeMillis(), "", false, namespaceId,
NamingUtils.getGroupName(serviceName), NamingUtils.getServiceName(serviceName), instance.getIp(),
instance.getPort()));
return "ok";
}
重点关注getInstanceOperator().registerInstance(namespaceId, serviceName, instance)方法
/**
* This method creates {@code IpPortBasedClient} if it doesn't exist.
*/
@Override
public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {
NamingUtils.checkInstanceIsLegal(instance);
//判断是否临时
boolean ephemeral = instance.isEphemeral();
//获取客户端ID
String clientId = IpPortBasedClient.getClientId(instance.toInetAddr(), ephemeral);
//如果客户端不存在则新建
createIpPortClientIfAbsent(clientId);
//封装service对象
Service service = getService(namespaceId, serviceName, ephemeral);
//进行实例注册
clientOperationService.registerInstance(service, instance, clientId);
}
clientOperationService为一个接口且有多个实现,我们查看其实现类找到EphemeralClientOperationServiceImpl中的registerInstance方法
public void registerInstance(Service service, Instance instance, String clientId) throws NacosException {
//检查实例是否合法
NamingUtils.checkInstanceIsLegal(instance);
//获取单例的service
Service singleton = ServiceManager.getInstance().getSingleton(service);
if (!singleton.isEphemeral()) {
throw new NacosRuntimeException(NacosException.INVALID_PARAM,
String.format("Current service %s is persistent service, can't register ephemeral instance.",
singleton.getGroupedServiceName()));
}
//获取客户端
Client client = clientManager.getClient(clientId);
if (!clientIsLegal(client, clientId)) {
return;
}
//封装实例发布信息
InstancePublishInfo instanceInfo = getPublishInfo(instance);
client.addServiceInstance(singleton, instanceInfo);
client.setLastUpdatedTime();
client.recalculateRevision();
//通知中心发布客户端服务注册事件
NotifyCenter.publishEvent(new ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId));
NotifyCenter
.publishEvent(new MetadataEvent.InstanceMetadataEvent(singleton, instanceInfo.getMetadataId(), false));
}
然后我们关注publishEvent
/**
* Request publisher publish event Publishers load lazily, calling publisher.
*
* @param eventType class Instances type of the event type.
* @param event event instance.
*/
private static boolean publishEvent(final Class extends Event> eventType, final Event event) {
if (ClassUtils.isAssignableFrom(SlowEvent.class, eventType)) {
return INSTANCE.sharePublisher.publish(event);
}
final String topic = ClassUtils.getCanonicalName(eventType);
EventPublisher publisher = INSTANCE.publisherMap.get(topic);
if (publisher != null) {
return publisher.publish(event);
}
if (event.isPluginEvent()) {
return true;
}
LOGGER.warn("There are no [{}] publishers for this event, please register", topic);
return false;
}
在DefaultPublisher中的publish方法,加入阻塞队列等待执行
@Override
public boolean publish(Event event) {
checkIsStart();
//加入阻塞队列尾部
boolean success = this.queue.offer(event);
if (!success) {
LOGGER.warn("Unable to plug in due to interruption, synchronize sending time, event : {}", event);
//接收事件
receiveEvent(event);
return true;
}
return true;
}
void checkIsStart() {
if (!initialized) {
throw new IllegalStateException("Publisher does not start");
}
}
@Override
public void shutdown() {
this.shutdown = true;
this.queue.clear();
}
public boolean isInitialized() {
return initialized;
}
/**
* Receive and notifySubscriber to process the event.
*
* @param event {@link Event}.
*/
void receiveEvent(Event event) {
final long currentEventSequence = event.sequence();
//检查有无订阅者
if (!hasSubscriber()) {
LOGGER.warn("[NotifyCenter] the {} is lost, because there is no subscriber.", event);
return;
}
// Notification single event listener
for (Subscriber subscriber : subscribers) {
if (!subscriber.scopeMatches(event)) {
continue;
}
// Whether to ignore expiration events
if (subscriber.ignoreExpireEvent() && lastEventSequence > currentEventSequence) {
LOGGER.debug("[NotifyCenter] the {} is unacceptable to this subscriber, because had expire",
event.getClass());
continue;
}
// Because unifying smartSubscriber and subscriber, so here need to think of compatibility.
// Remove original judge part of codes.
//通知订阅者
notifySubscriber(subscriber, event);
}
}
@Override
public void notifySubscriber(final Subscriber subscriber, final Event event) {
LOGGER.debug("[NotifyCenter] the {} will received by {}", event, subscriber);
final Runnable job = () -> subscriber.onEvent(event);
final Executor executor = subscriber.executor();
if (executor != null) {
executor.execute(job);
} else {
try {
//执行事件
job.run();
} catch (Throwable e) {
LOGGER.error("Event callback exception: ", e);
}
}
}
然后我们通过ClientRegisterServiceEvent的其他引用找到了ClientServiceIndexesManager中的onEvent方法,发现ClientRegisterServiceEvent事件被封装成了ServiceChangedEvent并发布
@Override
public void onEvent(Event event) {
if (event instanceof ClientOperationEvent.ClientReleaseEvent) {
handleClientDisconnect((ClientOperationEvent.ClientReleaseEvent) event);
} else if (event instanceof ClientOperationEvent) {
handleClientOperation((ClientOperationEvent) event);
}
}
private void handleClientDisconnect(ClientOperationEvent.ClientReleaseEvent event) {
Client client = event.getClient();
for (Service each : client.getAllSubscribeService()) {
removeSubscriberIndexes(each, client.getClientId());
}
DeregisterInstanceReason reason = event.isNative()
? DeregisterInstanceReason.NATIVE_DISCONNECTED : DeregisterInstanceReason.SYNCED_DISCONNECTED;
long currentTimeMillis = System.currentTimeMillis();
for (Service each : client.getAllPublishedService()) {
removePublisherIndexes(each, client.getClientId());
InstancePublishInfo instance = client.getInstancePublishInfo(each);
NotifyCenter.publishEvent(new DeregisterInstanceTraceEvent(currentTimeMillis,
"", false, reason, each.getNamespace(), each.getGroup(), each.getName(),
instance.getIp(), instance.getPort()));
}
}
private void handleClientOperation(ClientOperationEvent event) {
Service service = event.getService();
String clientId = event.getClientId();
if (event instanceof ClientOperationEvent.ClientRegisterServiceEvent) {
addPublisherIndexes(service, clientId);
} else if (event instanceof ClientOperationEvent.ClientDeregisterServiceEvent) {
removePublisherIndexes(service, clientId);
} else if (event instanceof ClientOperationEvent.ClientSubscribeServiceEvent) {
addSubscriberIndexes(service, clientId);
} else if (event instanceof ClientOperationEvent.ClientUnsubscribeServiceEvent) {
removeSubscriberIndexes(service, clientId);
}
}
private void addPublisherIndexes(Service service, String clientId) {
publisherIndexes.computeIfAbsent(service, key -> new ConcurrentHashSet<>());
publisherIndexes.get(service).add(clientId);
NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true));
}
而Nacos注册中心是通过JRaftServer的NacosStateMachine中RequestProcessor对象去执行onApply方法,而RequestProcessor为抽象类,其实现类为InstanceMetadataProcessor,我们来到其中的onApply方法
@Override
public Response onApply(WriteRequest request) {
readLock.lock();
try {
MetadataOperation op = serializer.deserialize(request.getData().toByteArray(), processType);
switch (DataOperation.valueOf(request.getOperation())) {
case ADD:
case CHANGE:
updateInstanceMetadata(op);
break;
case DELETE:
deleteInstanceMetadata(op);
break;
default:
return Response.newBuilder().setSuccess(false)
.setErrMsg("Unsupported operation " + request.getOperation()).build();
}
return Response.newBuilder().setSuccess(true).build();
} catch (Exception e) {
Loggers.RAFT.error("onApply {} instance metadata operation failed. ", request.getOperation(), e);
String errorMessage = null == e.getMessage() ? e.getClass().getName() : e.getMessage();
return Response.newBuilder().setSuccess(false).setErrMsg(errorMessage).build();
} finally {
readLock.unlock();
}
}
private void updateInstanceMetadata(MetadataOperation op) {
Service service = Service.newService(op.getNamespace(), op.getGroup(), op.getServiceName());
service = ServiceManager.getInstance().getSingleton(service);
namingMetadataManager.updateInstanceMetadata(service, op.getTag(), op.getMetadata());
NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true));
}
private void deleteInstanceMetadata(MetadataOperation op) {
Service service = Service.newService(op.getNamespace(), op.getGroup(), op.getServiceName());
service = ServiceManager.getInstance().getSingleton(service);
namingMetadataManager.removeInstanceMetadata(service, op.getTag());
}
核心在NamingMetadataManager的updateInstanceMetadata中
private ConcurrentMap> instanceMetadataMap;
/**
* Update instance metadata.
*
* @param service service
* @param metadataId instance metadata id
* @param instanceMetadata new instance metadata
*/
public void updateInstanceMetadata(Service service, String metadataId, InstanceMetadata instanceMetadata) {
if (!instanceMetadataMap.containsKey(service)) {
instanceMetadataMap.putIfAbsent(service, new ConcurrentHashMap<>(INITIAL_CAPACITY));
}
instanceMetadataMap.get(service).put(metadataId, instanceMetadata);
}
其存放客户端实例的对象是一个两层的ConcurrentHashMap,第一层的Map key为service对象,第二层的Map Key为具体的实例Id,value为实例对象。