前一阵子一直在忙着做项目,最近有空,把项目中用到的SpringBoot微服务相关架构再梳理一遍。

项目资源:https://github.com/sunroyi/SpringCloud

主要分为以下几步:

(1)搭建SpringBootService,这里是各个微服务的业务逻辑。(这里搭建了2个Service,用来测试熔断)

(2)搭建SpringBootEureka,用来发现服务。

(3)搭建SpringBootConfig,用于动态维护配置文件。

(4)搭建SpringBootConsumer,这个不是必须的,只有当一个更大的业务需要调用其他多个微服务Service时才需要搭建。

​ 可以通过Ribbon+RestTemplate,Feign两种方式进行微服务之间的调用。可以加入Hystrix进行熔断处理。

(5)搭建SpringBootZuul,用于路由控制。可以加入Hystrix用于熔断处理。

(6)使用Jenkins进行代码的自动化部署。

-—————————————————————————————————————–

在项目搭建之前,需要先了解Spring Cloud中Hystrix、Ribbon以及Feign它们三者之间在处理微服务调用超时从而触发熔断降级的关系。这里我借用一下别人的描述:https://www.jianshu.com/p/31dfb595170c

Ribbon:

它的作用是负载均衡,会帮你在每次请求时选择一台机器,均匀的把请求分发到各个机器上Ribbon的负载均衡默认使用的最经典的Round Robin轮询算法。这是啥?简单来说,就是如果订单服务对库存服务发起10次请求,那就先让你请求第1台机器、然后是第2台机器、第3台机器、第4台机器、第5台机器,接着再来—个循环,第1台机器、第2台机器。。。以此类推。

Feign:

Feign客户端是一个web声明式http远程调用工具,提供了接口和注解方式进行调用(用来调用其他服务)

Feign的一个关键机制就是使用了动态代理。

\1. 首先,如果你对某个接口定义了@FeignClient注解,Feign就会针对这个接口创建一个动态代理

\2. 接着你要是调用那个接口,本质就是会调用 Feign创建的动态代理,这是核心中的核心

\3. Feign的动态代理会根据你在接口上的@RequestMapping等注解,来动态构造出你要请求的服务的地址

\4. 最后针对这个地址,发起请求、解析响应

Hystrix:

在微服务架构里,一个系统会有很多的服务。以本文的业务场景为例:订单服务在一个业务流程里需要调用三个服务。现在假设订单服务自己最多只有100个线程可以处理请求,然后呢,积分服务不幸的挂了,每次订单服务调用积分服务的时候,都会卡住几秒钟,然后抛出—个超时异常。

咱们一起来分析一下,这样会导致什么问题?

\1. 如果系统处于高并发的场景下,大量请求涌过来的时候,订单服务的100个线程都会卡在请求积分服务这块。导致订单服务没有一个线程可以处理请求

\2. 然后就会导致别人请求订单服务的时候,发现订单服务也挂了,不响应任何请求了

上面这个,就是微服务架构中恐怖的服务雪崩问题

这么多服务互相调用,要是不做任何保护的话,某一个服务挂了,就会引起连锁反应,导致别的服务也挂。比如积分服务挂了,会导致订单服务的线程全部卡在请求积分服务这里,没有一个线程可以工作,瞬间导致订单服务也挂了,别人请求订单服务全部会卡住,无法响应。

Hystrix是隔离、熔断以及降级的一个框架。啥意思呢?说白了,Hystrix会搞很多个小小的线程池,比如订单服务请求库存服务是一个线程池,请求仓储服务是一个线程池,请求积分服务是一个线程池。每个线程池里的线程就仅仅用于请求那个服务。

打个比方:现在很不幸,积分服务挂了,会咋样?

当然会导致订单服务里的那个用来调用积分服务的线程都卡死不能工作了啊!但是由于订单服务调用库存服务、仓储服务的这两个线程池都是正常工作的,所以这两个服务不会受到任何影响。

这个时候如果别人请求订单服务,订单服务还是可以正常调用库存服务扣减库存,调用仓储服务通知发货。只不过调用积分服务的时候,每次都会报错。但是如果积分服务都挂了,每次调用都要去卡住几秒钟干啥呢?有意义吗?当然没有!所以我们直接对积分服务熔断不就得了,比如在5分钟内请求积分服务直接就返回了,不要去走网络请求卡住几秒钟,这个过程,就是所谓的熔断!那人家又说,兄弟,积分服务挂了你就熔断,好歹你干点儿什么啊!别啥都不干就直接返回啊?没问题,咱们就来个降级:每次调用积分服务,你就在数据库里记录一条消息,说给某某用户增加了多少积分,因为积分服务挂了,导致没增加成功!这样等积分服务恢复了,你可以根据这些记录手工加一下积分。这个过程,就是所谓的降级。

-—————————————————————————————————————–

我们知道在Spring Cloud微服务体系下,微服务之间的互相调用可以通过Feign进行声明式调用,在这个服务调用过程中Feign会通过Ribbon从服务注册中心获取目标微服务的服务器地址列表,之后在网络请求的过程中Ribbon就会将请求以负载均衡的方式打到微服务的不同实例上,从而实现Spring Cloud微服务架构中最为关键的功能即服务发现及客户端负载均衡调用。

另一方面微服务在互相调用的过程中,为了防止某个微服务的故障消耗掉整个系统所有微服务的连接资源,所以在实施微服务调用的过程中我们会要求在调用方实施针对被调用微服务的熔断逻辑。而要实现这个逻辑场景在Spring Cloud微服务框架下我们是通过Hystrix这个框架来实现的。

调用方会针对被调用微服务设置调用超时时间,一旦超时就会进入熔断逻辑,而这个故障指标信息也会返回给Hystrix组件,Hystrix组件会根据熔断情况判断被调微服务的故障情况从而打开熔断器,之后所有针对该微服务的请求就会直接进入熔断逻辑,直到被调微服务故障恢复,Hystrix断路器关闭为止。

-—————————————————————————————————————–

一. SpringBootService的搭建

1. 创建SpringBoot项目

1.1. New Project -> Maven -> Next

img

1.2. 输入GroupId,ArtifactId -> Next -> Finish

img

img

2. SpringBoot项目的相关配置

2.1. 在pom.xml中加入SpringBoot相关的Jar包

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
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
<?xml version="1.0" encoding="UTF-8"?>



<project xmlns="http://maven.apache.org/POM/4.0.0"



xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"



xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">



<modelVersion>4.0.0</modelVersion>







<groupId>com.sun</groupId>



<artifactId>springbootService</artifactId>



<version>1.0-SNAPSHOT</version>







<parent>



<groupId>org.springframework.boot</groupId>



<artifactId>spring-boot-starter-parent</artifactId>



<version>1.5.8.RELEASE</version>



<relativePath /> <!-- lookup parent from repository -->



</parent>







<properties>



<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>



<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>



<java.version>1.8</java.version>



<spring-cloud.version>Edgware.SR2</spring-cloud.version>



</properties>







<dependencies>



<dependency>



<groupId>org.springframework.boot</groupId>



<!-- 添加这个依赖之后就可以创建一个web应用程序。starter poms部分可以引入所有需要在实际项目中使用的依赖。



spring-boot-starter-web依赖包含所有的spring-core, spring-web, spring-webmvc,嵌入的Tomcat server和其他web应用相关的库。 -->



