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

时间:2022-09-03 20:42:40

前言

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

一、springframework的lifecycle接口

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

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/*
 * 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类就实现了该接口

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

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

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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运行 会得到如下结果:

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

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

二、discoveryclient源码探究

@enablediscoveyclient

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/*
 * 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) 我们可以参考一下这个类:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/*
 * 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 ,我们来看一下结构图:

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

我们可以得知这个类实现了lifecycle接口,那么我们看一看start方法,此方法在它的父类abstractdiscoverylifecycle里:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
/*
 * 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方法里有一段这个代码:

 
1
2
3
4
5
6
7
8
9
if (!this.running.get() && getconfiguredport() > 0) {
 register();
 if (shouldregistermanagement()) {
 registermanagement();
 }
 this.context.publishevent(new instanceregisteredevent<>(this,
  getconfiguration()));
 this.running.compareandset(false, true);
 }

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

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//.....
 
 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就是实现了此接口:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/*
 * 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的支持:

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

1、实现registration接口

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
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的接口

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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抽象类

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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、定义启动类

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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的默认装配。

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

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

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

 
1
java -jar member-server-0.0.1-snapshot.jar --server.port=8800

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

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

总结

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

原文链接:https://www.cnblogs.com/niechen/p/8893759.html