Spring Cloud学习教程之DiscoveryClient的深入探究

网友投稿 520 2023-02-07


Spring Cloud学习教程之DiscoveryClient的深入探究

前言

当我们使用@DiscoveryClient注解的时候,会不会有如下疑问:它为什么会进行注册服务的操作,它不是应该用作服务发现的吗?下面我们就来深入的探究一下其源码。

一、Springframework的LifeCycle接口

要搞明白这个问题我们需要了解一下这个重要的接口:

/*

* Copyright 2002-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://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.context;

/**

* A common interface defining methods for start/stop lifecycle control.

* The typical use case for this is to control asynchronous processing.

* NOTE: This interface does not imply specific auto-startup semantics.

* ConsdCuTNider implementing {@link SmartLifecycle} for that purpose.

*

*

Can be implemented by both components (typically a Spring bean defined in a

* Spring context) and containers (typically a Spring {@link ApplicationContext}

* itself). Containers will propagate start/stop signals to all components that

* apply within each container, e.g. for a stop/restart scenario at runtime.

*

*

Can be used for direct invocations or for management operations via JMX.

* In the latter case, the {@link org.springframework.jmx.export.MBeanExporter}

* will typically be defined with an

* {@link org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler},

* restricting the visibility of activity-controlled components to the Lifecycle

* interface.

*

*

Note that the Lifehttp://cycle interface is only supported on top-level singleton

* beans. On any other component, the Lifecycle interface will remain undetected

* and hence ignored. Also, note that the extended {@link SmartLifecycle} interface

* provides integration with the application context's startup and shutdown phases.

*

* @author Juergen Hoeller

* @since 2.0

* @see SmartLifecycle

* @see ConfigurableApplicationContext

* @see org.springframework.jms.listener.AbstractMessageListenerContainer

* @see org.springframework.scheduling.quartz.SchedulerFactoryBean

*/

public interface Lifecycle {

/**

* Start this component.

*

Should not throw an exception if the component is already running.

*

In the case of a container, this will propagate the start signal to all

* components that apply.

* @see SmartLifecycle#isAutoStartup()

*/

void start();

/**

* Stop this component, typically in a synchronous fashion, such that the component is

* fully stopped upon return of this method. Consider implementing {@link SmartLifecycle}

* and its {@code stop(Runnable)} variant when asynchronous stop behavior is necessary.

*

Note that this stop notification is not guaranteed to come before destruction: On

* regular shutdown, {@code Lifecycle} beans will first receive a stop notification before

* the general destruction callbacks are being propagated; however, on hot refresh during a

* context's lifetime or on aborted refresh attempts, only destroy methods will be called.

*

Should not throw an exception if the component isn't started yet.

*

In the case of a container, this will propagate the stop signal to all components

* that apply.

* @see SmartLifecycle#stop(Runnable)

* @see org.springframework.beans.factory.DisposableBean#destroy()

*/

void stop();

/**

* Check whether this component is currently running.

*

In the case of a container, this will return {@code true} only if all

* components that apply are currently running.

* @return whether the component is currently running

*/

boolean isRunning();

}

该接口定义启动/停止生命周期控制方法,当spring ioc容器启动或停止时将发送一个启动或者停止的信号通知到各个组件,因此我们可以在对应的方法里做我们想要的事情。我们可以通过类图发现我们常用的ClasspathXmlApplicationContext类就实现了该接口

下面我们来简单演示一下案例,创建类MyLifeCycle:

package org.hzgj.spring.study.context;

import org.springframework.context.SmartLifecycle;

public class MyLifeCycle implements SmartLifecycle {

@Override

public void start() {

System.out.println("MyLifeCycle start ....");

}

@Override

public void stop() {

System.out.println("MyLifeCycle stop .....");

}

@Override

public boolean isRunning() {

return false;

}

@Override

public boolean isAutoStartup() {

return true;

}

@Override

public void stop(Runnable callback) {

}

@Override

public int getPhase() {

System.out.println("phase");

return 10;

}

}

在这里我们继承SmartLifeCycle该接口继承了LifeCycle, isRunning方法用于检测当前的组件是否处在运行状态,注意只有当isRunning返回值为false才可以运行

我们把MyLifeCycle配置到spring配置文件里,通过ClassPathXmlApplicationContext运行 会得到如下结果:

另外在这里的getPhase方法,这个是定义阶段值(可以理解为优先级,值越小对应的LifeCycle越先执行)

二、DiscoveryClient源码探究

@EnableDiscoveyClient

/*

* Copyright 2013-2015 the original author or authors.

*

* LidCuTNcensed 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://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;

}

请注意 @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://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 {

@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 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 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;

}

}

这个类重写的方法来自于接口 ImportSelector,我们可以根据 if(autoRegister)下的代码追踪到类:org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration ,我们来看一下结构图:

我们可以得知这个类实现了Lifecycle接口,那么我们看一看start方法,此方法在它的父类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://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 {

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();

}

}

}

注意在start方法里有一段这个代码:

if (!this.running.get() && getConfiguredPort() > 0) {

register();

if (shouldRegisterManagement()) {

registerManagement();

}

this.context.publishEvent(new InstanceRegisteredEvent<>(this,

getConfiguration()));

this.running.compareAndSet(false, true);

}

请注意register() 这个方法是本类里的抽象方法。那么我们回过头看一下AbstractAutoServiceRegistration类里的代码,我这里只贴出关键部分:

//.....

protected AbstractAutoServiceRegistration(ServiceRegistry serviceRegistry, AutoServiceRegistrationProperties properties) {

this.serviceRegistry = serviceRegistry;

this.properties = properties;

}

//......

/**

* Register the local service with the {@link ServiceRegistry}

*/

@Override

protected void register() {

this.serviceRegistry.register(getRegistration());

}

我们可以发现在构造函数里传了一个ServiceRegistry类型,这个接口是SpringCloud给我们提供用于服务注册的接口。在这里EurekaServiceRegistry就是实现了此接口:

/*

* Copyright 2013-2016 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://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.eureka.serviceregistry;

import java.util.HashMap;

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import org.springframework.cloud.client.serviceregistry.ServiceRegistry;

import com.netflix.appinfo.InstanceInfo;

/**

* @author Spencer Gibb

*/

public class EurekaServiceRegistry implements ServiceRegistry {

private static final Log log = LogFactory.getLog(EurekaServiceRegistry.class);

@Override

public void register(EurekaRegistration reg) {

maybeInitializeClient(reg);

if (log.isInfoEnabled()) {

log.info("Registering application " + reg.getInstanceConfig().getAppname()

+ " with eureka with status "

+ reg.getInstanceConfig().getInitialStatus());

}

reg.getApplicationInfoManager()

.setInstanceStatus(reg.getInstanceConfig().getInitialStatus());

if (reg.getHealthCheckHandler() != null) {

reg.getEurekaClient().registerHealthCheck(reg.getHealthCheckHandler());

}

}

private void maybeInitializeClient(EurekaRegistration reg) {

// force initialization of possibly scoped proxies

reg.getApplicationInfoManager().getInfo();

reg.getEurekaClient().getApplications();

}

@Override

public void deregister(EurekaRegistration reg) {

if (reg.getApplicationInfoManager().getInfo() != null) {

if (log.isInfoEnabled()) {

log.info("Unregistering application " + reg.getInstanceConfig().getAppname()

+ " with eureka with status DOWN");

}

reg.getApplicationInfoManager().setInstanceStatus(InstanceInfo.InstanceStatus.DOWN);

//shutdown of eureka client should happen with EurekaRegistration.close()

//auto registration will create a bean which will be properly disposed

//manual registrations will need to call close()

}

}

@Override

public void setStatus(EurekaRegistration registration, String status) {

InstanceInfo info = registration.getApplicationInfoManager().getInfo();

//TODO: howto deal with delete properly?

if ("CANCEL_OVERRIDE".equalsIgnoreCase(status)) {

registration.getEurekaClient().cancelOverrideStatus(info);

return;

}

//TODO: howto deal with status types across discovery systems?

InstanceInfo.InstanceStatus newStatus = InstanceInfo.InstanceStatus.toEnum(status);

registration.getEurekaClient().setStatus(newStatus, info);

}

@Override

public Object getStatus(EurekaRegistration registration) {

HashMap status = new HashMap<>();

InstanceInfo info = registration.getApplicationInfoManager().getInfo();

status.put("status", info.getStatus().toString());

status.put("overriddenStatus", info.getOverriddenStatus().toString());

return status;

}

public void close() {

}

}