<artifactId>spring-boot-starter-web</artifactId>



</dependency>







<dependency>



<groupId>org.springframework.boot</groupId>



<artifactId>spring-boot-starter-test</artifactId>



<scope>test</scope>



</dependency>











<dependency>



<groupId>org.apache.commons</groupId>



<artifactId>commons-lang3</artifactId>



<version>3.4</version>



</dependency>







<dependency>



<groupId>com.alibaba</groupId>



<artifactId>fastjson</artifactId>



<version>1.2.7</version>



</dependency>







<dependency>



<groupId>com.fasterxml.jackson.module</groupId>



<artifactId>jackson-module-jaxb-annotations</artifactId>



</dependency>







<dependency>



<groupId>org.springframework.cloud</groupId>



<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>



<version>1.4.0.RELEASE</version>



</dependency>







<!--Spring Cloud Config 客户端依赖-->



<dependency>



<groupId>org.springframework.cloud</groupId>



<artifactId>spring-cloud-starter-config</artifactId>



<version>1.4.0.RELEASE</version>



</dependency>







<!--Spring Boot Actuator,感应服务端变化-->



<dependency>



<groupId>org.springframework.boot</groupId>



<artifactId>spring-boot-starter-actuator</artifactId>



</dependency>



</dependencies>







<build>



<plugins>



<plugin>



<groupId>org.springframework.boot</groupId>



<artifactId>spring-boot-maven-plugin</artifactId>



</plugin>



</plugins>



</build>







</project>

2.2. 在resources中添加application.properties,这个文件用来进行 项目的相关配置

1
2
3
4
5
6
7
8
9
spring.application.name=springbootService



server.port=6001




3. 项目结构和代码

3.1. 项目目录结构

img

其中,common目录下是一些共通代码,我这里直接拿过来用了。

sun下面的controller提供对外接口,entity定义实体类。

ComsumerClientApplication在sun目录下,是SpringBoot项目的启动文件。

3.2. 相关Java代码

(1)ComsumerClientApplication.java

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
package sun;







import org.springframework.boot.SpringApplication;



import org.springframework.boot.autoconfigure.SpringBootApplication;



import org.springframework.boot.web.servlet.ServletComponentScan;







@SpringBootApplication



@ServletComponentScan



@EnableDiscoveryClient



@RefreshScope //开启配置更新功能



public class ConsumerClientApplication {







public static void main(String[] args) {







SpringApplication.run(ConsumerClientApplication.class, args);



}



}

(2)ServiceController.java

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
/**



* Copyright &copy; 2012-2014 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved.



*/



package sun.controller;







import common.entity.RestfulResult;



import common.utils.CommUtils;



import org.springframework.web.bind.annotation.RequestBody;



import org.springframework.web.bind.annotation.RequestMapping;



import org.springframework.web.bind.annotation.RestController;



import sun.entity.ServiceInfo;







import javax.servlet.http.HttpServletRequest;



import javax.servlet.http.HttpServletResponse;







@RestController // 重要,如果用Controller会404



@RequestMapping(value = "service")



public class ServiceController {







@RequestMapping(value = "hello")



public void login(HttpServletRequest request, HttpServletResponse response,



@RequestBody ServiceInfo serviceInfo) {







RestfulResult restfulResult = new RestfulResult();







try {



restfulResult.setData("Service1:Welcome " + serviceInfo.getName() + "!");







} catch (Exception e) {



e.printStackTrace();



}







CommUtils.printDataJason(response, restfulResult);



}







@RequestMapping(value = "rest")



public String rest(@RequestBody ServiceInfo serviceInfo){







return "Service1:Welcome " + serviceInfo.getName() + " !";



}



}

(3)ServiceInfo.java

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
package sun.entity;







public class ServiceInfo {



private static final long serialVersionUID = 1L;







private String name;







public String getName() {



return name;



}







public void setName(String name) {



this.name = name;



}



}

(4)RestfulResult.java

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
package common.entity;











public class RestfulResult {







private String result = "Success";



private String message;



private Object data; // 返回数据



private int cntPage; // page数



private long cntData; // 返回数据总数







public String getResult() {



return result;



}



public void setResult(String result) {



this.result = result;



}



public String getMessage() {



return message;



}



public void setMessage(String message) {



this.message = message;



}



public Object getData() {



return data;



}



public void setData(Object data) {



this.data = data;



}



public int getCntPage() {



return cntPage;



}



public void setCntPage(int cntPage) {



this.cntPage = cntPage;



}



public long getCntData() {



return cntData;



}



public void setCntData(long cntData) {



this.cntData = cntData;



}



}

(5)CommonUtils.java

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
package common.utils;











import javax.servlet.http.HttpServletResponse;







public class CommUtils {







// JSON格式化



public static String printDataJason(HttpServletResponse response,



Object item) {



try {







JsonUtils.renderString(response, item);







} catch (Exception e) {



e.printStackTrace();



}







return null;



}







/**



* 随机生成6位随机验证码



*



*/



public static String createRandomVcode(int len) {



// 验证码



String vcode = "";



for (int i = 0; i < len; i++) {



vcode = vcode + (int) (Math.random() * 9);



}



return vcode;



}



}

(6)JsonUtils.java

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
package common.utils;







import javax.servlet.http.HttpServletResponse;



import java.io.IOException;







public class JsonUtils {



public JsonUtils() {



}







public static String renderString(HttpServletResponse response, Object object) {



return renderString(response, JsonMapper.toJsonString(object), "application/json");



}







public static String renderString(HttpServletResponse response, String string, String type) {



try {



response.setContentType(type);



response.setCharacterEncoding("utf-8");



response.getWriter().print(string);



return null;



} catch (IOException var4) {



return null;



}



}



}

(7)JsonMapper.java

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
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
package common.utils;







import com.fasterxml.jackson.annotation.JsonInclude.Include;



import com.fasterxml.jackson.core.JsonGenerator;



import com.fasterxml.jackson.core.JsonParser.Feature;



import com.fasterxml.jackson.core.JsonProcessingException;



import com.fasterxml.jackson.databind.*;



import com.fasterxml.jackson.databind.module.SimpleModule;



import com.fasterxml.jackson.databind.util.JSONPObject;



import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;



import org.apache.commons.lang3.StringEscapeUtils;



import org.apache.commons.lang3.StringUtils;



import org.slf4j.Logger;



import org.slf4j.LoggerFactory;







import java.io.IOException;



import java.util.TimeZone;







public class JsonMapper extends ObjectMapper {



private static final long serialVersionUID = 1L;



private static Logger logger = LoggerFactory.getLogger(JsonMapper.class);



private static JsonMapper mapper;







public JsonMapper() {



this(Include.NON_EMPTY);



}







public JsonMapper(Include include) {



if (include != null) {



this.setSerializationInclusion(include);



}







this.enableSimple();



this.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);



this.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {



public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {



jgen.writeString("");



}



});



this.registerModule((new SimpleModule()).addSerializer(String.class, new JsonSerializer<String>() {



public void serialize(String value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {



jgen.writeString(StringEscapeUtils.unescapeHtml4(value));



}



}));



this.setTimeZone(TimeZone.getDefault());



}







public static JsonMapper getInstance() {



if (mapper == null) {



mapper = (new JsonMapper()).enableSimple();



}







return mapper;



}







