• 从服务间的一次调用分析整个springcloud的调用过程(一)


    首先我们知道springcloud是一个微服务框架,按照官方文档的说法,springcloud提供了一些开箱即用的功能:

          1 分布式/版本化配置

          2 服务的注册与发现

          3 路由

          4 服务到服务之间调用

          5 负载均衡

          6 断路器

          7 分布式消息

    本文我们讲解的组件注册中心用的是阿里的nacos(关于nacos的安装可自行百度),网关用的就是springcloudgateway,负载均衡ribbon,断路器hystrix,客户端调用目标是openfeign

    一 先看服务的注册

     

          服务的注册核心的类就是spring-cloud-commons下的两个类

                          org.springframework.cloud.client.serviceregistry.ServiceRegistry

                          org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration

        ServiceRegistry是一个接口,接口中定义了如何向注册中心注册服务

    package org.springframework.cloud.client.serviceregistry;
    
    /**
     * Contract to register and deregister instances with a Service Registry.
     *
     * @author Spencer Gibb
     * @since 1.2.0
     */
    public interface ServiceRegistry<R extends Registration> {
    
        /**
         * Registers the registration. A registration typically has information about
         * an instance, such as its hostname and port.
         * @param registration The registration.
         */
        void register(R registration);
    
        /**
         * Deregisters the registration.
         * @param registration
         */
        void deregister(R registration);
    
        /**
         * Closes the ServiceRegistry. This is a lifecycle method.
         */
        void close();
    
        /**
         * Sets the status of the registration. The status values are determined
         * by the individual implementations.
         *
         * @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
         * @param registration The registration to update.
         * @param status The status to set.
         */
        void setStatus(R registration, String status);
    
        /**
         * Gets the status of a particular registration.
         *
         * @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
         * @param registration The registration to query.
         * @param <T> The type of the status.
         * @return The status of the registration.
         */
        <T> T getStatus(R registration);
    }

     AbstractAutoServiceRegistration则是springcloud向注册中心注册服务的启动逻辑

    package org.springframework.cloud.client.serviceregistry;
    
    import java.util.concurrent.atomic.AtomicBoolean;
    import java.util.concurrent.atomic.AtomicInteger;
    
    import javax.annotation.PreDestroy;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    import org.springframework.beans.BeansException;
    import org.springframework.boot.web.context.ConfigurableWebServerApplicationContext;
    import org.springframework.boot.web.context.WebServerInitializedEvent;
    import org.springframework.cloud.client.discovery.ManagementServerPortUtils;
    import org.springframework.cloud.client.discovery.event.InstancePreRegisteredEvent;
    import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.context.ApplicationListener;
    import org.springframework.core.env.Environment;
    
    /**
     * Lifecycle methods that may be useful and common to {@link ServiceRegistry}
     * implementations.
     *
     * TODO: Document the lifecycle.
     *
     * @param <R> Registration type passed to the {@link ServiceRegistry}.
     *
     * @author Spencer Gibb
     */
    public abstract class AbstractAutoServiceRegistration<R extends Registration>
            implements AutoServiceRegistration, ApplicationContextAware, ApplicationListener<WebServerInitializedEvent> {
        private static final Log logger = LogFactory
                .getLog(AbstractAutoServiceRegistration.class);
    
        private boolean autoStartup = true;
    
        private AtomicBoolean running = new AtomicBoolean(false);
    
        private int order = 0;
    
        private ApplicationContext context;
    
        private Environment environment;
    
        private AtomicInteger port = new AtomicInteger(0);
    
        private final ServiceRegistry<R> serviceRegistry;
        private AutoServiceRegistrationProperties properties;
    
        @Deprecated
        protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry) {
            this.serviceRegistry = serviceRegistry;
        }
    
        protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry, AutoServiceRegistrationProperties properties) {
            this.serviceRegistry = serviceRegistry;
            this.properties = properties;
        }
    
        protected ApplicationContext getContext() {
            return context;
        }
    
        @Override
        @SuppressWarnings("deprecation")
        public void onApplicationEvent(WebServerInitializedEvent event) {
            bind(event);
        }
    
        @Deprecated
        public void bind(WebServerInitializedEvent event) {
            ApplicationContext context = event.getApplicationContext();
            if (context instanceof ConfigurableWebServerApplicationContext) {
                if ("management".equals(
                        ((ConfigurableWebServerApplicationContext) context).getServerNamespace())) {
                    return;
                }
            }
            this.port.compareAndSet(0, event.getWebServer().getPort());
            this.start();
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext)
                throws BeansException {
            this.context = applicationContext;
            this.environment = this.context.getEnvironment();
        }
    
        @Deprecated
        protected Environment getEnvironment() {
            return environment;
        }
    
        @Deprecated
        protected AtomicInteger getPort() {
            return port;
        }
    
        public boolean isAutoStartup() {
            return this.autoStartup;
        }
    
        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()) {
                this.context.publishEvent(new InstancePreRegisteredEvent(this, getRegistration()));
                register();
                if (shouldRegisterManagement()) {
                    registerManagement();
                }
                this.context.publishEvent(
                        new InstanceRegisteredEvent<>(this, getConfiguration()));
                this.running.compareAndSet(false, true);
            }
    
        }
    
        /**
         * @return Whether the management service should be registered with the
         * {@link ServiceRegistry}.
         */
        protected boolean shouldRegisterManagement() {
            if (this.properties == null || this.properties.isRegisterManagement()) {
                return getManagementPort() != null
                        && ManagementServerPortUtils.isDifferent(this.context);
            }
            return false;
        }
    
        /**
         * @return The object used to configure the registration.
         */
        @Deprecated
        protected abstract Object getConfiguration();
    
        /**
         * @return True, if this is enabled.
         */
        protected abstract boolean isEnabled();
    
        /**
         * @return The serviceId of the Management Service.
         */
        @Deprecated
        protected String getManagementServiceId() {
            // TODO: configurable management suffix
            return this.context.getId() + ":management";
        }
    
        /**
         * @return The service name of the Management Service.
         */
        @Deprecated
        protected String getManagementServiceName() {
            // TODO: configurable management suffix
            return getAppName() + ":management";
        }
    
        /**
         * @return The management server port.
         */
        @Deprecated
        protected Integer getManagementPort() {
            return ManagementServerPortUtils.getPort(this.context);
        }
    
        /**
         * @return The app name (currently the spring.application.name property).
         */
        @Deprecated
        protected String getAppName() {
            return this.environment.getProperty("spring.application.name", "application");
        }
    
        @PreDestroy
        public void destroy() {
            stop();
        }
    
        public boolean isRunning() {
            return this.running.get();
        }
    
        protected AtomicBoolean getRunning() {
            return running;
        }
    
        public int getOrder() {
            return this.order;
        }
    
        public int getPhase() {
            return 0;
        }
    
        protected ServiceRegistry<R> getServiceRegistry() {
            return this.serviceRegistry;
        }
    
        protected abstract R getRegistration();
    
        protected abstract R getManagementRegistration();
    
        /**
         * Register the local service with the {@link ServiceRegistry}.
         */
        protected void register() {
            this.serviceRegistry.register(getRegistration());
        }
    
        /**
         * Register the local management service with the {@link ServiceRegistry}.
         */
        protected void registerManagement() {
            R registration = getManagementRegistration();
            if (registration != null) {
                this.serviceRegistry.register(registration);
            }
        }
    
        /**
         * De-register the local service with the {@link ServiceRegistry}.
         */
        protected void deregister() {
            this.serviceRegistry.deregister(getRegistration());
        }
    
        /**
         * De-register the local management service with the {@link ServiceRegistry}.
         */
        protected void deregisterManagement() {
            R registration = getManagementRegistration();
            if (registration != null) {
                this.serviceRegistry.deregister(registration);
            }
        }
    
        public void stop() {
            if (this.getRunning().compareAndSet(true, false) && isEnabled()) {
                deregister();
                if (shouldRegisterManagement()) {
                    deregisterManagement();
                }
                this.serviceRegistry.close();
            }
        }
    }
    View Code

    从代码可以看出该类实现了ApplicationListener来监听WebServerInitializedEvent,最终调用register方法来注册服务,register方法就是通过ServiceRegistry接口的实现,比如阿里nacos的实现类com.alibaba.cloud.nacos.registry.NacosServiceRegistry

    /*
     * Copyright (C) 2018 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.alibaba.cloud.nacos.registry;
    
    import java.util.List;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.cloud.client.serviceregistry.Registration;
    import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
    import org.springframework.util.StringUtils;
    
    import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
    import com.alibaba.nacos.api.naming.NamingService;
    import com.alibaba.nacos.api.naming.pojo.Instance;
    
    /**
     * @author xiaojing
     * @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
     */
    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 = getNacosInstanceFromRegistration(registration);
    
            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) {
    
            if (!status.equalsIgnoreCase("UP") && !status.equalsIgnoreCase("DOWN")) {
                log.warn("can't support status {},please choose UP or DOWN", status);
                return;
            }
    
            String serviceId = registration.getServiceId();
    
            Instance instance = getNacosInstanceFromRegistration(registration);
    
            if (status.equalsIgnoreCase("DOWN")) {
                instance.setEnabled(false);
            }
            else {
                instance.setEnabled(true);
            }
    
            try {
                nacosDiscoveryProperties.namingMaintainServiceInstance()
                        .updateInstance(serviceId, instance);
            }
            catch (Exception e) {
                throw new RuntimeException("update nacos instance status fail", e);
            }
    
        }
    
        @Override
        public Object getStatus(Registration registration) {
    
            String serviceName = registration.getServiceId();
            try {
                List<Instance> instances = nacosDiscoveryProperties.namingServiceInstance()
                        .getAllInstances(serviceName);
                for (Instance instance : instances) {
                    if (instance.getIp().equalsIgnoreCase(nacosDiscoveryProperties.getIp())
                            && instance.getPort() == nacosDiscoveryProperties.getPort())
                        return instance.isEnabled() ? "UP" : "DOWN";
                }
            }
            catch (Exception e) {
                log.error("get all instance of {} error,", serviceName, e);
            }
            return null;
        }
    
        private Instance getNacosInstanceFromRegistration(Registration registration) {
            Instance instance = new Instance();
            instance.setIp(registration.getHost());
            instance.setPort(registration.getPort());
            instance.setWeight(nacosDiscoveryProperties.getWeight());
            instance.setClusterName(nacosDiscoveryProperties.getClusterName());
            instance.setMetadata(registration.getMetadata());
            return instance;
        }
    
    }
    View Code

    我们可以看到NacosServiceRegistry.register方法最终会调用NamingService.registerInstance

     @Override
        public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
    
            if (instance.isEphemeral()) {
                BeatInfo beatInfo = new BeatInfo();
                beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName, groupName));
                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();
                beatInfo.setPeriod(instanceInterval == 0 ? DEFAULT_HEART_BEAT_INTERVAL : instanceInterval);
    
                beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo);
            }
    
            serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance);
        }
    View Code

    该方法判断isEphemeral,是true的话会向beatReactor新增一个当前service的心跳任务,作用就是通过定时任务向注册中心发一个心跳请求,表示服务仍然存活,这个定时任务的间隔时间默认为5秒,默认值定义在com.alibaba.nacos.api.common.Constants里面,我们可以自定义该配置,spring.cloud.nacos.discovery.heart-beat-interval=1000,这个时间如果太长可能服务已经出问题了,但是注册中心无法感知,导致请求还会打到该节点,太短的话可能会对注册中心造成压力

    最终向注册中心注册服务就是发起一个简单的http请求,具体细节大家可以看源代码

    二 再看springcloud的feign配置加载

         feign的启用通过注解EnableFeignClients,该注解会通过import FeignClientsRegistrar来加载feign的配置,其中FeignClientsRegistrar是对接口ImportBeanDefinitionRegistrar的一个实现,该接口相当于自定义对某一包下bean的自定义装配,比如这里的FeignClientsRegistrar就是对注解中basePackages下的feign clientbean的自定义装配。

          其它关于对feign的配置加载,可参考我的另一篇文章 https://www.cnblogs.com/minjay/p/15746223.html 

    三 ribbon的配置加载 

          ribbon是负载均衡工具,在ribbon之前spring首先会先配置LoadBalancerAutoConfiguration,如下代码

    /*
     * Copyright 2013-2017 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.cloud.netflix.ribbon;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import java.util.ArrayList;
    import java.util.List;
    
    import com.netflix.client.IClient;
    import com.netflix.client.http.HttpRequest;
    import com.netflix.ribbon.Ribbon;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.autoconfigure.AutoConfigureAfter;
    import org.springframework.boot.autoconfigure.AutoConfigureBefore;
    import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
    import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.cloud.client.actuator.HasFeatures;
    import org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration;
    import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
    import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
    import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
    import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Conditional;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.client.AsyncRestTemplate;
    import org.springframework.web.client.RestTemplate;
    
    /**
     * Auto configuration for Ribbon (client side load balancing).
     *
     * @author Spencer Gibb
     * @author Dave Syer
     * @author Biju Kunjummen
     */
    @Configuration
    @Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
    @RibbonClients
    @AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
    @AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
    @EnableConfigurationProperties({RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class})
    public class RibbonAutoConfiguration {
    
        @Autowired(required = false)
        private List<RibbonClientSpecification> configurations = new ArrayList<>();
    
        @Autowired
        private RibbonEagerLoadProperties ribbonEagerLoadProperties;
    
        @Bean
        public HasFeatures ribbonFeature() {
            return HasFeatures.namedFeature("Ribbon", Ribbon.class);
        }
    
        @Bean
        public SpringClientFactory springClientFactory() {
            SpringClientFactory factory = new SpringClientFactory();
            factory.setConfigurations(this.configurations);
            return factory;
        }
    
        @Bean
        @ConditionalOnMissingBean(LoadBalancerClient.class)
        public LoadBalancerClient loadBalancerClient() {
            return new RibbonLoadBalancerClient(springClientFactory());
        }
    
        @Bean
        @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
        @ConditionalOnMissingBean
        public LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(final SpringClientFactory clientFactory) {
            return new RibbonLoadBalancedRetryFactory(clientFactory);
        }
    
        @Bean
        @ConditionalOnMissingBean
        public PropertiesFactory propertiesFactory() {
            return new PropertiesFactory();
        }
    
        @Bean
        @ConditionalOnProperty(value = "ribbon.eager-load.enabled")
        public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {
            return new RibbonApplicationContextInitializer(springClientFactory(),
                    ribbonEagerLoadProperties.getClients());
        }
    
        @Configuration
        @ConditionalOnClass(HttpRequest.class)
        @ConditionalOnRibbonRestClient
        protected static class RibbonClientHttpRequestFactoryConfiguration {
    
            @Autowired
            private SpringClientFactory springClientFactory;
    
            @Bean
            public RestTemplateCustomizer restTemplateCustomizer(
                    final RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory) {
                return restTemplate -> restTemplate.setRequestFactory(ribbonClientHttpRequestFactory);
            }
    
            @Bean
            public RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory() {
                return new RibbonClientHttpRequestFactory(this.springClientFactory);
            }
        }
    
        //TODO: support for autoconfiguring restemplate to use apache http client or okhttp
    
        @Target({ ElementType.TYPE, ElementType.METHOD })
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        @Conditional(OnRibbonRestClientCondition.class)
        @interface ConditionalOnRibbonRestClient { }
    
        private static class OnRibbonRestClientCondition extends AnyNestedCondition {
            public OnRibbonRestClientCondition() {
                super(ConfigurationPhase.REGISTER_BEAN);
            }
    
            @Deprecated //remove in Edgware"
            @ConditionalOnProperty("ribbon.http.client.enabled")
            static class ZuulProperty {}
    
            @ConditionalOnProperty("ribbon.restclient.enabled")
            static class RibbonProperty {}
        }
    
        /**
         * {@link AllNestedConditions} that checks that either multiple classes are present
         */
        static class RibbonClassesConditions extends AllNestedConditions {
    
            RibbonClassesConditions() {
                super(ConfigurationPhase.PARSE_CONFIGURATION);
            }
    
            @ConditionalOnClass(IClient.class)
            static class IClientPresent {
    
            }
    
            @ConditionalOnClass(RestTemplate.class)
            static class RestTemplatePresent {
    
            }
    
            @ConditionalOnClass(AsyncRestTemplate.class)
            static class AsyncRestTemplatePresent {
    
            }
    
            @ConditionalOnClass(Ribbon.class)
            static class RibbonPresent {
    
            }
    
        }
    }
    View Code

       LoadBalancerAutoConfiguration会自动帮我们注入支持负载均衡的RestTemplate以及配置重试retry相关。

      加载完LoadBalancerAutoConfiguration后会自动配置RibbonAutoConfiguration,其中很核心的类就是装载了bean LoadBalancerClient为RibbonLoadBalancerClient,LoadBalancerClient接口是spring自己定义的一个接口,但是我发现这个接口的实现在整个调用过程中都用不到,还望哪位大佬能指导下这个实现机制在什么时候会用的到

       还有一个核心配置就是SpringClientFactory,这个类的作用就是在请求时根据你的serviceId去加载相关配置,比如连接超时和读取超时

       

         

  • 相关阅读:
    C#调用默认浏览器打开网页的几种方法
    关于百度排名点击器作弊提升百度排名的说明
    网站被百度降权的各种原因大解析
    百度搜索引擎排名原理、因素
    【ASP.NET开发】获取客户端IP地址 via C#
    防抖和节流
    vuex
    watch 和 computed
    JavaScript捕获与冒泡与委托
    XSS攻击
  • 原文地址:https://www.cnblogs.com/minjay/p/15878162.html
Copyright © 2020-2023  润新知