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://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.context; /** * a common interface defining methods for start/stop lifecycle control. * the typical use case for this is to control asynchronous processing. * <b>note: this interface does not imply specific auto-startup semantics. * consider implementing {@link smartlifecycle} for that purpose.</b> * * <p>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. * * <p>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. * * <p>note that the lifecycle interface is only supported on <b>top-level singleton * beans</b>. 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. * <p>should not throw an exception if the component is already running. * <p>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. * <p>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. * <p>should not throw an exception if the component isn't started yet. * <p>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. * <p>in the case of a container, this will return {@code true} only if <i>all</i> * 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. * * 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; }
请注意 @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; } }
这个类重写的方法来自于接口 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://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(); } } }
注意在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<r> 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://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.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<eurekaregistration> { 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<string, object> 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<string, string> 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<redisregistration> { @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> 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<redisregistration> { @autowired private redisregistration redisregistration; protected redisautoserviceregistration(serviceregistry<redisregistration> 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<serviceinstance> getinstances(string serviceid) { return redistemplate.opsforlist().range(serviceid, 0, -1). parallelstream().map((function<string, serviceinstance>) 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<string> getservices() { list<string> 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里已经缓存的有服务注册的值了:
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。