public static JsonMapper nonDefaultMapper() {



if (mapper == null) {



mapper = new JsonMapper(Include.NON_DEFAULT);



}







return mapper;



}







public String toJson(Object object) {



try {



return this.writeValueAsString(object);



} catch (IOException var3) {



logger.warn("write to json string error:" + object, var3);



return null;



}



}







public <T> T fromJson(String jsonString, Class<T> clazz) {



if (StringUtils.isEmpty(jsonString)) {



return null;



} else {



try {



return this.readValue(jsonString, clazz);



} catch (IOException var4) {



logger.warn("parse json string error:" + jsonString, var4);



return null;



}



}



}







public <T> T fromJson(String jsonString, JavaType javaType) {



if (StringUtils.isEmpty(jsonString)) {



return null;



} else {



try {



return this.readValue(jsonString, javaType);



} catch (IOException var4) {



logger.warn("parse json string error:" + jsonString, var4);



return null;



}



}



}







public JavaType createCollectionType(Class<?> collectionClass, Class... elementClasses) {



return this.getTypeFactory().constructParametricType(collectionClass, elementClasses);



}







public <T> T update(String jsonString, T object) {



try {



return this.readerForUpdating(object).readValue(jsonString);



} catch (JsonProcessingException var4) {



logger.warn("update json string:" + jsonString + " to object:" + object + " error.", var4);



} catch (IOException var5) {



logger.warn("update json string:" + jsonString + " to object:" + object + " error.", var5);



}







return null;



}







public String toJsonP(String functionName, Object object) {



return this.toJson(new JSONPObject(functionName, object));



}







public JsonMapper enableEnumUseToString() {



this.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);



this.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);



return this;



}







public JsonMapper enableJaxbAnnotation() {



JaxbAnnotationModule module = new JaxbAnnotationModule();



this.registerModule(module);



return this;



}







public JsonMapper enableSimple() {



this.configure(Feature.ALLOW_SINGLE_QUOTES, true);



this.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);



return this;



}







public ObjectMapper getMapper() {



return this;



}







public static String toJsonString(Object object) {



return getInstance().toJson(object);



}







public static Object fromJsonString(String jsonString, Class<?> clazz) {



return getInstance().fromJson(jsonString, clazz);



}



}

4. 项目启动和测试

4.1. 运行Debug,启动ConsumerClientApplication.java

img

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
"C:\Program Files\Java\jdk1.8.0_66\bin\java" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:54364,suspend=y,server=n -XX:TieredStopAtLevel=1 -noverify -Dspring.output.ansi.enabled=always -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=54362 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=localhost -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true -javaagent:D:\ideaIU-2018.1.win\lib\rt\debugger-agent.jar=file:/C:/Users/Administrator/AppData/Local/Temp/capture607016.props -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_66\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\rt.jar;D:\workspace20160509\springbootService\target\classes;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter-web\1.5.8.RELEASE\spring-boot-starter-web-1.5.8.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter\1.5.8.RELEASE\spring-boot-starter-1.5.8.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot\1.5.8.RELEASE\spring-boot-1.5.8.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\1.5.8.RELEASE\spring-boot-autoconfigure-1.5.8.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter-logging\1.5.8.RELEASE\spring-boot-starter-logging-1.5.8.RELEASE.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-classic\1.1.11\logback-classic-1.1.11.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-core\1.1.11\logback-core-1.1.11.jar;C:\Users\Administrator\.m2\repository\org\slf4j\jcl-over-slf4j\1.7.25\jcl-over-slf4j-1.7.25.jar;C:\Users\Administrator\.m2\repository\org\slf4j\jul-to-slf4j\1.7.25\jul-to-slf4j-1.7.25.jar;C:\Users\Administrator\.m2\repository\org\slf4j\log4j-over-slf4j\1.7.25\log4j-over-slf4j-1.7.25.jar;C:\Users\Administrator\.m2\repository\org\yaml\snakeyaml\1.17\snakeyaml-1.17.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter-tomcat\1.5.8.RELEASE\spring-boot-starter-tomcat-1.5.8.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\apache\tomcat\embed\tomcat-embed-core\8.5.23\tomcat-embed-core-8.5.23.jar;C:\Users\Administrator\.m2\repository\org\apache\tomcat\tomcat-annotations-api\8.5.23\tomcat-annotations-api-8.5.23.jar;C:\Users\Administrator\.m2\repository\org\apache\tomcat\embed\tomcat-embed-el\8.5.23\tomcat-embed-el-8.5.23.jar;C:\Users\Administrator\.m2\repository\org\apache\tomcat\embed\tomcat-embed-websocket\8.5.23\tomcat-embed-websocket-8.5.23.jar;C:\Users\Administrator\.m2\repository\org\hibernate\hibernate-validator\5.3.5.Final\hibernate-validator-5.3.5.Final.jar;C:\Users\Administrator\.m2\repository\javax\validation\validation-api\1.1.0.Final\validation-api-1.1.0.Final.jar;C:\Users\Administrator\.m2\repository\org\jboss\logging\jboss-logging\3.3.1.Final\jboss-logging-3.3.1.Final.jar;C:\Users\Administrator\.m2\repository\com\fasterxml\classmate\1.3.4\classmate-1.3.4.jar;C:\Users\Administrator\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.8.10\jackson-databind-2.8.10.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-web\4.3.12.RELEASE\spring-web-4.3.12.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-aop\4.3.12.RELEASE\spring-aop-4.3.12.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-beans\4.3.12.RELEASE\spring-beans-4.3.12.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-context\4.3.12.RELEASE\spring-context-4.3.12.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-webmvc\4.3.12.RELEASE\spring-webmvc-4.3.12.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-expression\4.3.12.RELEASE\spring-expression-4.3.12.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-core\4.3.12.RELEASE\spring-core-4.3.12.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\apache\commons\commons-lang3\3.4\commons-lang3-3.4.jar;C:\Users\Administrator\.m2\repository\com\alibaba\fastjson\1.2.7\fastjson-1.2.7.jar;C:\Users\Administrator\.m2\repository\com\fasterxml\jackson\module\jackson-module-jaxb-annotations\2.8.10\jackson-module-jaxb-annotations-2.8.10.jar;C:\Users\Administrator\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.8.10\jackson-core-2.8.10.jar;C:\Users\Administrator\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.8.0\jackson-annotations-2.8.0.jar;D:\ideaIU-2018.1.win\lib\idea_rt.jar" sun.ConsumerClientApplication



Connected to the target VM, address: '127.0.0.1:54364', transport: 'socket'







. ____ _ __ _ _



/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \



( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \



\\/ ___)| |_)| | | | | || (_| | ) ) ) )



' |____| .__|_| |_|_| |_\__, | / / / /



=========|_|==============|___/=/_/_/_/



:: Spring Boot :: (v1.5.8.RELEASE)







2019-07-09 14:17:10.710 INFO 923048 --- [ main] sun.ConsumerClientApplication : Starting ConsumerClientApplication on PC-Sun with PID 923048 (D:\workspace20160509\springbootService\target\classes started by Administrator in D:\workspace20160509\springbootPService)



2019-07-09 14:17:10.763 INFO 923048 --- [ main] sun.ConsumerClientApplication : No active profile set, falling back to default profiles: default



2019-07-09 14:17:11.754 INFO 923048 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@5db6b9cd: startup date [Tue Jul 09 14:17:11 CST 2019]; root of context hierarchy



