纵有疾风起
人生不言弃

@EnableDiscoveryClient 注解如何实现服务注册与发现

@EnableDiscoveryClient 是如何实现服务注册的?
我们首先需要了解 Spring-Cloud-Commons 这个模块,Spring-Cloud-Commons 是 Spring-Cloud 官方提供的一套抽象层,类似于 JDBC 一样,提供了一套规范,具体的实现有实现厂商去根据标准实现,在Finchley版中, Spring-Cloud-Commons 共提供了6个模块标准规范。

actuator
circuitbreaker
discovery
hypermedia
loadbalancer
serviceregistry
在今天的文章中,我们一起来探讨学习一下 discovery、serviceregistry这两个模块,我们使用 alibaba 的 nacos-discovery 实现来进行学习。
@EnableDiscoveryClient 注解做了什么事?

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@Import(EnableDiscoveryClientImportSelector.class)public @interface EnableDiscoveryClient {    /**     * If true, the ServiceRegistry will automatically register the local server.     */    boolean autoRegister() default true;}

从EnableDiscoveryClient源码可以看出该接口有一个autoRegister()方法默认返回值是true,它还做了一件非常重要的事,引用了EnableDiscoveryClientImportSelector类。为什么说这个类非常重要呢?我们来看看就知道了

EnableDiscoveryClientImportSelector 类做了什么事?

@Order(Ordered.LOWEST_PRECEDENCE - 100)public class EnableDiscoveryClientImportSelector        extends SpringFactoryImportSelector<EnableDiscoveryClient> {    @Override    public String[] selectImports(AnnotationMetadata metadata) {        String[] imports = super.selectImports(metadata);        AnnotationAttributes attributes = AnnotationAttributes.fromMap(                metadata.getAnnotationAttributes(getAnnotationClass().getName(), true));        boolean autoRegister = attributes.getBoolean("autoRegister");        if (autoRegister) {            List<String> importsList = new ArrayList<>(Arrays.asList(imports));            importsList.add("org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");            imports = importsList.toArray(new String[0]);        } else {            Environment env = getEnvironment();            if(ConfigurableEnvironment.class.isInstance(env)) {                ConfigurableEnvironment configEnv = (ConfigurableEnvironment)env;                LinkedHashMap<String, Object> map = new LinkedHashMap<>();                map.put("spring.cloud.service-registry.auto-registration.enabled", false);                MapPropertySource propertySource = new MapPropertySource(                        "springCloudDiscoveryClient", map);                configEnv.getPropertySources().addLast(propertySource);            }        }        return imports;    }    @Override    protected boolean isEnabled() {        return getEnvironment().getProperty(                "spring.cloud.discovery.enabled", Boolean.class, Boolean.TRUE);    }    @Override    protected boolean hasDefaultFactory() {        return true;    }}

将焦点聚集到selectImports()方法上,该类获取了autoRegister 的值。

当autoRegister=true 时,将AutoServiceRegistrationConfiguration类添加到自动装配中,系统就会去自动装配AutoServiceRegistrationConfiguration类,在具体的实现中自动装配类都是在这个AutoServiceRegistrationConfiguration类自动装配完成后才装配的,也就是说autoRegister=true就更够实现服务注册

当autoRegister=false时,将spring.cloud.service-registry.auto-registration.enabled 设置成了 false,这样跟注册相关的类将不会自动装配,因为自动注册相关的类都有一个条件装配@ConditionalOnProperty(value = “spring.cloud.service-registry.auto-registration.enabled”, matchIfMissing = true),换句话说如果我们不想该服务注册到注册中心,只是想从注册中心拉取服务,我们只需要引导类上的注解改成@EnableDiscoveryClient(autoRegister = false)

nacos 是如何根据标准去实现服务注册的?
我们先看看在org.springframework.cloud.alibaba.nacos包下的NacosDiscoveryAutoConfiguration类

NacosDiscoveryAutoConfiguration类做了些什么?