那么至此我们可以总结如下几点:

1、使用@DiscoveryClient注册服务是利用了LifeCycle机制,在容器启动时会执行ServiceRegistry的register()方法。

2、使用@DiscoveryClient要比@EnableEurekaClient与@EnableEurekaServer更灵活,因为它屏蔽了对服务注册的实现,我们甚至可以自定义注册中心。

3、这里面还会自动去寻找DiscoveryClient接口的实现用作服务发现

三、Discoveryclient实战之redis注册中心

下面我们实现一个基于redis为注册中心的需求,来理解一下Discoveryclient。顺便理解一下Springcloud重要的接口:ServiceRegistry,ServiceInstance,再此之前我们先添加对redis的支持:

compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-redis'

1、实现Registration接口

package com.hzgj.lyrk.member;

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.NetworkInterface;

import java.net.URI;

import java.util.Enumeration;

import java.util.Map;

@Component

public class RedisRegistration implements Registration {

@Value("${server.port}")

private Integer port;

@Value("${spring.application.name}")

private String applicationName;

private String host;

public void setHost(String host) {

this.host = host;

}

public void setPort(Integer port) {

this.port = port;

}

public void setApplicationName(String applicationName) {

this.applicationName = applicationName;

}

@Override

public String getServiceId() {

return applicationName + ":" + getHost() + ":" + getPort();

}

@Override

public String getHost() {

try {

if (host == null)

return getLocalHostLANAddress().getHostAddress();

else

return host;

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

@Override

public int getPort() {

return port;

}

@Override

public boolean isSecure() {

return false;

}

@Override

public URI getUri() {

return null;

}

@Override

public Map getMetadata() {

return null;

}

public String getServiceName() {

return this.applicationName;

}

public InetAddress getLocalHostLANAddress() throws Exception {

try {

InetAddress candidateAddress = null;

// 遍历所有的网络接口

for (Enumeration ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements(); ) {

NetworkInterface iface = (NetworkInterface) ifaces.nextElement();

// 在所有的接口下再遍历IP

for (Enumeration inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements(); ) {

InetAddress inetAddr = (InetAddress) inetAddrs.nextElement();

if (!inetAddr.isLoopbackAddress()) {// 排除loopback类型地址

if (inetAddr.isSiteLocalAddress()) {

// 如果是site-local地址,就是它了

return inetAddr;

} else if (candidateAddress == null) {

// site-local类型的地址未被发现,先记录候选地址

candidateAddress = inetAddr;

}

}

}

}

if (candidateAddress != null) {

return candidateAddress;

}

// 如果没有发现 non-loopback地址.只能用最次选的方案

InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();

return jdkSuppliedAddress;

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

}

该接口继承了ServiceIntance,那么此接口最主要作用就是定义了一个服务实例的规范,比如说它的serviceId是什么,端口号是什么等

2、实现ServiceRegistry的接口

package com.hzgj.lyrk.member;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.cloud.client.serviceregistry.ServiceRegistry;

import org.springframework.data.redis.core.StringRedisTemplate;

public class RedisServiceRegistry implements ServiceRegistry {

@Autowired

private StringRedisTemplate redisTemplate;

@Override

public void register(RedisRegistration registration) {

String serviceId = registration.getServiceId();

redisTemplate.opsForList().leftPush(serviceId, registration.getHost() + ":" + registration.getPort());

}

@Override

public void deregister(RedisRegistration registration) {

redisTemplate.opsForList().remove(registration.getServiceId(), 1, registration.getHost() + ":" + registration.getPort());

}

@Override

public void close() {

//redisTemplate.d

System.out.println("closed ...");

}

@Override

public void setStatus(RedisRegistration registration, String status) {

}

@Override

public T getStatus(RedisRegistration registration) {

return null;

}

}

该接口主要作用是定义如何进行服务注册 ,服务注销,设置与获取服务状态等操作

3、继承 AbstractAutoServiceRegistration抽象类

package com.hzgj.lyrk.member;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration;

import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;

import org.springframework.cloud.client.serviceregistry.ServiceRegistry;

public class RedisAutoServiceRegistration extends AbstractAutoServiceRegistration {

@Autowired

private RedisRegistration redisRegistration;

protected RedisAutoServiceRegistration(ServiceRegistry serviceRegistry, AutoServiceRegistrationProperties properties) {

super(serviceRegistry, properties);

// serviceRegistry.register(getRegistration());

}

@Override

protected int getConfiguredPort() {

return redisRegistration.getPort();

}

@Override

protected void setConfiguredPort(int port) {

}

@Override

protected Object getConfiguration() {

return null;

}

@Override

protected boolean isEnabled() {

return true;

}

@Override

protected RedisRegistration getRegistration() {

return redisRegistration;

}

@Override

protected RedisRegistration getManagementRegistration() {

return null;

}

}

4、定义DiscoveryClient的实现类RedisDiscoveryClient

package com.hzgj.lyrk.member;

import org.apache.commons.lang.StringUtils;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.cloud.client.ServiceInstance;

import org.springframework.cloud.client.discovery.DiscoveryClient;

import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.ArrayList;

import java.util.List;

import java.util.function.Function;

import java.util.stream.Collectors;

public class RedisDiscoveryClient implements DiscoveryClient {

@Autowired

private StringRedisTemplate redisTemplate;

@Override

public String description() {

return "redis注册中心的服务发现";

}

@Override

public ServiceInstance getLocalServiceInstance() {

return null;

}

@Override

public List getInstances(String serviceId) {

return redisTemplate.opsForList().range(serviceId, 0, -1).

parallelStream().map((Function) s -> {

RedisRegistration redisRegistration = new RedisRegistration();

redisRegistration.setApplicationName(serviceId);

String hostName = StringUtils.split(s, ":")[0];

String port = StringUtils.split(s, ":")[1];

redisRegistration.setHost(hostName);

redisRegistration.setPort(Integer.parseInt(port));

//redisRegistration

return redisRegistration;

}).collect(Collectors.toList());

}

@Override

public List getServices() {

List list = new ArrayList<>();

list.addAll(redisTemplate.keys("*"));

return list;

}

}

该类主要是针对于redis注册中心的服务发现

5、定义自动装配的类用以创建对应的bean

package com.hzgj.lyrk.member;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;

import org.springframework.boot.context.properties.EnableConfigurationProperties;

import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.Primary;

@Configuration

@EnableConfigurationProperties(RedisConfig.class)

@ConditionalOnProperty(value = "spring.redis.registry.enabled", matchIfMissing = true)

public class RedisRegistryAutoConfiguration {

@Bean

RedisServiceRegistry redisServiceRegistry(RedisConfig redisConfig) {

System.out.println(redisConfig.getHost());

return new RedisServiceRegistry();

}

@Bean

RedisAutoServiceRegistration redisAutoServiceRegistration(RedisServiceRegistry redisServiceRegistry) {

return new RedisAutoServiceRegistration(redisServiceRegistry, new AutoServiceRegistrationProperties());

}

@Bean

@Primary

RedisDiscoveryClient redisDiscoveryClient() {

return new RedisDiscoveryClient();

}

}

6、定义启动类

package com.hzgj.lyrk.member;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.client.discovery.DiscoveryClient;

import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration;

import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration;

import org.springframework.context.ConfigurableApplicationContext;

@EnableDiscoveryClient

@SpringBootApplication(exclude = {SimpleDiscoveryClientAutoConfiguration.class, CompositeDiscoveryClientAutoConfiguration.class})

public class MemberApplication {

public static void main(String[] args) {

ConfigurableApplicationContext applicationContext = SpringApplication.run(MemberApplication.class, args);

DiscoveryClient discoveryClient = applicationContext.getBean(DiscoveryClient.class);

discoveryClient.getServices().forEach(action -> {

System.out.println(action);

});

}

}

这里在SpringbootApplication注解里排除DiscoveryClient的默认装配。

当我们启动成功后可以发现,控制台已经输出对应的服务名称与地址:

我们再次通过gradle打包生成jar文件并运行:

java -jar member-server-0.0.1-SNAPSHOT.jar --server.port=8800

我们可以看到redis里已经缓存的有服务注册的值了:

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Spring Cloud学习教程之Zuul统一异常处理与回退
下一篇:详解Vue2.0配置mint
相关文章

 发表评论

暂时没有评论,来抢沙发吧~