2019-07-09 14:17:22.203 INFO 923048 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 6000 (http)



2019-07-09 14:17:22.285 INFO 923048 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]



2019-07-09 14:17:22.312 INFO 923048 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.23



2019-07-09 14:17:23.143 INFO 923048 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext



2019-07-09 14:17:23.145 INFO 923048 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 11433 ms



2019-07-09 14:17:24.072 INFO 923048 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]



2019-07-09 14:17:24.089 INFO 923048 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]



2019-07-09 14:17:24.091 INFO 923048 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]



2019-07-09 14:17:24.092 INFO 923048 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]



2019-07-09 14:17:24.092 INFO 923048 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]



2019-07-09 14:17:25.239 INFO 923048 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@5db6b9cd: startup date [Tue Jul 09 14:17:11 CST 2019]; root of context hierarchy



2019-07-09 14:17:25.376 INFO 923048 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/service/hello]}" onto public void sun.controller.ServiceController.login(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse,sun.entity.ServiceInfo)



2019-07-09 14:17:25.380 INFO 923048 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)



2019-07-09 14:17:25.381 INFO 923048 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)



2019-07-09 14:17:25.440 INFO 923048 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]



2019-07-09 14:17:25.440 INFO 923048 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]



2019-07-09 14:17:25.520 INFO 923048 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]



2019-07-09 14:17:25.896 INFO 923048 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup



2019-07-09 14:17:26.094 INFO 923048 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 6000 (http)



2019-07-09 14:17:26.100 INFO 923048 --- [ main] sun.ConsumerClientApplication : Started ConsumerClientApplication in 17.811 seconds (JVM running for 28.604)

4.2. 调用localhost:6001/service/hello来测试

img

5. 项目打包和执行

5.1. 打包

Maven Projects -> package

img

执行结果:

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
[INFO] --- maven-jar-plugin:2.6:jar (default-jar) @ springbootService ---



[INFO] Building jar: D:\workspace20160509\springbootService\target\springbootService-1.0-SNAPSHOT.jar



[INFO]



[INFO] --- spring-boot-maven-plugin:1.5.8.RELEASE:repackage (default) @ springbootService ---



[INFO] ------------------------------------------------------------------------



[INFO] BUILD SUCCESS



[INFO] ------------------------------------------------------------------------



[INFO] Total time: 26.847 s



[INFO] Finished at: 2019-07-09T14:36:54+08:00



[INFO] Final Memory: 29M/250M



[INFO] ------------------------------------------------------------------------







Process finished with exit code 0

找到jar包目录,创建start.bat文件

img

start.bat内容:

1
java -jar springbootService-1.0-SNAPSHOT.jar

5.2. 执行start.bat启动

img

img

6. 项目复制

为了在下面测试Hystrix熔断,这里复制一份springbootService2项目,区别有两个地方:

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
ServiceController:
/**



* Copyright &copy; 2012-2014 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved.



*/



package sun.controller;







import common.entity.RestfulResult;



import common.utils.CommUtils;



import org.springframework.web.bind.annotation.RequestBody;



import org.springframework.web.bind.annotation.RequestMapping;



import org.springframework.web.bind.annotation.RestController;



import sun.entity.ServiceInfo;







import javax.servlet.http.HttpServletRequest;



import javax.servlet.http.HttpServletResponse;







@RestController // 重要,如果用Controller会404



@RequestMapping(value = "service")



public class ServiceController {







@RequestMapping(value = "hello")



public void login(HttpServletRequest request, HttpServletResponse response,



@RequestBody ServiceInfo serviceInfo) {







RestfulResult restfulResult = new RestfulResult();







try {



restfulResult.setData("Service2:Welcome " + serviceInfo.getName() + "!");







} catch (Exception e) {



e.printStackTrace();



}







CommUtils.printDataJason(response, restfulResult);



}







@RequestMapping(value = "rest")



public String rest(@RequestBody ServiceInfo serviceInfo){







return "Service2:Welcome " + serviceInfo.getName() + " !";



}



}

application.properties:

1
2
3
4
5
6
7
8
9
10
11
12
13
spring.application.name=springbootService



server.port=6006







eureka.client.service-url.defaultZone=http://localhost:5000/eureka/

二. 搭建Eureka

1. 创建Maven项目springbootEureka

同springbootService

2. 项目结构和代码

img

pom.xml

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
<?xml version="1.0" encoding="UTF-8"?>



<project xmlns="http://maven.apache.org/POM/4.0.0"



xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"



xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">



<modelVersion>4.0.0</modelVersion>







<groupId>com.sun</groupId>



<artifactId>springbootEureka</artifactId>



<version>1.0-SNAPSHOT</version>







<parent>



<groupId>org.springframework.boot</groupId>



<artifactId>spring-boot-starter-parent</artifactId>



<version>1.5.12.RELEASE</version>



</parent>







<properties>



<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>



<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>



<java.version>1.8</java.version>



<spring-cloud.version>Edgware.RELEASE</spring-cloud.version>



</properties>







<dependencies>



<dependency>



<groupId>org.springframework.cloud</groupId>



<artifactId>spring-cloud-starter-eureka-server</artifactId>



<version>1.3.5.RELEASE</version>



</dependency>



</dependencies>







</project>

application.properties

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
server.port=5000



eureka.instance.hostname=localhost



#是否向服务注册中心注册自己,默认为true



eureka.client.register-with-eureka=false



#是否检索服务



eureka.client.fetch-registry=true



eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/

ConsumerClientApplication.java

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
package sun;







import org.springframework.boot.SpringApplication;



import org.springframework.boot.autoconfigure.SpringBootApplication;



import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;







@SpringBootApplication



@EnableEurekaServer



public class ConsumerClientApplication {







public static void main(String[] args) {







SpringApplication.run(ConsumerClientApplication.class, args);



}



}

3. 启动Eureka

3.1. 运行Debug,启动ConsumerClientApplication.java

3.2. 打开http://localhost:5000,表示正常启动

img

4. 修改springbootService,使Eureka可以发现此服务

pom.xml中增加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<dependency>



<groupId>org.springframework.cloud</groupId>



<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>



<version>1.4.0.RELEASE</version>



</dependency>

application.properties中增加

1
eureka.client.service-url.defaultZone=http://localhost:5000/eureka/

ConsumerClientApplication.java中增加

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
@EnableDiscoveryClient
package sun;







import org.springframework.boot.SpringApplication;



import org.springframework.boot.autoconfigure.SpringBootApplication;



import org.springframework.boot.web.servlet.ServletComponentScan;



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







@SpringBootApplication



@ServletComponentScan



@EnableDiscoveryClient



public class ConsumerClientApplication {







public static void main(String[] args) {







SpringApplication.run(ConsumerClientApplication.class, args);



}



}

启动后,在Eureka中可以看到springbootService已注册

img

三. 搭建SpringBootConfig配置中心

我这里用的是SVN,在config目录下创建文件springbootService-release.properties。

将springbootService的application.properties的内容复制进去。

img

为了与原文件区分看效果,我这里将server.port设为6003(原来是6001)

1
2
3
4
5
6
7
8
9
10
11
12
13
spring.application.name=springbootService



server.port=6003







eureka.client.service-url.defaultZone=http://localhost:5000/eureka/

配置中心其实就是读取SVN上的文件后,发送给其他服务,让他们读取。