@Configuration@EnableConfigurationProperties@ConditionalOnNacosDiscoveryEnabled@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class,        AutoServiceRegistrationAutoConfiguration.class })public class NacosDiscoveryAutoConfiguration {    @Bean    public NacosServiceRegistry nacosServiceRegistry(            NacosDiscoveryProperties nacosDiscoveryProperties) {        return new NacosServiceRegistry(nacosDiscoveryProperties);    }    @Bean    @ConditionalOnBean(AutoServiceRegistrationProperties.class)    public NacosRegistration nacosRegistration(            NacosDiscoveryProperties nacosDiscoveryProperties,            ApplicationContext context) {        return new NacosRegistration(nacosDiscoveryProperties, context);    }    @Bean    @ConditionalOnBean(AutoServiceRegistrationProperties.class)    public NacosAutoServiceRegistration nacosAutoServiceRegistration(            NacosServiceRegistry registry,            AutoServiceRegistrationProperties autoServiceRegistrationProperties,            NacosRegistration registration) {        return new NacosAutoServiceRegistration(registry,                autoServiceRegistrationProperties, registration);    }}

该类的自动装配是在AutoServiceRegistrationConfiguration之后完成,当autoRegister设置为false时,NacosDiscoveryAutoConfiguration就不会装配,也就意味着服务不会像注册中心进行注册。好了我们还是来看看NacosDiscoveryAutoConfiguration干了些啥吧,主要是装配了NacosServiceRegistry、NacosRegistration、NacosAutoServiceRegistration三个bean,来看看三个bean干了那些骚操作。

NacosServiceRegistry类做了些什么?

