• springcloud源码分析(一)之采用redis实现注册中心


    注册中心

    在分布式架构中注册中心起到了管理各种服务功能包括服务的注册、发现、熔断、负载、降级等功能,在分布式架构中起到了不可替代的作用。常见的注册中心有eureka,zookeeper等等,在springcloud中,它封装了Netflix公司开发的Eureka模块来实现服务的注册与发现,简单的来说注册中心里会存放着我们的ip、端口、业务,如果是只是存储我们可以想到很多,数据库,文件,内存,redis都是可以的存的。那么今天小编这里就把redis当成注册中心来实现。

    配置与分析

    在springcloud中我们创建一个业务模块去找注册中心时,一般会@EnableDiscoveryClient、@EnableEurekaClient、@EnableEurekaServer,不过@EnableEurekaClient和@EnableEurekaServer都是Eureka给我提供的。这里我们就要SpringCloud给我们提供的@EnableDiscoveryClient,点击进入@EnableDiscoveryClient源码中。

    /*
     * Copyright 2013-2015 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.client.discovery;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import org.springframework.context.annotation.Import;
    
    /**
     * Annotation to enable a DiscoveryClient implementation.
     * @author Spencer Gibb
     */
    @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;
    }
    View Code

    我们可以看到autoRegister()方法,我们可以看上面的注释,如果为true,将把本地服务注册到ServiceRegistry中。ServiceRegistry可以理解为将服务注册到注册中心的一个通道,那么我们想把服务注册到注册中心你个方法就必须返回true。

    可以看到注解上的注解@Import(EnableDiscoveryClientImportSelector.class)

    这个注解引入了这个类,那我们再进入这个类

    /*
     * Copyright 2013-2015 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.client.discovery;
    
    import org.springframework.boot.bind.RelaxedPropertyResolver;
    import org.springframework.cloud.commons.util.SpringFactoryImportSelector;
    import org.springframework.core.Ordered;
    import org.springframework.core.annotation.AnnotationAttributes;
    import org.springframework.core.annotation.Order;
    import org.springframework.core.env.ConfigurableEnvironment;
    import org.springframework.core.env.Environment;
    import org.springframework.core.env.MapPropertySource;
    import org.springframework.core.type.AnnotationMetadata;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.LinkedHashMap;
    import java.util.List;
    
    /**
     * @author Spencer Gibb
     */
    @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 new RelaxedPropertyResolver(getEnvironment()).getProperty(
                    "spring.cloud.discovery.enabled", Boolean.class, Boolean.TRUE);
        }
    
        @Override
        protected boolean hasDefaultFactory() {
            return true;
        }
    
    }
    View Code

    我们可以看到selectImports方法里的判断,autoRegister的判断,当我autoRegister()为true时才会执行List的添加,添加的内容我们可以发现Lis添加的内容,只不过是个配置类,不过我们顺藤摸瓜就可以找到serviceregistry的包,我们可以发现包下有许多包是做配置的,那么我们找到最核心的类AbstractAutoServiceRegistration类找到他的父类AbstractDiscoveryLifecycle,在这个类中我们找到抽象register()方法,看一下上面的注释我们就可以发现这个才是用来注册的方法,接下我们找到重写的start()方法可以看到register()方法在这里被调用。

    AbstractAutoServiceRegistration源码

    package org.springframework.cloud.client.serviceregistry;
    
    import org.springframework.cloud.client.discovery.AbstractDiscoveryLifecycle;
    
    /**
     * 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
     */
    @SuppressWarnings("deprecation")
    public abstract class AbstractAutoServiceRegistration<R extends Registration> extends AbstractDiscoveryLifecycle implements AutoServiceRegistration {
    
        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 ServiceRegistry<R> getServiceRegistry() {
            return this.serviceRegistry;
        }
    
        protected abstract R getRegistration();
    
        protected abstract R getManagementRegistration();
    
        /**
         * Register the local service with the {@link ServiceRegistry}
         */
        @Override
        protected void register() {
            this.serviceRegistry.register(getRegistration());
        }
    
        /**
         * Register the local management service with the {@link ServiceRegistry}
         */
        @Override
        protected void registerManagement() {
            R registration = getManagementRegistration();
            if (registration != null) {
                this.serviceRegistry.register(registration);
            }
        }
    
        /**
         * De-register the local service with the {@link ServiceRegistry}
         */
        @Override
        protected void deregister() {
            this.serviceRegistry.deregister(getRegistration());
        }
    
        /**
         * De-register the local management service with the {@link ServiceRegistry}
         */
        @Override
        protected void deregisterManagement() {
            R registration = getManagementRegistration();
            if (registration != null) {
                this.serviceRegistry.deregister(registration);
            }
        }
    
        @Override
        public void stop() {
            if (this.getRunning().compareAndSet(true, false) && isEnabled()) {
                deregister();
                if (shouldRegisterManagement()) {
                    deregisterManagement();
                }
                this.serviceRegistry.close();
            }
        }
    
        @Override
        protected boolean shouldRegisterManagement() {
            if (this.properties == null || this.properties.isRegisterManagement()) {
                return super.shouldRegisterManagement();
            }
            return false;
        }
    }
    View Code

    AbstractDiscoveryLifecycle源码

    /*
     * Copyright 2013-2015 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.client.discovery;
    
    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.context.embedded.EmbeddedServletContainerInitializedEvent;
    import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
    import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
    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 various DiscoveryClient implementations.
     *
     * @deprecated use {@link org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration} instead. This class will be removed in the next release train.
     *
     * @author Spencer Gibb
     */
    @Deprecated
    public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle,
            ApplicationContextAware, ApplicationListener<EmbeddedServletContainerInitializedEvent> {
    
        private static final Log logger = LogFactory.getLog(AbstractDiscoveryLifecycle.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);
    
        protected ApplicationContext getContext() {
            return context;
        }
    
        @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;
        }
    
        @Override
        public boolean isAutoStartup() {
            return this.autoStartup;
        }
    
        @Override
        public void stop(Runnable callback) {
            try {
                stop();
            } catch (Exception e) {
                logger.error("A problem occurred attempting to stop discovery lifecycle", e);
            }
            callback.run();
        }
    
        @Override
        public void start() {
            if (!isEnabled()) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Discovery Lifecycle disabled. Not starting");
                }
                return;
            }
    
            // only set the port if the nonSecurePort is 0 and this.port != 0
            if (this.port.get() != 0 && getConfiguredPort() == 0) {
                setConfiguredPort(this.port.get());
            }
            // only initialize if nonSecurePort is greater than 0 and it isn't already running
            // because of containerPortInitializer below
            if (!this.running.get() && getConfiguredPort() > 0) {
                register();
                if (shouldRegisterManagement()) {
                    registerManagement();
                }
                this.context.publishEvent(new InstanceRegisteredEvent<>(this,
                        getConfiguration()));
                this.running.compareAndSet(false, true);
            }
        }
    
        @Deprecated
        protected abstract int getConfiguredPort();
        @Deprecated
        protected abstract void setConfiguredPort(int port);
    
        /**
         * @return if the management service should be registered with the {@link ServiceRegistry}
         */
        protected boolean shouldRegisterManagement() {
            return getManagementPort() != null && ManagementServerPortUtils.isDifferent(this.context);
        }
    
        /**
         * @return the object used to configure the registration
         */
        @Deprecated
        protected abstract Object getConfiguration();
    
    
        /**
         * Register the local service with the DiscoveryClient
         */
        protected abstract void register();
    
        /**
         * Register the local management service with the DiscoveryClient
         */
        protected void registerManagement() {
        }
    
        /**
         * De-register the local service with the DiscoveryClient
         */
        protected abstract void deregister();
    
        /**
         * De-register the local management service with the DiscoveryClient
         */
        protected void deregisterManagement() {
        }
    
        /**
         * @return true, if the {@link DiscoveryLifecycle} 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");
        }
    
        @Override
        public void stop() {
            if (this.running.compareAndSet(true, false) && isEnabled()) {
                deregister();
                if (shouldRegisterManagement()) {
                    deregisterManagement();
                }
            }
        }
    
        @PreDestroy
        public void destroy() {
            stop();
        }
    
        @Override
        public boolean isRunning() {
            return this.running.get();
        }
    
        protected AtomicBoolean getRunning() {
            return running;
        }
    
        @Override
        public int getOrder() {
            return this.order;
        }
    
        @Override
        public int getPhase() {
            return 0;
        }
    
        @Override
        @Deprecated
        public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {
            // TODO: take SSL into account
            // Don't register the management port as THE port
            if (!"management".equals(event.getApplicationContext().getNamespace())) {
                this.port.compareAndSet(0, event.getEmbeddedServletContainer().getPort());
                this.start();
            }
        }
    }
    View Code

    回到AbstractAutoServiceRegistration,我们可以参考AbstractAutoServiceRegistration中重新的regiter()方法调用了serviceRegistry的register()方法,那么我们进入register()方法参考一下注释我们可以发现传入的参数类,主要传入我们的ip 端口 业务等等,那么方法的参数是它的一个方法,那么我们重写他AbstractAutoServiceRegistration类看一下这个方法我们需要返回一个什么类进去。

    package com.rk.ytl.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration;
    import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
    import org.springframework.cloud.client.serviceregistry.Registration;
    import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * @author 杨天乐
     * @date 2018/4/20 16:26
     */
    @Configuration
    public class RedisAutoServiceRegistration extends AbstractAutoServiceRegistration  {
    
        @Autowired
        private MyServiceInstance myServiceInstance;
    
        @Autowired
        private AutoServiceRegistrationProperties autoServiceRegistrationProperties;
    
        @Value("${server.port}")
        private Integer port;
    
        protected RedisAutoServiceRegistration(ServiceRegistry serviceRegistry) {
            super(serviceRegistry);
        }
    
        @Override
        protected Registration getRegistration() {
            return myServiceInstance;
        }
    
        @Override
        protected Registration getManagementRegistration() {
            return null;
        }
    
        @Override
        protected int getConfiguredPort() {
            return port;
        }
    
        @Override
        protected void setConfiguredPort(int port) {
    
        }
    
        @Override
        protected Object getConfiguration() {
            return null;
        }
    
        @Override
        protected boolean isEnabled() {
            return autoServiceRegistrationProperties.isEnabled();
        }
    }
    View Code

    这个是我写好的,我们重写完之后我们可以看到返回了一个Registration对象,那么可以根据源码找到Registration对象继承了ServiceInstance,我们看一下源码

    /*
     * Copyright 2013-2015 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.client;
    
    import java.net.URI;
    import java.util.Map;
    
    /**
     * Represents an instance of a Service in a Discovery System
     * @author Spencer Gibb
     */
    public interface ServiceInstance {
    
        /**
         * @return the service id as registered.
         */
        String getServiceId();
    
        /**
         * @return the hostname of the registered ServiceInstance
         */
        String getHost();
    
        /**
         * @return the port of the registered ServiceInstance
         */
        int getPort();
    
        /**
         * @return if the port of the registered ServiceInstance is https or not
         */
        boolean isSecure();
    
        /**
         * @return the service uri address
         */
        URI getUri();
    
        /**
         * @return the key value pair metadata associated with the service instance
         */
        Map<String, String> getMetadata();
    }
    View Code

    (注意:这里一定要把isEnable()方法设置为true,不然是不可以的,至于为什么,大家可以参考AbstractDiscoveryLifecycle的start()方法如果不为true是执行不了register()方法的)

    端口,ip,业务都在这里,那么我们就是找他了,再写一个类去实现它。

    package com.rk.ytl.config;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cloud.client.serviceregistry.Registration;
    import org.springframework.stereotype.Component;
    
    import java.net.InetAddress;
    import java.net.URI;
    import java.net.UnknownHostException;
    import java.util.Map;
    
    /**
     * @author 杨天乐
     * @date 2018/4/20 16:29
     */
    @Component
    public class MyServiceInstance implements Registration {
    
        @Value("${spring.application.name}")
        private String applicationName;
    
        @Value("${server.port}")
        private Integer port;
    
        @Override
        public String getServiceId() {
            return applicationName;
        }
    
        @Override
        public String getHost()  {
            String host = "";
            try {
                InetAddress inetAddress = InetAddress.getLocalHost();
                host =inetAddress.getHostAddress();
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }
            return host;
        }
    
        @Override
        public int getPort() {
            return port;
        }
    
        @Override
        public boolean isSecure() {
            return false;
        }
    
        @Override
        public URI getUri() {
            return null;
        }
    
        @Override
        public Map<String, String> getMetadata() {
            return null;
        }
    }
    View Code

    然后把他注入进来就可以了。参数完成了那么我们回头看是哪个类的register()方法是serviceRegistry类的,那么上面说到了serviceRegistry类是一个把服务中到服务中心的通道,那么我们就可以自己实现通过

    serviceRegistry通向redis的通道。

    package com.rk.ytl.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.client.serviceregistry.Registration;
    import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.data.redis.core.ValueOperations;
    import org.springframework.stereotype.Component;
    
    /**
     * @author 杨天乐
     * @date 2018/4/20 17:02
     */
    @Component
    public class MyServiceRegistry implements ServiceRegistry {
    
    
        @Autowired
        private StringRedisTemplate stringRedisTemplate;
    
        @Override
        public void register(Registration registration) {
            ValueOperations<String, String> redis = stringRedisTemplate.opsForValue();
            String hostname= registration.getHost()+":"+registration.getPort();
            String serviceId = registration.getServiceId();
            redis.set(serviceId,hostname);
        }
    
        @Override
        public void deregister(Registration registration) {
            stringRedisTemplate.delete(registration.getServiceId());
        }
    
        @Override
        public void close() {
    
        }
    
        @Override
        public void setStatus(Registration registration, String status) {
    
        }
    
        @Override
        public Object getStatus(Registration registration) {
            return null;
        }
    }
    View Code

    接下来配置一个启动类就可以了

    package com.rk.ytl;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    /**
     * @author 杨天乐
     * @date 2018/4/20 16:12
     */
    @SpringBootApplication
    @EnableDiscoveryClient
    public class RedisRegisterApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(RedisRegisterApplication.class,args);
        }
    }
    View Code

    yml配置:

    spring:
      redis:
        host: redis地址
      application:
        name: redis-register-center
    server:
      port: 9090
    View Code

    运行后我们在redis里就可以看见我们的本机的ip端口

  • 相关阅读:
    [JSOI2018]潜入行动
    [ZJOI2013]丽洁体
    [CTSC2017]吉夫特
    [SDOI2016]储能表
    POJ-3616 Milking Time
    POJ-2385 Apple Catching---DP
    POJ-2229 Sumsets---完全背包变形
    hdu-1114 Piggy-Bank---完全背包
    POJ-3050 Hopscotch---DFS
    POJ-3187 Backward Digit Sums---枚举全排列
  • 原文地址:https://www.cnblogs.com/yangtianle/p/8893907.html
Copyright © 2020-2023  润新知