1. 创建Maven项目springbootConfig

同springbootService

2. 项目结构和代码

img

pom.xml

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
246
247
248
249
250
251
252
253
254
255
256
257
<?xml version="1.0" encoding="UTF-8"?>



<project xmlns="http://maven.apache.org/POM/4.0.0"



xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"



xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">



<modelVersion>4.0.0</modelVersion>







<groupId>com.sun</groupId>



<artifactId>springbootConfig</artifactId>



<version>1.0-SNAPSHOT</version>



<packaging>jar</packaging>







<parent>



<groupId>org.springframework.boot</groupId>



<artifactId>spring-boot-starter-parent</artifactId>



<version>1.5.12.RELEASE</version>



</parent>







<properties>



<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>



<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>



<java.version>1.8</java.version>



<spring-cloud.version>Edgware.RELEASE</spring-cloud.version>



</properties>







<dependencies>



<dependency>



<groupId>org.springframework.cloud</groupId>



<artifactId>spring-cloud-config-server</artifactId>



</dependency>



<dependency>



<groupId>org.tmatesoft.svnkit</groupId>



<artifactId>svnkit</artifactId>



</dependency>



<dependency>



<groupId>org.springframework.cloud</groupId>



<artifactId>spring-cloud-starter-eureka</artifactId>



</dependency>



<dependency>



<groupId>org.springframework.boot</groupId>



<artifactId>spring-boot-starter-test</artifactId>



<scope>test</scope>



</dependency>



</dependencies>







<dependencyManagement>



<dependencies>



<dependency>



<groupId>org.springframework.cloud</groupId>



<artifactId>spring-cloud-dependencies</artifactId>



<version>${spring-cloud.version}</version>



<type>pom</type>



<scope>import</scope>



</dependency>



</dependencies>



</dependencyManagement>







<build>



<plugins>



<plugin>



<groupId>org.springframework.boot</groupId>



<artifactId>spring-boot-maven-plugin</artifactId>



</plugin>



</plugins>



</build>



</project>

application.yml

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
server:



port: 6002



spring:



application:



name: springbootConfig



profiles:



active: subversion



cloud:



config:



server:



svn:



uri: https://192.168.3.97/svn/SourceCode/SMPH/Beats/trunk/test/config



#username: *****



#password: *****



default-label:







eureka:



client:



service-url:



defaultZone: http://localhost:5000/eureka/



instance:



preferIpAddress: true



instance-id: ${spring.cloud.client.ipAddress}:${server.port}



lease-expiration-duration-in-seconds: 30



lease-renewal-interval-in-seconds: 30

ConsumerClientApplication

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
package sun;







import org.springframework.boot.SpringApplication;



import org.springframework.boot.autoconfigure.SpringBootApplication;



import org.springframework.cloud.config.server.EnableConfigServer;







@SpringBootApplication



@EnableConfigServer



public class ConsumerClientApplication {







public static void main(String[] args) {







SpringApplication.run(ConsumerClientApplication.class, args);



}



}

3. 启动Config配置中心

3.1. Eureka中能够看到Config配置中心已注册

img

3.2. 打开http://localhost:6002/springbootService/release,能够看到配置文件的内容

1
{"name":"springbootService","profiles":["release"],"label":null,"version":"523","state":null,"propertySources":[{"name":"https://192.168.3.97/svn/SourceCode/SMPH/Beats/trunk/test/config/springbootService-release.properties","source":{"server.port":"6003","eureka.client.service-url.defaultZone":"http://localhost:5000/eureka/","spring.application.name":"springbootService"}}]}

4. 修改springbootService,使它能从配置中心读取配置文件

4.1. 在resources目录下创建bootstrap.yml,用来配置读取Config配置中心下的哪个文件

这里对应localhost:6002/springbootService-release.properties文件

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
spring:



application:



name : springbootService



cloud:



config:



uri : http://localhost:6002/



profile : release

4.2. 修改pom.xml,增加

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
<!--Spring Cloud Config 客户端依赖-->



<dependency>



<groupId>org.springframework.cloud</groupId>



<artifactId>spring-cloud-starter-config</artifactId>



<version>1.4.0.RELEASE</version>



</dependency>







<!--Spring Boot Actuator,感应服务端变化-->



<dependency>



<groupId>org.springframework.boot</groupId>



<artifactId>spring-boot-starter-actuator</artifactId>



</dependency>

4.3. 在ConsumerClientApplication.java中增加

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
@RefreshScope   //开启配置更新功能
package sun;







import org.springframework.boot.SpringApplication;



import org.springframework.boot.autoconfigure.SpringBootApplication;



import org.springframework.boot.web.servlet.ServletComponentScan;



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



import org.springframework.cloud.context.config.annotation.RefreshScope;







@SpringBootApplication



@ServletComponentScan



@EnableDiscoveryClient



@RefreshScope //开启配置更新功能



public class ConsumerClientApplication {







public static void main(String[] args) {







SpringApplication.run(ConsumerClientApplication.class, args);



}



}

4.4. 启动springbootService,在Eureka中可以看到,多了个端口是6003的服务。

img

4.5. 接口可以从6003访问,原来的6001不能用了,说明配置中心的文件覆盖了本地的application.properties

img

四. 搭建SpringBootConsumerFeign,使用Feign来调用各个微服务

1.直接从springbootService复制一份,命名为springbootConsumerFeign

别忘了把配置里面的名字都改掉

2. 项目结构和代码

img

2.1. 在pom中添加Feign

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
<dependency>



<groupId>org.springframework.cloud</groupId>



<artifactId>spring-cloud-starter-openfeign</artifactId>



<version>1.4.0.RELEASE</version>



</dependency>







<dependency>



<groupId>org.springframework.cloud</groupId>



<artifactId>spring-cloud-starter-hystrix</artifactId>



</dependency>







<dependency>



<groupId>com.netflix.hystrix</groupId>



<artifactId>hystrix-javanica</artifactId>



<version>RELEASE</version>



</dependency>

2.2. 修改application.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
spring.application.name=springbootConsumerFeign



server.port=6004







feign.hystrix.enabled=true







eureka.client.service-url.defaultZone=http://localhost:5000/eureka/

2.3. 在ConsumerClientApplication.java中增加

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
@EnableFeignClients
package sun;







import org.springframework.boot.SpringApplication;



import org.springframework.boot.autoconfigure.SpringBootApplication;



import org.springframework.boot.web.servlet.ServletComponentScan;



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



import org.springframework.cloud.context.config.annotation.RefreshScope;



import org.springframework.cloud.netflix.feign.EnableFeignClients;







@SpringBootApplication



@ServletComponentScan



@EnableDiscoveryClient



@EnableFeignClients



public class ConsumerClientApplication {







public static void main(String[] args) {







SpringApplication.run(ConsumerClientApplication.class, args);



}



}

2.4. 在sun目录下添加client目录,并新建文件ServiceFeignClient

这里是声明接口,指向springbootService的接口

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
package sun.client;







import common.entity.RestfulResult;



import org.springframework.cloud.netflix.feign.FeignClient;



import org.springframework.stereotype.Component;



import org.springframework.web.bind.annotation.RequestBody;



import org.springframework.web.bind.annotation.RequestMapping;



import sun.entity.ServiceInfo;







@Component



@FeignClient(value = "springbootService", fallback=ServiceFallback.class) //这里的value对应调用服务的spring.applicatoin.name