public class NacosServiceRegistry implements ServiceRegistry<Registration> {    private static final Logger log = LoggerFactory.getLogger(NacosServiceRegistry.class);    private final NacosDiscoveryProperties nacosDiscoveryProperties;    private final NamingService namingService;    public NacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {        this.nacosDiscoveryProperties = nacosDiscoveryProperties;        this.namingService = nacosDiscoveryProperties.namingServiceInstance();    }    @Override    public void register(Registration registration) {        if (StringUtils.isEmpty(registration.getServiceId())) {            log.warn("No service to register for nacos client...");            return;        }        String serviceId = registration.getServiceId();        Instance instance = new Instance();        instance.setIp(registration.getHost());        instance.setPort(registration.getPort());        instance.setWeight(nacosDiscoveryProperties.getWeight());        instance.setClusterName(nacosDiscoveryProperties.getClusterName());        instance.setMetadata(registration.getMetadata());        try {            namingService.registerInstance(serviceId, instance);            log.info("nacos registry, {} {}:{} register finished", serviceId,                    instance.getIp(), instance.getPort());        }        catch (Exception e) {            log.error("nacos registry, {} register failed...{},", serviceId,                    registration.toString(), e);        }    }    @Override    public void deregister(Registration registration) {        log.info("De-registering from Nacos Server now...");        if (StringUtils.isEmpty(registration.getServiceId())) {            log.warn("No dom to de-register for nacos client...");            return;        }        NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();        String serviceId = registration.getServiceId();        try {            namingService.deregisterInstance(serviceId, registration.getHost(),                    registration.getPort(), nacosDiscoveryProperties.getClusterName());        }        catch (Exception e) {            log.error("ERR_NACOS_DEREGISTER, de-register failed...{},",                    registration.toString(), e);        }        log.info("De-registration finished.");    }    @Override    public void close() {    }    @Override    public void setStatus(Registration registration, String status) {        // nacos doesn't support set status of a particular registration.    }    @Override    public <T> T getStatus(Registration registration) {        // nacos doesn't support query status of a particular registration.        return null;    }}

该类实现了 spring-cloud-commons 提供的 ServiceRegistry 接口,重写了register、deregister两个方法,在register
方法中主要是将配置文件装换成Instance实例,调用了namingService.registerInstance(serviceId, instance);方法,这个方法应该是服务端提供的服务注册方法。这一顿操作之后,服务就注册好了。

NacosRegistration类做了些什么?

public class NacosRegistration implements Registration, ServiceInstance {    public static final String MANAGEMENT_PORT = "management.port";    public static final String MANAGEMENT_CONTEXT_PATH = "management.context-path";    public static final String MANAGEMENT_ADDRESS = "management.address";    public static final String MANAGEMENT_ENDPOINT_BASE_PATH = "management.endpoints.web.base-path";    private NacosDiscoveryProperties nacosDiscoveryProperties;    private ApplicationContext context;    public NacosRegistration(NacosDiscoveryProperties nacosDiscoveryProperties,            ApplicationContext context) {        this.nacosDiscoveryProperties = nacosDiscoveryProperties;        this.context = context;    }    @PostConstruct    public void init() {        Map<String, String> metadata = nacosDiscoveryProperties.getMetadata();        Environment env = context.getEnvironment();        String endpointBasePath = env.getProperty(MANAGEMENT_ENDPOINT_BASE_PATH);        if (!StringUtils.isEmpty(endpointBasePath)) {            metadata.put(MANAGEMENT_ENDPOINT_BASE_PATH, endpointBasePath);        }        Integer managementPort = ManagementServerPortUtils.getPort(context);        if (null != managementPort) {            metadata.put(MANAGEMENT_PORT, managementPort.toString());            String contextPath = env                    .getProperty("management.server.servlet.context-path");            String address = env.getProperty("management.server.address");            if (!StringUtils.isEmpty(contextPath)) {                metadata.put(MANAGEMENT_CONTEXT_PATH, contextPath);            }            if (!StringUtils.isEmpty(address)) {                metadata.put(MANAGEMENT_ADDRESS, address);            }        }    }    @Override    public String getServiceId() {        return nacosDiscoveryProperties.getService();    }    @Override    public String getHost() {        return nacosDiscoveryProperties.getIp();    }    @Override    public int getPort() {        return nacosDiscoveryProperties.getPort();    }    public void setPort(int port) {        this.nacosDiscoveryProperties.setPort(port);    }    @Override    public boolean isSecure() {        return nacosDiscoveryProperties.isSecure();    }    @Override    public URI getUri() {        return DefaultServiceInstance.getUri(this);    }    @Override    public Map<String, String> getMetadata() {        return nacosDiscoveryProperties.getMetadata();    }    public boolean isRegisterEnabled() {        return nacosDiscoveryProperties.isRegisterEnabled();    }    public String getCluster() {        return nacosDiscoveryProperties.getClusterName();    }    public float getRegisterWeight() {        return nacosDiscoveryProperties.getWeight();    }    public NacosDiscoveryProperties getNacosDiscoveryProperties() {        return nacosDiscoveryProperties;    }    public NamingService getNacosNamingService() {        return nacosDiscoveryProperties.namingServiceInstance();    }    @Override    public String toString() {        return "NacosRegistration{" + "nacosDiscoveryProperties="                + nacosDiscoveryProperties + '}';    }}

该类主要是装配了一些management管理类的配置信息

NacosAutoServiceRegistration类做了些什么事情?

public class NacosAutoServiceRegistration        extends AbstractAutoServiceRegistration<Registration> {    private static final Logger log = LoggerFactory            .getLogger(NacosAutoServiceRegistration.class);    private NacosRegistration registration;    public NacosAutoServiceRegistration(ServiceRegistry<Registration> 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;    }}

这个类主要是调用NacosServiceRegistryregister()方法,我们来关注一下他的父类AbstractAutoServiceRegistrationstart(),这个才是启动方法。

