一、技术版本与选型说明
项目地址
:https://gitee.com/codeming2000/cloud-first
版本说明:
技术 | 版本 |
---|---|
cloud | Hoxton.SR1 |
boot | 2.2.2.RELEASE |
cloud alibaba | 2.1.0.RELEASE |
java | java8 |
Maven | 3.5及以上 |
Mysql | 5.7及以上 |
关于Cloud各种组件的停更/升级/替换:
参考资料:
Spring Cloud
https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/
https://www.bookstack.cn/read/spring-cloud-docs/docs-index.md (中文)
Spring Boot
https://docs.spring.io/spring-boot/docs/2.2.2.RELEASE/reference/htmlsingle/
二、微服务架构编码构建
原则:约定 > 配置 > 编码
IDEA新建project工作空间
父工程步骤
New Project
聚合总工程名字
Maven选版本
工程名字
字符编码
注解生效激活
java编译版本选8
File Type过滤
父工程pom
1 |
|
Rest微服务工程构建
1.cloud-provider-payment8001微服务提供者支付Module模块
建cloud-provider-payment8001
创建完成后回到父工程查看pom文件变化
改POM文件
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
<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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.ming.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-provider-payment8001</artifactId>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>写YML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17server:
port: 8001
spring:
application:
name: cloud-payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db2019?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
username: root
password: 123456
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: com.ming.springcloud.entities主启动
1
2
3
4
5
6
7
8
9
10
11package com.ming.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class, args);
}
}业务类
1.建表SQL
1
2
3
4
5CREATE TABLE `payment` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`serial` VARCHAR(200) DEFAULT '',
PRIMARY KEY(`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf82.entitles
主实体Payment
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package com.ming.springcloud.entities;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
public class Payment implements Serializable {
private Long id;
private String serial;
} Json封装体CommonResult
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package com.ming.springcloud.entities;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
public class CommonResult<T> {
private Integer code;
private String message;
private T data;
public CommonResult(Integer code,String message){
this(code,message,null);
}
}3.dao
接口PaymentDao
1
2
3
4
5
6
7
8
9
10
11
12package com.ming.springcloud.dao;
import com.ming.springcloud.entities.Payment;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
public interface PaymentDao {
public int create(Payment payment);
public Payment getPaymentById(@Param("id") Long id);
} mybatis的映射文件PaymentMapper.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<!--src\main\resources\mapper\PaymentMapper.xml-->
<mapper namespace="com.atguigu.springcloud.dao.PaymentDao">
<insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
insert into payment(serial) values(${serial});
</insert>
<resultMap id="BaseResultMap" type="com.atguigu.springcloud.entities.Payment">
<id column="id" property="id" jdbcType="BIGINT"></id>
<id column="serial" property="serial" jdbcType="VARCHAR"></id>
</resultMap>
<select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
select * from payment where id=#{id}
</select>
</mapper>4.service
接口PaymentService
1
2
3
4
5
6
7
8
9
10package com.ming.springcloud.service;
import com.ming.springcloud.entities.Payment;
import org.apache.ibatis.annotations.Param;
public interface PaymentService {
public int create(Payment payment);
public Payment getPaymentById(@Param("id") Long id);
} 实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25package com.ming.springcloud.service.impl;
import com.ming.springcloud.dao.PaymentDao;
import com.ming.springcloud.entities.Payment;
import com.ming.springcloud.service.PaymentService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
public class PaymentServiceImpl implements PaymentService {
private PaymentDao paymentDao;
public int create(Payment payment) {
return paymentDao.create(payment);
}
public Payment getPaymentById(Long id) {
return paymentDao.getPaymentById(id);
}
}5.controller
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
42package com.ming.springcloud.controller;
import com.ming.springcloud.entities.CommonResult;
import com.ming.springcloud.entities.Payment;
import com.ming.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
4j
public class PaymentController {
private PaymentService paymentService;
"/payment/creat") (value =
public CommonResult create(@RequestBody Payment payment){
int result = paymentService.create(payment);
log.info("--------------插入结果:"+result);
if (result>0){
return new CommonResult(200,"插入数据库成功",result);
}else {
return new CommonResult(444,"插入数据库失败",null);
}
}
"/payment/get/{id}") (value =
public CommonResult getPaymentById(@PathVariable Long id){
Payment payment = paymentService.getPaymentById(id);
log.info("--------------插入结果:"+payment);
if (payment!=null){
return new CommonResult(200,"查询成功",payment);
}else {
return new CommonResult(444,"没有对应记录,查询ID:"+id,null);
}
}
}测试
http://localhost:8001/payment/get/1
postman模拟post
运行
通过修改idea的workpace.xml的方式来快速打开Run DashBoard窗口
开启Run DashBoard
小总结
1.建module
2.改POM
3.写YML
4.主启动
5.业务类
2.热部署Devtools
Adding devtools to your project
1
2
3
4
5
6<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>Adding plugin to your pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13下一段配置黏贴到父工程当中的pom里
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>Enabling automatic build
Update the value of
重启IDEA
3.cloud-consumer-order80微服务消费者订单Module模块
建cloud-consumer-order80
改POM
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
<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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.ming.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-consumer-order80</artifactId>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>写YML
1
2server:
port: 8000主启动
1
2
3
4
5
6
7
8
9
10
11package com.ming.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class, args);
}
}业务类
1.创建entities(将cloud-provider-payment8001工程下的entities包下的两个实体类复制过来)
2.首说RestTemplate
3.config配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14package com.ming.springcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
public class ApplicationContextConfig {
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}创建controller
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
31package com.ming.springcloud.controller;
import com.ming.springcloud.entities.CommonResult;
import com.ming.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
4j
public class OrderController {
public static final String PAYMENT_URL = "http://localhost:8001";
private RestTemplate restTemplate;
"/consumer/payment/create") (
public CommonResult<Payment> create(Payment payment) {
return restTemplate.postForObject(PAYMENT_URL + "/payment/creat", payment, CommonResult.class);
}
"/consumer/payment/get/{id}") (
public CommonResult<Payment> getPayment(@PathVariable("id") Long id) {
return restTemplate.getForObject(PAYMENT_URL + "/payment/get/"+ id, CommonResult.class);
}
}测试
先启动cloud-provider-payment8001
再启动cloud-consumer-order80
http://localhost:8000/consumer/payment/get/32
不要忘记@RequestBody注解(provider)
4.工程重构
观察问题
新建 cloud-api-commons
POM
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
<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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.ming.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-api-commons</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.1.0</version>
</dependency>
</dependencies>
</project>entities
Payment实体
CommonResult通用封装类
maven命令clean install
订单80和支付8001分别改造
删除各自的原先有过的entities文件夹
各自黏贴POM内容
1
2
3
4
5<dependency>
<groupId>com.ming.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
目前工程样图
三、Eureka服务注册与发现
Eureka基础知识
什么是服务治理
什么是服务注册
Eureka两组件
单机Eureka构建步骤
1.IDEA生成eurekaServer端服务注册中心
建Module
cloud-eureka-server7001
改POM
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
<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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.ming.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-eureka-server7001</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>com.ming.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
</project>写YML
1
2
3
4
5
6
7
8
9
10
11
12
13
14server:
port: 7001
eureka:
instance:
hostname: localhost #eureka服务端的实例名字
client:
#表识不向注册中心注册自己
register-with-eureka: false
#表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/主启动
1
2
3
4
5
6
7
8
9
10
11
12
13package com.minh.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class, args);
}
}测试
2.EurekaClient端cloud-provider-payment8001将注册进EurekaServer成为服务提供者provider
cloud-provider-payment8001
改POM
1
2
3
4<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>写YML
1
2
3
4
5
6eureka:
client:
register-with-eureka: true
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka主启动
@EnableEurekaClient
测试
先要启动EurekaServer
微服务注册名配置说明
自我保护机制
3. EurekaClient端cloud-consumer-order80将注册进EurekaServer成为服务消费者consumer
cloud-consumer-order80
POM
1
2
3
4<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>写YML
1
2
3
4
5
6
7
8
9
10spring:
application:
name: cloud-order-service
eureka:
client:
register-with-eureka: true
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka主启动
@EnableEurekaClient
测试
集群Eureka构建步骤
1.Eureka集群原理说明
2.EurekaServer集群环境构建步骤
参考cloud-eureka-server7001
新建cloud-eureka-server7002
改POM
修改映射配置
找到C:\Windows\System32\drivers\etc路径下的hosts文件
1
2127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com写YML
7001
1
2
3
4
5
6
7
8
9
10
11
12
13
14server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名字
client:
#表识不向注册中心注册自己
register-with-eureka: false
#表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://eureka7002.com:7002/eureka/7002
1
2
3
4
5
6
7
8
9
10
11
12
13
14server:
port: 7002
eureka:
instance:
hostname: eureka7002.com #eureka服务端的实例名字
client:
#表识不向注册中心注册自己
register-with-eureka: false
#表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://eureka7001.com:7001/eureka/主启动
3.将支付服务8001微服务发布到上面2台Eureka集群配置中
1 | service-url: |
4.将订单服务80微服务发布到上面2台Eureka集群配置中
1 | service-url: |
5.测试1
- 先要启动EurekaServer,7001/7002服务
- 再要启动服务提供者provider,8001服务
- 再要启动消费者,80
- http://localhost/consumer/payment/get/31
6.支付服务提供者8001集群环境构建
参考cloud-provider-payment8001
新建cloud-provider-payment8002
改POM
写YML
主启动类
业务类
修改8001/8002的Controller
7.负载均衡
订单服务访问地址不能写死
1
public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
1
2
3
4
5
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
8.测试2
先要启动EurekaServer,7001/7002服务
再要启动服务提供者provider,8001/8002服务
结果
负载均衡效果达到
8001/8002端口交替出现
Ribbon和Eureka整合后Consumer可以直接调用服务而不用再关心地址和端口号,且该服务还有负载功能了
actuator微服务信息完善
1.主机名称:服务名称修改
修改cloud-provider-payment
1
2
3
4
5
6
7
8
9eureka:
client:
register-with-eureka: true
fetchRegistry: true
service-url:
# defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
instance:
instance-id: payment8002
2.访问信息有ip信息提示
修改cloud-provider-payment
1
2
3
4
5
6
7
8
9
10eureka:
client:
register-with-eureka: true
fetchRegistry: true
service-url:
# defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
instance:
instance-id: payment8001
prefer-ip-address: true
服务发现Discovery
对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息
修改cloud-provider-payment8001的Controller
1 |
|
8001主启动类 @EnableDiscoveryClient
测试:
- 先要启动EurekaServer,7001/7002服务
- 再启动8001主启动类,需要稍等一会
- http://localhost:8001/payment/discovery
Eureka自我保护
1.故障现象
2.导致原因
- 一句话:某时刻某一个微服务不可用了,Eureka不会立刻清理,依旧会对该微服务的信息进行保存
- 属于CAP里面的AP分支
3.怎么禁止自我保护(一般生产环境中不会禁止自我保护)
注册中心eureakeServer端7001
出厂默认,自我保护机制是开启的 eureka.server.enable-self-preservation = true
使用eureka.server.enable-self-preservation = false可以禁用自我保护模式
1 | server: |
生产者客户端eureakeClient端8001
默认
eureka.instance.lease-renewal-interval-in-seconds=30 单位为秒(默认是30秒)
eureka.instance.lease-expiration-duration-in-seconds=90 单位为秒(默认是90秒)
配置
测试
7001和8001都配置完成
先启动7001再启动8001
先关闭8001–>马上被删除了
Eureka停止更新
https://github.com/Netflix/eureka/wiki
四、Zookeeper服务注册与发现
SpringCloud整合Zookeeper代替Eureka
1.注册中心Zookeeper
- zookeeper是一个分布式协调工具,可以实现注册中心功能
- 关闭Linux服务器防火墙后启动zookeeper服务器
- zookeeper服务器取代Eureka服务器,zk作为服务注册中心
2.服务提供者
新建cloud-provider-payment8004
POM
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
<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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.ming.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-provider-payment8004</artifactId>
<dependencies>
<dependency>
<groupId>com.ming.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-zookeeper-discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<!--排除zk3.5.3-->
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>YML
1
2
3
4
5
6
7
8
9server:
port: 8004
spring:
application:
name: cloud-provider-payment
cloud:
zookeeper:
connect-string: 192.168.0.205:2181主启动类
Controller
1
2
3
4
5
6
7
8
9
10
11
12
4j
public class PaymentController {
"${server.port}") (
private String serverPort;
"/payment/zk") (value =
public String paymentzk() {
return "springcloud with zookeeper:" + serverPort + "\t" + UUID.randomUUID().toString();
}
}启动8004注册进zookeeper
启动后问题
解决zookeeper版本jar包冲突问题
排出zk冲突后的新POM
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-zookeeper-discovery</artifactId>
<!--排除zk3.5.3-->
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.8</version>
</dependency>验证测试
验证测试2
思考
服务节点是临时节点还是持久节点? 临时节点 CP
3.服务消费者
新建cloud-consumerzk-order80
POM
YML
主启动
业务类
配置Bean
1
2
3
4
5
6
7
8
9
public class ApplicationContextConfig {
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
4j
public class OrderZKController {
public static final String INVOKE_URL = "http://cloud-provider-payment";
private RestTemplate restTemplate;
"/consumer/payment/zk") (
public String payment() {
String result = restTemplate.getForObject(INVOKE_URL + "/payment/zk", String.class);
return result;
}
}启动8004注册进zookeeper
验证测试
访问测试地址
五、Consul服务注册与发现
1. Consul简介
https://www.consul.io/intro/index.html
能干嘛
服务发现
提供HTTP和DNS两种发现方式
健康监测
支持多种协议,HTTP、TCP、Docker、Shell脚本定制化
KV存储
key , Value的存储方式
多数据中心
Consul支持多数据中心
可视化Web界面
去哪下
https://www.consul.io/downloads.html
怎么玩
https://www.springcloud.cc/spring-cloud-consul.html
2.安装并运行Consul
官网安装说明
https://learn.hashicorp.com/consul/getting-started/install.html
下载完成后只有一个consul.exe文件,硬盘路径下双击运行,查看版本信息
使用开发模式启动
consul agent -dev
通过以下地址可以访问Consul的首页:http;//localhost:8500
3.服务提供者
新建Module cloud-providerconsul-payment8006
POM
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
<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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.ming.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-providerconsul-payment8006</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.ming.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>YML
1
2
3
4
5
6
7
8
9
10
11
12server:
port: 8006
spring:
application:
name: consul-provider-payment
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}主启动类
1
2
3
4
5
6
7
8
9
10
11
12package com.ming.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
public class PaymentMain8006 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8006.class, args);
}
}业务类Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package com.ming.springcloud.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
4j
public class PaymentController {
"${server.port}") (
private String serverPort;
"/payment/consul") (value =
public String paymentzk() {
return "springcloud with consul:" + serverPort + "\t" + UUID.randomUUID().toString();
}
}验证测试
4.服务消费者
新建Module cloud-consumerconsul-order80
POM
YML
主启动类
配置Bean
1
2
3
4
5
public RestTemplate getRestTemplate(){
return new RestTemplate();
}Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
4j
public class OrderConsulController {
public static final String INVOKE_URL = "http://consul-provider-payment";
private RestTemplate restTemplate;
"/consumer/payment/consul") (
public String payment (){
String result = restTemplate.getForObject(INVOKE_URL+"/payment/consul",String.class);
return result;
}
}验证测试
访问测试地址
5.三个注册中心异同点
CAP
C:Consistency(强一致性)
A:Availability(可用性)
P:Partition tolerance(分区容错)
CAP理论关注粒度是数据,而不是整体系统设计的策略
经典CAP图
AP(Eureka)
CP(Zookeeper/Consul)
六、Ribbon负载均衡服务调用
1.概述
是什么
官网资料
https://github.com/Netflix/ribbon/wiki/Getting-Started
Ribbon目前也进入维护模式
未来替换方案
能干嘛
LB(负载均衡)
集中式LB
进程内LB
总结:负载均衡+RestTemplate调用
2.Ribbon负载均衡演示
架构说明
总结:Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。
POM
RestTemplate的使用
官网
getForObject方法/getForEntity方法
postForObject/postForEntity
GET请求方法
POST请求方法
3.Ribbon核心组件IRule
IRule:根据特定算法从服务列表中选取一个要访问的服务
com.netflix.loadbalancer.RoundRobinRule
轮询
com.netflix.loadbalancer.RandomRule
随机
com.netflix.loadbalancer.RetryRule
先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试
WeightedResponseTimeRule
对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择
BestAvailableRule
会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
AvailabilityFilteringRule
先过滤掉故障实例,再选择并发较小的实例
ZoneAvoidanceRule
默认规则,复合判断server所在区域的性能和server的可用性选择服务器
如何替换
修改cloud-consumer-order80
注意配置细节
新建package
com.ming.myrule
上面包下新建MySelfRule规则类
1
2
3
4
5
6
7
8
9
10
11
12
13
14package com.ming.myrule;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class MySelfRule {
public IRule myRule(){
return new RandomRule();//定义为随机
}
}主启动类添加@RibbonClient
1
2
3
4
5
6
7
8
"CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class) (name =
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class, args);
}
}测试
4. Ribbon负载均衡算法
原理
RoundRobinRule源码
1 | /* |
手写
ApplicationContextBean去掉@LoadBalanced
LoadBalancer接口
1
2
3
4
5
6
7
8
9
10package com.ming.springcloud.lb;
import org.springframework.cloud.client.ServiceInstance;
import java.util.List;
public interface LoadBalancer {
//收集服务器总共有多少台能够提供服务的机器,并放到list里面
ServiceInstance instances(List<ServiceInstance> serviceInstances);
}MyLB
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
30package com.ming.springcloud.lb;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class MyLB implements LoadBalancer {
private AtomicInteger atomicInteger = new AtomicInteger(0);
private final int getAndIncrement() {
int current;
int next;
do {
current = this.atomicInteger.get();
next = current >= 2147483647 ? 0 : current + 1;
} while (!this.atomicInteger.compareAndSet(current,next));//第一个参数是期望值,第二个参数是修改值是
System.out.println("*******第几次访问,次数next: "+next);
return next;
}
public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
int index = getAndIncrement() % serviceInstances.size(); //得到服务器的下标位置
return serviceInstances.get(index);
}
}OrderController
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private LoadBalancer loadBalancer;
private DiscoveryClient discoveryClient;
"/consumer/payment/lb") (value =
public String getPaymentLB(){
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
if (instances == null || instances.size() <= 0){
return null;
}
ServiceInstance serviceInstance = loadBalancer.instances(instances);
URI uri = serviceInstance.getUri();
return restTemplate.getForObject(uri+"/payment/lb",String.class);
}测试
七、OpenFeign服务接口调用
1.概述
OpenFeign是什么
Feign是一个声明式的web服务客户端,让编写web服务客户端变得非常容易,只需创建一个接口并在接口上添加注解即可
GitHub:https://github.com/spring-cloud/spring-cloud-openfeign
能干嘛
Feign和OpenFeign两者区别
2.OpenFeign使用步骤
接口+注解
微服务调用接口+@FeignClient
新建cloud-consumer-feign-order80
Feign在消费端使用
POM
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
<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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.ming.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-consumer-feign-order80</artifactId>
<!--openfeign-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.ming.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>YML
1
2
3
4
5
6
7server:
port: 8000
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka主启动类
1
2
3
4
5
6
7
public class OrderFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain80.class, args);
}
}业务类
业务逻辑接口+@FeignClient配置调用provider服务
新建PaymentFeignService接口并新增注解@FeignClient
1
2
3
4
5
6
7
"CLOUD-PAYMENT-SERVICE") (value =
public interface PaymentFeignService {
"/payment/get/{id}") (value =
public CommonResult getPaymentById(@PathVariable("id") Long id);
}控制层Controller
1
2
3
4
5
6
7
8
9
10
11
12
4j
public class OrderFeignController {
private PaymentFeignService paymentFeignService;
"/consumer/payment/get/{id}") (value =
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
return paymentFeignService.getPaymentById(id);
}
}测试
总结
3.OpenFeign超时控制
超时设置,故意设置超时演示出错情况
服务提供方8001故意写暂停程序
1
2
3
4
5"/payment/feign/timeout") (value =
public String paymentFeignTimeout(){
try { TimeUnit.SECONDS.sleep(3); }catch (Exception e) {e.printStackTrace();}
return serverPort;
}服务消费方80添加超时方法PaymentFeignService
1
2"/payment/feign/timeout") (value =
public String paymentFeignTimeout();服务消费方80添加超时方法OrderFeignController
1
2
3
4"/consumer/payment/feign/timeout") (value =
public String paymentFeignTimeout(){
return paymentFeignService.paymentFeignTimeout();
}测试
OpenFeign默认等待一秒钟,超过后报错
是什么
OpenFeign默认支持Ribbon
YML文件里需要开启OpenFeign客户端超时控制
1 | ribbon: |
4.OpenFeign日志打印功能
是什么
日志级别
配置日志bean
1 | package com.ming.springcloud.config; |
YML文件里需要开启日志的Feign客户端
1 | logging: |
八、Hystrix断路器
1.概述
分布式系统面临的问题
是什么
能干嘛
- 服务降级
- 服务熔断
- 接近实时的监控
- …
官网资料
https://github.com/Netflix/Hystrix/wiki/How-To-Use
Hystrix官宣,停更进维
https://github.com/Netflix/Hystrix
- 被动修复bugs
- 不再接受合并请求
- 不再发布新版本
2.Hystrix重要概念
服务降级
服务器忙,请稍候再试,不让客户端等待并立刻返回一个友好提示,fallback
哪些情况会触发降级
- 程序运行异常
- 超时
- 服务熔断触发服务降级
- 线程池/信号量打满也会导致服务降级
服务熔断
类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
就是保险丝
服务的降级->进而熔断->恢复调用链路
服务限流
秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行
3.hystrix案例
构建
新建cloud-provider-hystrix-payment8001
POM
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
<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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.ming.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-provider-hystrix-payment8001</artifactId>
<dependencies>
<!--新增hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.ming.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>YML
1
2
3
4
5
6
7
8
9
10
11
12
13server:
port: 8001
spring:
application:
name: cloud-provider-hystrix-payment
eureka:
client:
register-with-eureka: true #表识不向注册中心注册自己
fetch-registry: true #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://eureka7001.com:7001/eureka/主启动
1
2
3
4
5
6
7
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class, args);
}
}业务类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class PaymentService {
//成功
public String paymentInfo_OK(Integer id){
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_OK,id: "+id+"\t"+"哈哈哈" ;
}
//失败
public String paymentInfo_TimeOut(Integer id){
int timeNumber = 3;
try { TimeUnit.SECONDS.sleep(timeNumber); }catch (Exception e) {e.printStackTrace();}
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_TimeOut,id: "+id+"\t"+"呜呜呜"+" 耗时(秒)"+timeNumber;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
4j
public class PaymentController {
private PaymentService paymentService;
"${server.port}") (
private String serverPort;
"/payment/hystrix/ok/{id}") (
public String paymentInfo_OK(@PathVariable("id") Integer id){
String result = paymentService.paymentInfo_OK(id);
log.info("*******result:"+result);
return result;
}
"/payment/hystrix/timeout/{id}") (
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
String result = paymentService.paymentInfo_TimeOut(id);
log.info("*******result:"+result);
return result;
}
}正常测试
高并发测试
上述在非高并发情形下,还能勉强满足
Jmeter压测测试
开启Jmeter,来20000个并发压死8001,20000个请求都去访问paymentInfo_TimeOut服务
再来一个访问
看演示结果
两个都在自己转圈圈
为什么会被卡死
tomcat的默认的工作线程数被打满了,没有多余的线程来分解压力和处理。
Jmeter压测结论
上面还是服务提供者8001自己测试,假如此时外部的消费者80也来访问,那消费者只能干等,最终导致消费端80不满意,服务端8001直接被拖死
看热闹不嫌弃事大,80新建加入
新建 cloud-consumer-feign-hystrix-order80
POM
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
<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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.ming.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-consumer-feign-hystrix-order80</artifactId>
<dependencies>
<!--新增hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.ming.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>YML
1
2
3
4
5
6
7
8server:
port: 8000
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/主启动
1
2
3
4
5
6
7
public class OrderHystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderHystrixMain80.class,args);
}
}业务类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package com.ming.springcloud.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
"CLOUD-PROVIDER-HYSTRIX-PAYMENT") (value =
public interface PaymentHystrixService {
"/payment/hystrix/ok/{id}") (
public String paymentInfo_OK(@PathVariable("id") Integer id);
"/payment/hystrix/timeout/{id}") (
public String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}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
29package com.ming.springcloud.controller;
import com.ming.springcloud.service.PaymentHystrixService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
4j
public class OrderHystrixController {
private PaymentHystrixService paymentHystrixService;
"/consumer/payment/hystrix/ok/{id}") (
public String paymentInfo_OK(@PathVariable("id") Integer id){
return paymentHystrixService.paymentInfo_OK(id);
}
"/consumer/payment/hystrix/timeout/{id}") (
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
return paymentHystrixService.paymentInfo_TimeOut(id);
}
}正常测试
高并发测试
2W个线程压8001
消费端80微服务再去访问正常的OK微服务8001地址
http://localhost/consumer/payment/hystrix/timeout/1
消费者80 要么转圈圈等待 要么消费端报超时错误
故障现象和导致原因
8001同一层次的其他接口服务被困死,因为tomcat线程里面的工作线程已经被挤占完毕
80此时调用8001,客户端访问响应缓慢,转圈圈
结论:正因为有上述故障或不佳表现,才有我们的降级/容错/限流等技术诞生
如何解决?解决的要求
超时导致服务器变慢(转圈)
超时不再等待
出错(宕机或程序运行出错)
出错要有兜底
解决
对方服务(8001)超时了,调用者(80)不能一直卡死等待,必须有服务降级
对方服务(8001)down机了,调用者(80)不能一直卡死等待,必须有服务降级
对方服务(8001)OK,调用者(80)自己出故障或有自我要求(自己的等待时间小于服务提供者),自己处理降级
服务降级
降低配置 @HystrixCommand
8001先从自身找问题
设置自身调用超时时间的峰值,峰值内可以正常运行,超过了需要有兜底的方法处理,作服务降级fallback
8001fallback
业务类启用
1
2
3
4
5
6
7
8
9
10//失败
"paymentInfo_TimeOutHandler",commandProperties = { (fallbackMethod =
"execution.isolation.thread.timeoutInMilliseconds",value = "3000") //3秒钟以内就是正常的业务逻辑 (name =
})
public String paymentInfo_TimeOut(Integer id){
int a=10/0;
int timeNumber = 5;
try { TimeUnit.SECONDS.sleep(timeNumber); }catch (Exception e) {e.printStackTrace();}
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_TimeOut,id: "+id+"\t"+"呜呜呜"+" 耗时(秒)"+timeNumber;
}一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法
主启动类激活
添加新注解@EnableCircuitBreaker
80fallback
80订单微服务,也可以更好的保护自己,自己也依样画葫芦进行客户端降级保护
我们自己配置过的热部署方式对java代码的改动明显,但对@HystrixCommand内属性的修改建议重启微服务
YML
1 | feign: |
主启动 @EnableHystrix
业务类
1 | "/consumer/payment/hystrix/timeout/{id}") ( |
目前问题
每个方法配置一个???膨胀
和业务逻辑混一起???混乱
解决问题
膨胀问题
feign接口系列
@DefaultProperties(defaultFallback = “”)
controller配置
1
2
3
4
5
6
7
8
9
10"payment_Global_FallbackMethod") (defaultFallback =
public class OrderHystrixController {
/**
* 全局 fallback 方法
* @return
*/
public String payment_Global_FallbackMethod(){
return "Global异常处理信息,请稍后再试。/(╥﹏╥)/~~";
}
}方法上加@HystrixCommand 就找全局的
混乱问题
服务降级,客户端去调用服务端,碰上服务端宕机或关闭
本次案例服务降级处理是在客户端80实现完成的,与服务端8001没有关系,只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦
未来我们要面对的异常
运行
超时
宕机
再看我们的业务类PaymentController
修改cloud-consumer-feign-hystrix-order80
根据cloud-consumer-feign-hystrix-order80已经有的PaymentHystrixService接口,重新新建一个类(PaymentFallbackService)实现该接口,统一为接口里面的方法进行异常处理
PaymentFallbackService类实现PaymentFeignClientService接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package com.ming.springcloud.service;
import org.springframework.stereotype.Component;
public class PaymentFallbackService implements PaymentHystrixService {
public String paymentInfo_OK(Integer id) {
return "-----PaymentFallbackService fall back-paymentInfo_OK , (┬_┬)";
}
public String paymentInfo_TimeOut(Integer id) {
return "-----PaymentFallbackService fall back-paymentInfo_TimeOut , (┬_┬)";
}
}YML
1
2
3feign:
hystrix:
enabled: truePaymentFeignClientService接口
1
2
3
"CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class) (value =
public interface PaymentHystrixService {}测试
服务熔断
一句话就是家里保险丝
熔断是什么
https://martinfowler.com/bliki/CircuitBreaker.html
实操
修改cloud-provider-hystrix-payment8001
PaymentService
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//服务熔断
"paymentCircuitBreaker_fallback",commandProperties = { (fallbackMethod =
"circuitBreaker.enabled",value = "true"), //是否开启断路器 (name =
"circuitBreaker.requestVolumeThreshold",value = "10"), //请求次数 (name =
"circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //时间范围 (name =
"circuitBreaker.errorThresholdPercentage",value = "60"), //失败率达到多少后跳闸 (name =
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
if (id < 0){
throw new RuntimeException("*****id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
return "id 不能负数,请稍候再试,(┬_┬)/~~ id: " +id;
}PaymentController
1
2
3
4
5
6
7//===服务熔断
"/payment/circuit/{id}") (
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
String result = paymentService.paymentCircuitBreaker(id);
log.info("*******result:"+result);
return result;
}测试
自测cloud-provider-hystrix-payment8001
正确:http://localhost:8001/payment/circuit/1
错误:http://localhost:8001/payment/circuit/-1
一次正确一次错误trytry
重点测试 : 多次错误,然后慢慢正确,发现刚开始不满足条件,就算是正确的访问地址也不能进行访问,需要慢慢的恢复链路
总结
大神结论
熔断类型
熔断打开
请求不再进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入熔断状态
熔断关闭
熔断关闭不会对服务进行熔断
熔断半开
部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断
官网断路器流程图
官网步骤
断路器在什么情况下开始起作用
断路器开启或者关闭的条件
当满足一定阀值的时候(默认10秒内超过20个请求次数)
当失败率达到一定的时候(默认10秒内超过50%请求失败)
到达以上阀值,断路器将会开启
当开启的时候,所有请求都不会进行转发
一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。重复4和5
断路器打开之后
All配置
服务限流
后面高级篇alibaba的Sentinel说明
4.hystrix工作流程
https://github.com/Netflix/Hystrix/wiki/How-it-Works
步骤说明
5.服务监控hystrixDashboard
概述
仪表盘9001
新建cloud-consumer-hystrix-dashboard9001
POM
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
<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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.ming.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-consumer-hystrix-dashboard9001</artifactId>
<dependencies>
<!--新增hystrix dashboard-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>YML
1
2server:
port: 9001HystrixDashboardMain9001+新注解@EnableHystrixDashboard
1
2
3
4
5
6
7
8
9
10
11
12
13package com.ming.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
public class HystrixDashboardMain9001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardMain9001.class, args);
}
}所有Provider微服务提供类(8001/8002/8003)都需要监控依赖配置
1
2
3
4<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>启动cloud-consumer-hystrix-dashboard9001该微服务后续将监控微服务8001
断路器演示
修改cloud-provider-hystrix-payment8001
注意:新版本Hystrix需要在主启动类MainAppHystrix8001中指定监控路径
1
2
3
4
5
6
7
8
9
public ServletRegistrationBean getServlet(){
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}Unable to connect to Command Metric Stream
404
监控测试
启动1个eureka或者3个eureka集群均可
观察监控窗口
填写监控地址:http://localhost:8001/hystrix.stream
测试地址:
http://localhost:8001/payment/circuit/1
http://localhost:8001/payment/circuit/-1
先访问正确地址,再访问错误地址,再正确地址,会发现图示断路器都是慢慢放开的
如何看
1圈
1线
整图说明
整图说明2
搞懂一个才能看懂复杂的
九、Gateway新一代网关
1.概述简介
官网
上一代zuul 1.X:
https://github.com/Netflix/zuul/wiki
当前gateway:
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/
是什么
Spring Cloud Gateway 使用的Webflux中的reactor-netty响应式编程组件,底层使用了Netty通讯框架
源码架构
能干嘛
- 反向代理
- 鉴权
- 流量控制
- 熔断
- 日志监控
有了Zuul了怎么又出来了gateway
我们为什么选择Gatway?
neflix不太靠谱,zuul2.0一直跳票,迟迟不发布
SpringCloud Gateway具有如下特性
SpringCloud Gateway与Zuul的区别
Zuul1.x模型
GateWay模型
WebFlux是什么?
2.三大核心概念
Route(路由)
路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由
Predicate(断言)
参考的是java8的java.util.function.Predicate开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
Filter(过滤)
指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
总体
3.Gateway工作流程
官网总结
核心逻辑
路由转发+执行过滤器链
4.入门配置
新建Module cloud-gateway-gateway9527
POM
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
<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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.ming.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-gateway-gateway9527</artifactId>
<dependencies>
<!--新增gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.ming.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>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
37server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh #payment_routh #路由的ID,没有固定规则但要求唯一,简易配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
# uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2 #payment_routh #路由的ID,没有固定规则但要求唯一,简易配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
# uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
#- After=2020-03-15T15:35:07.412+08:00[GMT+08:00]
#- Cookie=username,zzyy
#- Header=X-Request-Id, \d+ #请求头要有X-Request-Id属性并且值为整数的正则表达式
#- Host=**.atguigu.com
#- Method=GET
#- Query=username, \d+ #要有参数名username并且值还要啥整数才能路由
eureka:
instance:
hostname: cloud-gateway-service
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka业务类(无)
主启动类
1
2
3
4
5
6
7
public class GateWayMain9527 {
public static void main(String[] args) {
SpringApplication.run(GateWayMain9527.class, args);
}
}9527网关如何做路由映射那???
cloud-provider-payment8001看看controller的访问地址
get lb
我们目前不想暴露8001端口,希望在8001外面套一层9527
YML新增网关配置
测试
YML配置说明
Gateway网关路由有两种配置方式
在配置文件yml中配置
代码中注入RouteLocator的Bean
自己写一个
需求:通过9527网关访问到外网的百度新闻网址
cloud-gateway-gateway9527
1
2
3
4
5
6
7
8
9
10
11
12
13
public class GatewayConfig {
public RouteLocator customRouterLocator(RouteLocatorBuilder routeLocatorBuilder){
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
routes.route("path_route_codeming",
r->r.path("/guonei")
.uri("http://news.baidu.com/guonei")).build();
return routes.build();
}
}
5.通过微服务名实现动态路由
默认情况下Gateway会根据注册中心的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能
启动
一个eureka7001+两个服务提供者8001/8002
POM
YML
需要注意的是uri的协议为lb,表示启用Gateway的负载均衡功能。
lb://serviceName是spring cloud gateway在微服务中自动为我们创建的负载均衡uri
1 | server: |
测试
http://localhost:9527/payment/lb 8001/8002两个端口切换
6.Predicate的使用
是什么
启动我们的gatewat9527
Route Predicate Factories这是什么?
常用的Route Predicate
After Route Predicate
Before Route Predicate
1
2- After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
- Before=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]Between Route Predicate
1
- Between=2020-03-08T10:59:34.102+08:00[Asia/Shanghai] , 2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
Cookie Route Predicate
不带cookies访问
带上cookies访问
1
- Cookie=username,zzyy #并且Cookie是username=zhangshuai才能访问
Header Route Predicate
1
- Header=X-Request-Id, \d+ #请求头中要有X-Request-Id属性并且值为整数的正则表达式
Host Route Predicate
1
- Host=**.atguigu.com
Method Route Predicate
1
- Method=GET
Path Route Predicate
Query Route Predicate
1
- Query=username, \d+ #要有参数名称并且是正整数才能路由
总结
说白了,Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理
7.Filter的使用
是什么
Spring Cloud Gateway的Filter
生命周期,Only Two
- pre :在业务逻辑之前
- post :在业务逻辑之后
种类,Only Two
GatewayFilter :单一
GlobalFilter :全局
常用的GatewayFilter
AddRequestParameter
自定义过滤器
自定义全局GlobalFilter
两个主要接口介绍
1 | public class MyLogGateWayFilter implements GlobalFilter, Ordered |
能干嘛
- 全局日志记录
- 统一网关鉴权
案例代码
1 | package com.ming.springcloud.filter; |
测试
正确:http://localhost:9527/payment/lb?uname=1
十、SpringCloud config分布式配置中心
1.概述
分布式系统面临的配置问题
是什么
能干嘛
集中管理配置文件
不同环境不同配置,动态化的配置更新,分环境部署比如dev/test/prod/beta/release
运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务会向配置中心统一拉取配置自己的信息
当配置发生变动时,服务不需要重启即可感知到配置的变化并应用新的配置
将配置信息以REST接口的形式暴露
post、curl访问刷新均可….
与Github整合配置
由于SpringCloud Config默认使用Git来存储配置文件(也有其它方式,比如支持svn和本地文件,但最推荐的还是Git,而且使用的是http/https访问的形式)
官网
https://cloud.spring.io/spring-cloud-static/spring-cloud-config/2.2.1.RELEASE/reference/html/
2.Config服务端配置与测试
用你自己的账号在Github上新建一个名为sprincloud-config的新Repository
由上一步获得刚新建的git地址
写你自己的仓库地址
本地硬盘上新建git仓库并clone
此时在本地D盘符下D:\44\SpringCloud2020\springcloud-config
表示多个环境的配置文件
保存格式必须为UTF-8
如果需要修改,此处模拟运维人员操作git和github
1
2
3git add .
git commit -m "init yml"
git push origin master新建Module模块cloud-config-center-3344它既为Cloud的配置中心模块cloudConfig Center
POM
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
<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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.ming.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-config-center-3344</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.ming.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>YML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17server:
port: 3344
spring:
application:
name: cloud-config-center
cloud:
config:
server:
git:
uri: git@gitee.com:codeming2000/sprincloud-config.git
search-paths:
- springcloud-config
label: master
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka主启动类
1
2
3
4
5
6
7
public class MainAppConfigCenter3344 {
public static void main(String[] args) {
SpringApplication.run(MainAppConfigCenter3344.class, args);
}
}windows下修改hosts文件,增加映射
127.0.0.1 config-3344.com
测试通过Config微服务是否可以从Github上获取配置内容
配置读取规则
1) /{label}/{application}-{profile}.yml(最推荐使用这种方式)
master分支
http://config-3344.com:3344/master/config-dev.yml
http://config-3344.com:3344/master/config-test.yml
http://config-3344.com:3344/master/config-prod.yml
dev分支
http://config-3344.com:3344/dev/config-dev.yml
http://config-3344.com:3344/dev/config-test.yml
http://config-3344.com:3344/dev/config-prod.yml
2) /{application}-{profile}.yml
http://config-3344.com:3344/config-dev.yml
http://config-3344.com:3344/config-test.yml
http://config-3344.com:3344/config-prod.yml
http://config-3344.com:3344/config-xxxx.yml(不存在的配置)
3) /{application}-{profile}[/{label}]
http://config-3344.com:3344/config/dev/master
http://config-3344.com:3344/config/test/master
http://config-3344.com:3344/config/prod/master
重要配置细节总结
成功实现了用SpringCloud Config 通过GitHub获取配置信息
3.Config客户端配置与测试
新建cloud-config-client-3355
POM
bootstap.yml
是什么
内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16server:
port: 3355
spring:
application:
name: config-client
cloud:
config:
label: master
name: config
profile: dev
uri: http://localhost:3344
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka修改config-dev.yml配置并提交到GitHub中,比如加个变量age或者版本号version
主启动
1
2
3
4
5
6
7
public class ConfigClientMain3355 {
public static void main(String[] args) {
SpringApplication.run(ConfigClientMain3355.class, args);
}
}业务类
1
2
3
4
5
6
7
8
9
10
11
public class ConfigClientController {
"${config.info}") (
private String configInfo;
"/configInfo") (
public String getConfigInfo(){
return configInfo;
}
}测试
成功实现了客户端3355访问SpringCloud Config3344通过GitHub获取配置信息
问题随时而来,分布式配置的动态刷新
Linux运维修改GitHub上的配置文件内容做调整
刷新3344,发现ConfigServer配置中心立刻响应
刷新3355,发现ConfigServer客户端没有任何响应
3355没有变化除非自己重启或者重新加载
难道每次运维修改配置文件,客户端都需要重启??噩梦
4.Config客户端之动态刷新
避免每次更新配置都要重启客户端微服务3355
动态刷新
修改3355模块
POM引入actuator监控
修改YML,暴露监控端口
1
2
3
4
5management:
endpoints:
web:
exposure:
include: "*"@RefreshScope业务类Controller修改
此时修改github—> 3344 —> 3355
没有改变,(┬_┬)
需要运维人员发送Post请求刷新3355
必须是Post请求
curl -X POST “http://localhost:3355/actuator/refresh"
再测试
http://localhost:3355/configInfo OK
成功实现了客户端3355刷新到最新配置内容 避免了服务的重启
想想还有什么问题?
- 假如有多个微服务客户端3355/3366/3377。。。。
- 每个微服务都要执行一次post请求,手动刷新?
- 可否广播,一次通知,处处生效?
- 我们想大范围的自动刷新,求方法
十一、SpringCloud Bus 消息总线
1.概述
上一讲解的加深和扩充,一言以蔽之
- 分布式自动刷新配置功能
- Spring Cloud Bus配合Spring Cloud Config使用可以实现配置的动态刷新
是什么
Bus支持两种消息代理:RabbitMQ和Kafka
能干嘛
为何被称为总线
2.RabbitMQ环境配置
安装Erlang,下载地址:
安装RabbitMQ,下载地址
https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.14/rabbitmq-server-3.7.14.exe
进入RabbitMQ安装目录下的sbin目录
C:\Windows\System32\cmd.exe
输入以下命令启动管理功能
rabbitmq-plugins enable rabbitmq_management
访问地址查看是否安装成功
输入账号密码并登录: guest guest
3.SpringCloud Bus动态刷新全局广播
必须先具备良好的RabbitMQ环境先
演示广播效果,增加复杂度,再以3355为模板再制作一个3366
- 新建 cloud-config-client-3366
- POM
- YML
- 主启动
- controller
设计思想
利用消息总线触发一个客户端/bus/refresh,而刷新所有客户端的配置
利用消息总线触发一个服务端ConfigServer的/bus/refresh端点,而刷新所有客户端的配置(更加推荐)
图二的架构显然更加合适,图一不适合的原因如下
打破了微服务的职责单一性,因为微服务本身是业务模块,它本不应该承担配置刷新职责
破坏了微服务各节点的对等性
有一定的局限性。例如,微服务在迁移时,它的网络地址常常会发生变化,此时如果想要做到自动刷新,那就会增加更多的修改
给cloud-config-center-3344配置中心服务端添加消息总线支持
POM
1
2
3
4<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>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
30server:
port: 3344
spring:
application:
name: cloud-config-center
cloud:
config:
server:
git:
uri: git@gitee.com:codeming2000/sprincloud-config.git
search-paths:
- springcloud-config
label: master
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
management:
endpoints:
web:
exposure:
include: 'bus-refresh'
给cloud-config-center-3355客户端添加消息总线支持
POM
1
2
3
4<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>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
29server:
port: 3355
spring:
application:
name: config-client
cloud:
config:
label: master
name: config
profile: dev
uri: http://localhost:3344
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka
management:
endpoints:
web:
exposure:
include: "*"
给cloud-config-center-3366客户端添加消息总线支持
POM
1
2
3
4<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>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
29server:
port: 3366
spring:
application:
name: config-client
cloud:
config:
label: master
name: config
profile: dev
uri: http://localhost:3344
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka
management:
endpoints:
web:
exposure:
include: "*"
测试
运维工程师
修改Github上配置文件增加版本号
发送Post请求
curl -X POST "http://localhost:3344/actuator/bus-refresh"
配置中心
客户端
http://localhost:3355/configInfo
http://localhost:3366/configInfo
获取配置信息,发现都已经刷新了
一次修改,广播通知,处处生效
4.SpringCloud Bus动态刷新定点通知
不想全部通知,只想定点通知
- 只通知3355
- 不通知3366
简单一句话
- 指定具体某一个实例生效而不是全部
- 公式:
http://localhost:配置中心的端口号/actuator/bus-refresh/{destination}
- /bus/refresh请求不再发送到具体的服务实例上,而是发给config server并通过destination参数类指定需要更新配置的服务或实例
案例
我们这里以刷新运行在3355端口上的config-client为例
只通知3355
不通知3366
curl -X POST "http://localhost:3344/actuator/bus-refresh/config-client:3355"
通知总结
十二、SpringCloud Stream消息驱动
1.消息驱动概述
是什么
屏蔽底层消息中间件的差异,降低切换版本,统一消息的编程模型
官网
https://spring.io/projects/spring-cloud-stream#overview
https://cloud.spring.io/spring-cloud-static/spring-cloud-stream/3.0.1.RELEASE/reference/html/
Spring Cloud Stream中文指导手册
https://m.wang1314.com/doc/webapp/topic/20971999.html
设计思想
标准MQ
生产者/消费者之间靠消息媒介传递信息内容(Message)
消息必须走特定的通道(消息通道MessageChannel)
消息通道里的消息如何被消费呢,谁负责收发处理
消息通道MessageChannel的子接口SubscribableChannel,由MessageHandler消息处理器订阅
为什么用Cloud Stream
stream凭什么可以统一底层差异
Binder
INPUT对应于消费者
OUTPUT对应于生产者
Stream中的消息通信方式遵循了发布-订阅模式
Topic主题进行广播
在RabbitMQ就是Exchange
在kafka中就是Topic
Spring Cloud Stream标准流程套路
Binder
很方便的连接中间件,屏蔽差异
Channel
通道,是队列Queue的一种抽象,在消息通讯系统中就是实现存储和转发的媒介,通过对Channel对队列进行配置
Source和Sink
简单的可理解为参照对象是Spring Cloud Stream自身,从Stream发布消息就是输出,接受消息就是输入
编码API和常用注解
2.案例说明
RabbitMQ环境已经OK
工程中新建三个子模块
- cloud-stream-rabbitmq-provider8801,作为生产者进行发消息模块
- cloud-stream-rabbitmq-consumer8802,作为消息接收模块
- cloud-stream-rabbitmq-consumer8803,作为消息接收模块
3.消息驱动之生产者
新建Module cloud-stream-rabbitmq-provider8801
POM
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
<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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.ming.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-stream-rabbitmq-provider8801</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.ming.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>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
33server:
port: 8801
spring:
application:
name: cloud-stream-provider
cloud:
stream:
binders: # 在此处配置要绑定的rabbitmq的服务信息;
defaultRabbit: # 表示定义的名称,用于于binding整合
type: rabbit # 消息组件类型
environment: # 设置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
bindings: # 服务的整合处理
output: # 这个名字是一个通道的名称
destination: studyExchange # 表示要使用的Exchange名称定义
content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
binder: defaultRabbit # 设置要绑定的消息服务的具体设置
eureka:
client: # 客户端进行Eureka注册的配置
service-url:
defaultZone: http://localhost:7001/eureka
instance:
lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
instance-id: send-8801.com # 在信息列表时显示主机名称
prefer-ip-address: true # 访问的路径变为IP地址主启动类StreamMQMain8801
1
2
3
4
5
6
public class StreamMQMain8801 {
public static void main(String[] args) {
SpringApplication.run(StreamMQMain8801.class, args);
}
}业务类
发送消息接口
1
2
3
4
5package com.ming.springcloud.service;
public interface IMessageProvider {
public String send();
}发送消息接口实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24package com.ming.springcloud.service.impl;
import com.ming.springcloud.service.IMessageProvider;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.MessageChannel;
import javax.annotation.Resource;
import java.util.UUID;
.class) //定义消息的推送管道 (Source
public class MessageProviderImpl implements IMessageProvider {
private MessageChannel output; // 消息发送管道
public String send() {
String serial = UUID.randomUUID().toString();
output.send(MessageBuilder.withPayload(serial).build());
System.out.println("*****serial: " + serial);
return null;
}
}Controller
1
2
3
4
5
6
7
8
9
10
11
public class SendMessageController {
private IMessageProvider messageProvider;
"/sendMessage") (value =
public String sendMessage() {
return messageProvider.send();
}
}测试
启动7001eureka
启动rabbitmq
启动8801
访问
4.消息驱动之消费者
新建Module cloud-stream-rabbitmq-consumer8802
POM
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
<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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.ming.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-stream-rabbitmq-consumer8802</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.ming.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>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
33server:
port: 8802
spring:
application:
name: cloud-stream-consumer
cloud:
stream:
binders: # 在此处配置要绑定的rabbitmq的服务信息;
defaultRabbit: # 表示定义的名称,用于于binding整合
type: rabbit # 消息组件类型
environment: # 设置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
bindings: # 服务的整合处理
input: # 这个名字是一个通道的名称
destination: studyExchange # 表示要使用的Exchange名称定义
content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
binder: defaultRabbit # 设置要绑定的消息服务的具体设置
eureka:
client: # 客户端进行Eureka注册的配置
service-url:
defaultZone: http://localhost:7001/eureka
instance:
lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
instance-id: receive-8802.com # 在信息列表时显示主机名称
prefer-ip-address: true # 访问的路径变为IP地址主启动类StreamMQMain8802
1
2
3
4
5
6
public class StreamMQMain8802 {
public static void main(String[] args) {
SpringApplication.run(StreamMQMain8802.class, args);
}
}业务类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package com.ming.springcloud.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;
.class) (Sink
public class ReceiveMessageListenerController {
"${server.port}") (
private String serverPort;
(Sink.INPUT)
public void input(Message<String> message) {
System.out.println("消费者1号,接受:"+message.getPayload()+"\t port:"+serverPort);
}
}测试8801发送8802接收消息
5.分组消费与持久化
依照8802,clone出来一份运行8803
- cloud-stream-rabbitmq-consumer8803
- POM
- YML
- 主启动类
- 业务类
启动
- RabbitMQ
- 7001:服务注册
- 8801:消息生产
- 8802:消息消费
- 8803:消息消费
运行后两个问题
- 有重复消费问题
- 消息持久化问题
消费
目前是8802/8803同时都收到了,存在重复消费问题
http://localhost:8801/sendMessage
如何解决?
分组和持久化属性group
生产实际案例
分组
原理
微服务应用放置于同一个group中,就能够保证消息只会被其中一个应用消费一次。不同的组是可以消费的,同一个组内会发生竞争关系,只有其中一个可以消费。
8802/8803都变成不同组,group两个不同
group: mingA、mingB
8802修改YML
8803修改YML
我们自己配置
结论:还是重复消费
8802/8803实现了轮询分组,每次只有一个消费者 8801模块的发的消息只能被8802或8803其中一个接收到,这样避免了重复消费(都设置:group: atguiguA)
8802/8803都变成相同组,group两个相同
结论:同一个组的多个微服务实例,每次只会有一个拿到
持久化
解决了重复消费问题,再看看持久化
停止8802/8803并去除掉8802的分组group:atguiguA
8803的分组group:atguiguA没有去掉
8801先发送4条信息到rabbitmq
先启动8802,无分组属性配置,后台没有打出来消息
先启动8803,有分组属性配置,后台打出来了MQ上的消息
结论:有分组的消息不会丢失 没有分组的会丢失
十三、SpringCloud Sleuth分布式请求链路追踪
1.概述
为什么会出现这个技术?需要解决哪些问题?
是什么
https://github.com/spring-cloud/spring-cloud-sleuth
Spring Cloud Sleuth提供了一套完整的服务跟踪的解决方案
在分布式系统中提供追踪解决方案并且兼容支持了zipkin
解决
2.搭建链路监控步骤
1.zipkin
下载
SpringCloud从F版起已不需要自己构建Zipkin server了,只需要调用jar包即可
https://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/
zipkin-server-2.12.9.exec.jar
运行jar
运行控制台
术语
完整的调用链路
上图what
名词解释
Trace:类似于树结构的Span集合,表示一条调用链路,存在唯一标识
span:表示调用链路来源,通俗的理解span就是一次请求信息
2.服务提供者
cloud-provider-payment8001
POM
1
2
3
4
5<!--包含了sleuth+zipkin-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>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
34server:
port: 8001
spring:
application:
name: cloud-payment-service
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
#采样率 介于0-1之间
probability: 1
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db2019?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
username: root
password: 123456
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: com.ming.springcloud.entities
eureka:
client:
register-with-eureka: true
fetchRegistry: true
service-url:
# defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
instance:
instance-id: payment8001
prefer-ip-address: true业务类PaymentController
1
2
3
4"/payment/zipkin") (
public String paymentZipkin() {
return "hi ,i'am paymentzipkin server fall back,welcome to atguigu,O(∩_∩)O哈哈~";
}
3.服务消费者(调用方)
cloud-consumer-order80
POM
1
2
3
4
5<!--包含了sleuth+zipkin-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>YML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20server:
port: 8000
spring:
application:
name: cloud-order-service
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
probability: 1
eureka:
client:
register-with-eureka: true
fetchRegistry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka
# defaultZone: http://localhost:7001/eureka
# defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka业务类OrderController
1
2
3
4
5
6// ====================> zipkin+sleuth
"/consumer/payment/zipkin") (
public String paymentZipkin() {
String result = restTemplate.getForObject("http://localhost:8001"+"/payment/zipkin/", String.class);
return result;
}
4.依次启动eureka7001/8001/80
80调用8001几次测试下