public interface ServiceFeignClient {







@RequestMapping(value = "/service/hello")



RestfulResult hello(@RequestBody ServiceInfo serviceInfo);







}

添加ServiceFallback.java,用于熔断发生时的处理。

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
package sun.client;







import common.entity.RestfulResult;



import org.springframework.stereotype.Component;



import sun.entity.ServiceInfo;







@Component



public class ServiceFallback implements ServiceFeignClient{







@Override



public RestfulResult hello(ServiceInfo serviceInfo) {



RestfulResult result = new RestfulResult();



result.setData("服务调用失败");



return result;



}



}

2.5. 修改ServiceController.java

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
/**



* Copyright &copy; 2012-2014 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved.



*/



package sun.controller;







import common.entity.RestfulResult;



import common.utils.CommUtils;



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



import org.springframework.web.bind.annotation.RequestBody;



import org.springframework.web.bind.annotation.RequestMapping;



import org.springframework.web.bind.annotation.RestController;



import sun.client.ServiceFeignClient;



import sun.entity.ServiceInfo;







import javax.servlet.http.HttpServletRequest;



import javax.servlet.http.HttpServletResponse;







@RestController



public class ServiceController {







@Autowired



ServiceFeignClient serviceFeignClient;







// 调用:localhost:6004/consumerService?token=1



@RequestMapping("/consumerService")



public void consumerService(HttpServletRequest request, HttpServletResponse response,



@RequestBody ServiceInfo serviceInfo){







RestfulResult restfulResult = serviceFeignClient.hello(serviceInfo);







CommUtils.printDataJason(response, restfulResult);



}



}

3. 启动springbootConsumer并测试接口

3.1. 启动后,在Eureka中发现服务

img

3.2. 调用SpringBootConsumer的接口http://localhost:6004/consumerService

连续调用,会轮询Service1和Service2

img

img

关闭Service1,看看Hystrix是否起作用:

img

五. 搭建SpringBootConsumerRibbon,使用Ribbon+RestTemplate来调用各个微服务

1.直接从springbootService复制一份,命名为springbootConsumerRibbon

别忘了把配置里面的名字都改掉

2. 项目结构和代码

img

2.1. 在pom中添加Feign

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
<dependency>



<groupId>org.springframework.cloud</groupId>



<artifactId>spring-cloud-starter-openfeign</artifactId>



<version>1.4.0.RELEASE</version>



</dependency>



<dependency>



<groupId>org.springframework.cloud</groupId>



<artifactId>spring-cloud-starter-hystrix</artifactId>



</dependency>



<dependency>



<groupId>com.netflix.hystrix</groupId>



<artifactId>hystrix-javanica</artifactId>



<version>RELEASE</version>



</dependency>



<dependency>



<groupId>org.springframework.cloud</groupId>



<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>



<version>1.4.0.RELEASE</version>



</dependency>

2.2. 修改application.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
spring.application.name=springbootConsumerRibbon



server.port=6007







eureka.client.service-url.defaultZone=http://localhost:5000/eureka/

2.3. 修改ConsumerClientApplication.java

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
package sun;







import org.springframework.boot.SpringApplication;



import org.springframework.boot.autoconfigure.SpringBootApplication;



import org.springframework.boot.web.servlet.ServletComponentScan;



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



import org.springframework.cloud.client.loadbalancer.LoadBalanced;



import org.springframework.cloud.context.config.annotation.RefreshScope;



import org.springframework.cloud.netflix.feign.EnableFeignClients;



import org.springframework.cloud.netflix.hystrix.EnableHystrix;



import org.springframework.context.annotation.Bean;



import org.springframework.web.client.RestTemplate;







@SpringBootApplication



@ServletComponentScan



@EnableDiscoveryClient



@EnableHystrix



public class ConsumerClientApplication {







public static void main(String[] args) {







SpringApplication.run(ConsumerClientApplication.class, args);



}







@Bean



@LoadBalanced



RestTemplate restTemplate(){



return new RestTemplate();



}



}

2.4. 修改ServiceController.java

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
/**



* Copyright &copy; 2012-2014 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved.



*/



package sun.controller;







import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;



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



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



import org.springframework.web.bind.annotation.RequestBody;



import org.springframework.web.bind.annotation.RequestMapping;



import org.springframework.web.bind.annotation.RestController;



import org.springframework.web.client.RestTemplate;



import sun.entity.ServiceInfo;







@RestController



public class ServiceController {







@Autowired



RestTemplate restTemplate;







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



String port;







// 调用:localhost:6007/consumerServiceRibbon?token=1



@RequestMapping("/consumerServiceRibbon")



@HystrixCommand(fallbackMethod="consumerServiceRibbonFallback")



public String consumerServiceRibbon(@RequestBody ServiceInfo serviceInfo){



String result = this.restTemplate.postForObject("http://springbootService/service/rest?token=1", serviceInfo, String.class);



return result;



}







public String consumerServiceRibbonFallback(@RequestBody ServiceInfo serviceInfo){



return "consumerServiceRibbon异常,端口:" + port + ",Name=" + serviceInfo.getName();



}



}

3. 启动springbootConsumer并测试接口

3.1. 启动后,在Eureka中发现服务

img

3.2. 调用SpringBootConsumer的接口 localhost:6007/consumerServiceRibbon?token=1

连续调用,会轮询Service1和Service2

imgimg

关闭Service1,看看Hystrix是否起作用:

img

六. 搭建Zuul+Hystrix

Zuul对外提供统一的服务入口,主要是用作网址重定向。

还可以通过Filter实现过滤器。

配合Hystrix实现熔断器,当服务宕机时可以做异常处理。

1. 创建Maven项目springbootZuul

同springbootService

2. 项目结构和代码

img

pom.xml

这里我把spring boot改成了2.0版本,spring cloud改成了Finchley.SR2。

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
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
<?xml version="1.0" encoding="UTF-8"?>



<project xmlns="http://maven.apache.org/POM/4.0.0"



xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"



xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">



<modelVersion>4.0.0</modelVersion>







<groupId>com.sun</groupId>



<artifactId>springbootZuul</artifactId>



<version>1.0-SNAPSHOT</version>



<packaging>jar</packaging>







<parent>



<groupId>org.springframework.boot</groupId>



<artifactId>spring-boot-starter-parent</artifactId>



<!--<version>1.5.8.RELEASE</version>-->



<version>2.0.0.RELEASE</version>



</parent>







<properties>



<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>



<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>



<java.version>1.8</java.version>



<spring-cloud.version>Finchley.SR2</spring-cloud.version>



<!--<spring-cloud.version>Edgware.SR2</spring-cloud.version>-->



</properties>







<dependencies>



<dependency>



<groupId>org.springframework.cloud</groupId>



<artifactId>spring-cloud-starter-eureka</artifactId>



<version>1.4.0.RELEASE</version>



</dependency>







<dependency>



<groupId>org.springframework.cloud</groupId>



<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>



</dependency>







<dependency>



<groupId>org.springframework.cloud</groupId>



<artifactId>spring-cloud-starter-netflix-zuul</artifactId>



</dependency>







<!-- 配置hystrix所需依赖的包 -->



<dependency>



<groupId>org.springframework.cloud</groupId>



<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>



</dependency>



</dependencies>







<dependencyManagement>



<dependencies>



<dependency>



<groupId>org.springframework.cloud</groupId>