    public void start() {        if (!isEnabled()) {            if (logger.isDebugEnabled()) {                logger.debug("Discovery Lifecycle disabled. Not starting");            }            return;        }        // only initialize if nonSecurePort is greater than 0 and it isn't already running        // because of containerPortInitializer below        if (!this.running.get()) {            register();            if (shouldRegisterManagement()) {                registerManagement();            }            this.context.publishEvent(                    new InstanceRegisteredEvent<>(this, getConfiguration()));            this.running.compareAndSet(false, true);        }    }    ......省略    protected void register() {        this.serviceRegistry.register(getRegistration());    }

在start()方法里调用了NacosAutoServiceRegistration.register方法,NacosAutoServiceRegistration.register的方法里又调用了父类AbstractAutoServiceRegistration.register方法,在父类AbstractAutoServiceRegistration.register方法里调用了NacosServiceRegistry.register方法,实现了服务注册。

服务注册大概经历了这么多,有兴趣的可以自己出看看源码,相信你肯定比我理解的更好。下面是个人学习 Nacos 服务注册的源码阅读流程图,其他的实现也差不多,主要是要理解 Spring-Cloud-Commons 的规范。

@EnableDiscoveryClient执行流程.png

@EnableDiscoveryClient 是如何实现服务发现?

通过上面我们知道了 Spring-Cloud-Commons 模块实现了一套规范,我们直接去看在服务发现的规范是什么?我们能够找到DiscoveryClient接口。

public interface DiscoveryClient {    /**     * A human readable description of the implementation, used in HealthIndicator     * @return the description     */    String description();    /**     * Get all ServiceInstances associated with a particular serviceId     * @param serviceId the serviceId to query     * @return a List of ServiceInstance     */    List<ServiceInstance> getInstances(String serviceId);    /**     * @return all known service ids     */    List<String> getServices();}

里面就提供了三个接口,我们接下来看看nacos是如何实现的?

public class NacosDiscoveryClient implements DiscoveryClient {    private static final Logger log = LoggerFactory.getLogger(NacosDiscoveryClient.class);    public static final String DESCRIPTION = "Spring Cloud Nacos Discovery Client";    private NacosDiscoveryProperties discoveryProperties;    public NacosDiscoveryClient(NacosDiscoveryProperties discoveryProperties) {        this.discoveryProperties = discoveryProperties;    }    @Override    public String description() {        return DESCRIPTION;    }    @Override    public List<ServiceInstance> getInstances(String serviceId) {        try {            List<Instance> instances = discoveryProperties.namingServiceInstance()                    .selectInstances(serviceId, true);            return hostToServiceInstanceList(instances, serviceId);        }        catch (Exception e) {            throw new RuntimeException(                    "Can not get hosts from nacos server. serviceId: " + serviceId, e);        }    }    private static ServiceInstance hostToServiceInstance(Instance instance,            String serviceId) {        NacosServiceInstance nacosServiceInstance = new NacosServiceInstance();        nacosServiceInstance.setHost(instance.getIp());        nacosServiceInstance.setPort(instance.getPort());        nacosServiceInstance.setServiceId(serviceId);        Map<String, String> 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;    }    private static List<ServiceInstance> hostToServiceInstanceList(            List<Instance> instances, String serviceId) {        List<ServiceInstance> result = new ArrayList<>(instances.size());        for (Instance instance : instances) {            result.add(hostToServiceInstance(instance, serviceId));        }        return result;    }    @Override    public List<String> getServices() {        try {            ListView<String> services = discoveryProperties.namingServiceInstance()                    .getServicesOfServer(1, Integer.MAX_VALUE);            return services.getData();        }        catch (Exception e) {            log.error("get service name from nacos server fail,", e);            return Collections.emptyList();        }    }}

这里面的逻辑非常简单,就不过多赘述了,有兴趣的小伙伴,可以自行去研究喔。

整个服务注册与发现差不多就是这样子,因为涉及的内容非常多,在很多地方小弟我确实看不懂,在上述中肯定有非常多的错误还请大神们多多指教。

文章转载于:https://www.cnblogs.com/lm970585581/p/13066729.html

原著是一个有趣的人,若有侵权,请通知删除

未经允许不得转载:起风网 » @EnableDiscoveryClient 注解如何实现服务注册与发现

分享到: 生成海报
avatar

评论 抢沙发

评论前必须登录!

立即登录   注册

切换注册

登录

忘记密码 ?

切换登录

注册

我们将发送一封验证邮件至你的邮箱, 请正确填写以完成账号注册和激活