前言
當我們使用@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類就實現了該接口
下面我們來簡單演示一下案例,創建類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運行 會得到如下結果:
另外在這里的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
,我們來看一下結構圖:
我們可以得知這個類實現了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的默認裝配。
當我們啟動成功后可以發現,控制臺已經輸出對應的服務名稱與地址:
我們再次通過gradle打包生成jar文件并運行:
1
|
java -jar member-server- 0.0 . 1 -snapshot.jar --server.port= 8800 |
我們可以看到redis里已經緩存的有服務注冊的值了:
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。
原文鏈接:https://www.cnblogs.com/niechen/p/8893759.html