<artifactId>spring-cloud-dependencies</artifactId>



<version>${spring-cloud.version}</version>



<type>pom</type>



<scope>import</scope>



</dependency>



</dependencies>



</dependencyManagement>







<build>



<plugins>



<plugin>



<groupId>org.springframework.boot</groupId>



<artifactId>spring-boot-maven-plugin</artifactId>



</plugin>



</plugins>



</build>



</project>

application.yml

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
spring:



application:



name : springbootZuul







server:



port : 6005







eureka:



client:



service-url:



defaultZone : http://localhost:5000/eureka/







zuul:



routes:



sbService :



path : /sbService/**



serviceId : springbootService

ConsumerClientApplication.java

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
package sun;







import org.springframework.boot.SpringApplication;



import org.springframework.boot.autoconfigure.SpringBootApplication;



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



import org.springframework.cloud.context.config.annotation.RefreshScope;



import org.springframework.cloud.netflix.eureka.EnableEurekaClient;



import org.springframework.cloud.netflix.zuul.EnableZuulProxy;







@SpringBootApplication



@EnableDiscoveryClient



@EnableZuulProxy



@RefreshScope



public class ConsumerClientApplication {



public static void main(String[] args) {



SpringApplication.run(ConsumerClientApplication.class, args);



}







}

serviceFilter(过滤器,非必须)

这里判断了请求中是否带token,如果没有,则显示”there is no request token”

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
package sun.filter;







import java.io.IOException;







import javax.servlet.http.HttpServletRequest;







import org.slf4j.Logger;



import org.slf4j.LoggerFactory;



import org.springframework.stereotype.Component;







import com.netflix.zuul.ZuulFilter;



import com.netflix.zuul.context.RequestContext;



import com.netflix.zuul.exception.ZuulException;







@Component



public class serviceFilter extends ZuulFilter {







private static Logger log=LoggerFactory.getLogger(serviceFilter.class);







@Override



public String filterType() {



return "pre"; // 定义filter的类型,有pre、route、post、error四种



}







@Override



public int filterOrder() {



return 0; // 定义filter的顺序,数字越小表示顺序越高,越先执行



}







@Override



public boolean shouldFilter() {



return true; // 表示是否需要执行该filter,true表示执行,false表示不执行



}







@Override



public Object run() {



// filter需要执行的具体操作



RequestContext ctx = RequestContext.getCurrentContext();



HttpServletRequest request = ctx.getRequest();



String token = request.getParameter("token");



System.out.println(token);



if(token==null){



log.warn("there is no request token");



ctx.setSendZuulResponse(false);



ctx.setResponseStatusCode(401);



try {



ctx.getResponse().getWriter().write("there is no request token");



} catch (IOException e) {



e.printStackTrace();



}



return null;



}



log.info("ok");



return null;



}



}
ServiceFallbackProvider(熔断器,非必须)

当没有服务时,显示”Sorry, the service is unavailable now.”

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
246
247
248
249
package sun.fallbackProvider;







import java.io.ByteArrayInputStream;



import java.io.IOException;



import java.io.InputStream;







import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;



import org.springframework.http.HttpHeaders;



import org.springframework.http.HttpStatus;



import org.springframework.http.MediaType;



import org.springframework.http.client.ClientHttpResponse;



import org.springframework.stereotype.Component;







@Component



public class ServiceFallbackProvider implements FallbackProvider {



@Override



// 指定熔断器功能应用于哪些路由的服务



public String getRoute() {



// 这里只针对"springbootService"服务进行熔断



// 如果需要针对所有服务熔断,则return "*"



return "springbootService";



}







@Override



public ClientHttpResponse fallbackResponse(String route, Throwable cause) {



System.out.println("route:"+route);







return new ClientHttpResponse() {



@Override



public HttpStatus getStatusCode() throws IOException {



return HttpStatus.OK;



}







@Override



public int getRawStatusCode() throws IOException {



return 200;



}







@Override



public String getStatusText() throws IOException {



return "ok";



}







@Override



public void close() {







}







@Override



// 发生熔断式,返回的信息



public InputStream getBody() throws IOException {



return new ByteArrayInputStream("Sorry, the service is unavailable now.".getBytes());



}







@Override



public HttpHeaders getHeaders() {



HttpHeaders headers = new HttpHeaders();



headers.setContentType(MediaType.APPLICATION_JSON);



return headers;



}



};



}



}

3. 启动springbootZuul并测试

3.1. 在Eureka上看到springbootZuul服务已启动

img

3.2. 现在可以通过统一路由访问服务了

img

3.3. 使用Filter,请求不带token时的效果:

img

3.4. 使用Filter,请求带token时的效果:

img

3.5. 使用Hystrix,关闭springbootService服务时的效果:

img

六. 自动化部署Jenkins

1. 将代码上传到Github

SVN也可以,这里我就用Github了。

先去下载Git官网下载Git-2.23.0-64-bit.exe

再去下载TortoiseGit,有了这个就不用老是打命令commit了。

1.1. 图形界面的更新代码

img

Git的项目初始化就不多说了,这里介绍一下用图形界面的代码更新步骤。

① Git Commit -> “master”

② Push

1.2. 我在将Git从1.X升级到2.X的过程中,遇到了

SourceTree error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version

的问题,将Git版本升级后也解决不了。

后来想起来,是不是在环境变量中配置了Git1.X的路径

果然,在系统里面的Path中找到了Git的配置。

原来Git2.X以后,默认路径放到了C:\Program Files\Git下,和原来1.X的路径不一样了。

所以需要把1.X的路径配置删除,否则还是会优先使用1.X的Git。

2. 搭建Jenkins环境

2.1. 去Jenkins官网下载war包

2.2. 启动Jenkins的war包

1
java -jar jenkins.war --httpPort=8080

启动后,访问http://localhost:8080即可。

使用的时候需要注册,这些就省略不说了。

2.3. Jenkins插件的安装

需要安装Maven,Git等插件,才能和项目关联使用

Manage Jenkins -> Manage Plugins -> Available里面找

img

img

img

img

2.4. 创建新项目

img

在Source Code Management里面选择Git,填入自己的仓库地址:

img

在Build Triggers里面,选择Poll SCM,在Schedule中填入 * * * * * :

img

在Build中,设置pom.xml的路径

一开始可能会报找不到pom.xml的Error,因为Jenkins需要先从Git库中同步项目,等同步完以后,Error就会自动消失。

img

在Post Steps中,Add post-build stemp -> Execute Windows bath command

img

添加的windows命令如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
D:\JenkinsTest\process.bat 6001
D:



del D:\JenkinsTest\springbootService-1.0-SNAPSHOT.jar



copy C:\Users\Administrator\.jenkins\workspace\SpringbootService\target\springbootService-1.0-SNAPSHOT.jar D:\JenkinsTest\springbootService-1.0-SNAPSHOT.jar



SET BUILD_ID=donKillMe



start javaw -Dhudson.util.ProcessTree.disable=true -jar D:\JenkinsTest\springbootService-1.0-SNAPSHOT.jar

效果如图:

img

其中,D:\JenkinsTest\process.bat的内容如下:

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
::demo



@echo off



::延迟环境变量扩展



setlocal enabledelayedexpansion



for /f "delims= tokens=1" %%i in ('netstat -aon ^| findstr %1') do (



set a=%%i)



::判断服务是否已经启动,如果启动则杀掉进程



if defined a (taskkill /F /pid "!a:~71,7!") else (echo Service does not exist)



::等待你按任意键结束



pause>nul



::执行时后面带上端口即可

命令说明:

① 创建D:\JenkinsTest目录

② process.bat 6001

是为了查找是否有占用6001端口的进程,如果有,则关闭6001端口的进程,因为我这里的Service启用的是6001端口。

③ 删除D:\JenkinsTest目录下的Service的Jar包,并将Jenkins自动打包生成的Jar包拷过来

然后通过Start javaw -jar *.jar命令在后台启动jar包。

④ 由于Jenkins默认在自动Build完成后,会关闭所有子进程,所以用下面这个命令可以避免Service被关闭

SET BUILD_ID=donKillMe

⑤ 据说start javaw -Dhudson.util.ProcessTree.disable=true -jar *.jar也有用,但是我用下来好像子进程还是被关闭了。

3. 测试

现在,当我更新代码到Git上以后,Jenkins就会自动将代码打包成Jar,然后执行我预先写好的命令自动部署启动。

img

在Console Output中可以看到Jenkins的执行内容:

img

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
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
Started by user sun wenli



Running as SYSTEM



Building in workspace C:\Users\Administrator\.jenkins\workspace\SpringbootService



No credentials specified



> git.exe rev-parse --is-inside-work-tree # timeout=10



Fetching changes from the remote Git repository



> git.exe config remote.origin.url https://github.com/sunroyi/springbootService.git # timeout=10



Fetching upstream changes from https://github.com/sunroyi/springbootService.git



> git.exe --version # timeout=10



> git.exe fetch --tags --force --progress https://github.com/sunroyi/springbootService.git +refs/heads/*:refs/remotes/origin/*



> git.exe rev-parse "refs/remotes/origin/master^{commit}" # timeout=10



> git.exe rev-parse "refs/remotes/origin/origin/master^{commit}" # timeout=10



Checking out Revision 7ee3c59e4573b4256e6949ebbaeb99c9d398a841 (refs/remotes/origin/master)



> git.exe config core.sparsecheckout # timeout=10



> git.exe checkout -f 7ee3c59e4573b4256e6949ebbaeb99c9d398a841



Commit message: "3"



> git.exe rev-list --no-walk 7ee3c59e4573b4256e6949ebbaeb99c9d398a841 # timeout=10



Parsing POMs



Established TCP socket on 58566



[SpringbootService] $ java -cp C:\Users\Administrator\.jenkins\plugins\maven-plugin\WEB-INF\lib\maven31-agent-1.13.jar;D:\maven\apache-maven-3.1.1\boot\plexus-classworlds-2.5.1.jar;D:\maven\apache-maven-3.1.1/conf/logging jenkins.maven3.agent.Maven31Main D:\maven\apache-maven-3.1.1 C:\Users\Administrator\.jenkins\war\WEB-INF\lib\remoting-3.29.jar C:\Users\Administrator\.jenkins\plugins\maven-plugin\WEB-INF\lib\maven31-interceptor-1.13.jar C:\Users\Administrator\.jenkins\plugins\maven-plugin\WEB-INF\lib\maven3-interceptor-commons-1.13.jar 58566



<===[JENKINS REMOTING CAPACITY]===>channel started



Executing Maven: -B -f C:\Users\Administrator\.jenkins\workspace\SpringbootService\pom.xml clean package



[INFO] Scanning for projects...



[INFO]



[INFO] ------------------------------------------------------------------------



[INFO] Building springbootService 1.0-SNAPSHOT



[INFO] ------------------------------------------------------------------------



[INFO]



[INFO] --- maven-clean-plugin:2.6.1:clean (default-clean) @ springbootService ---



[INFO] Deleting C:\Users\Administrator\.jenkins\workspace\SpringbootService\target



[INFO]



[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ springbootService ---



[INFO] Using 'UTF-8' encoding to copy filtered resources.



[INFO] Copying 1 resource



[INFO] Copying 1 resource



[INFO]



[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ springbootService ---



[INFO] Changes detected - recompiling the module!



[INFO] Compiling 7 source files to C:\Users\Administrator\.jenkins\workspace\SpringbootService\target\classes



[INFO]



[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ springbootService ---



[INFO] Using 'UTF-8' encoding to copy filtered resources.



[INFO] skip non existing resourceDirectory C:\Users\Administrator\.jenkins\workspace\SpringbootService\src\test\resources



[INFO]



[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ springbootService ---



[INFO] No sources to compile



[INFO]



[INFO] --- maven-surefire-plugin:2.18.1:test (default-test) @ springbootService ---



[INFO] No tests to run.



[JENKINS] Recording test results



[INFO]



[INFO] --- maven-jar-plugin:2.6:jar (default-jar) @ springbootService ---



[INFO] Building jar: C:\Users\Administrator\.jenkins\workspace\SpringbootService\target\springbootService-1.0-SNAPSHOT.jar



[INFO]



[INFO] --- spring-boot-maven-plugin:1.5.8.RELEASE:repackage (default) @ springbootService ---



[INFO] ------------------------------------------------------------------------



[INFO] BUILD SUCCESS



[INFO] ------------------------------------------------------------------------



[INFO] Total time: 34.060s



[INFO] Finished at: Thu Sep 12 11:27:02 CST 2019



[INFO] Final Memory: 37M/403M



[INFO] ------------------------------------------------------------------------



Waiting for Jenkins to finish collecting data



[JENKINS] Archiving C:\Users\Administrator\.jenkins\workspace\SpringbootService\pom.xml to com.sun/springbootService/1.0-SNAPSHOT/springbootService-1.0-SNAPSHOT.pom



[JENKINS] Archiving C:\Users\Administrator\.jenkins\workspace\SpringbootService\target\springbootService-1.0-SNAPSHOT.jar to com.sun/springbootService/1.0-SNAPSHOT/springbootService-1.0-SNAPSHOT.jar



channel stopped



[SpringbootService] $ cmd /c call C:\Users\ADMINI~1\AppData\Local\Temp\jenkins2482229857112663558.bat







C:\Users\Administrator\.jenkins\workspace\SpringbootService>D:\JenkinsTest\process.bat 6001



成功: 已终止 PID 为 1299256 的进程。



[SpringbootService] $ cmd /c call C:\Users\ADMINI~1\AppData\Local\Temp\jenkins7623094155436157631.bat







C:\Users\Administrator\.jenkins\workspace\SpringbootService>D:







D:\workspace20160509\Jenkins>del D:\JenkinsTest\springbootService-1.0-SNAPSHOT.jar







D:\workspace20160509\Jenkins>copy C:\Users\Administrator\.jenkins\workspace\SpringbootService\target\springbootService-1.0-SNAPSHOT.jar D:\JenkinsTest\springbootService-1.0-SNAPSHOT.jar



已复制 1 个文件。







D:\workspace20160509\Jenkins>SET BUILD_ID=donKillMe







D:\workspace20160509\Jenkins>start javaw -Dhudson.util.ProcessTree.disable=true -jar D:\JenkinsTest\springbootService-1.0-SNAPSHOT.jar







D:\workspace20160509\Jenkins>exit 0



Process leaked file descriptors. See https://jenkins.io/redirect/troubleshooting/process-leaked-file-descriptors for more information



Finished: SUCCESS

结果验证:

img