springCloud 和 springCloud Alibaba 目前是最主流的微服务框架组合。
版本选择:
选用 springboot 和 springCloud 版本有约束,不按照它的约束会有冲突。
工程建造
父工程
pom.xml
<?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.atguigu.springcloud</groupId>
<artifactId>cloud2020</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>8081</module>
<module>cloud-comsumer-order80</module>
<module>cloud-api-commons</module>
</modules>
<!-- 统一管理jar包版本 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.complier.source>1.8</maven.complier.source>
<maven.complier.target>1.8</maven.complier.target>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.16.18</lombok.version>
<mysql.version>5.1.47</mysql.version>
<druid.version>1.1.16</druid.version>
<mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
</properties>
<!-- dependencyManagement 子块继承之后,子module不用写 groupId 和 version -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</dependency>
<!-- 下面三个基本是微服务架构的标配 -->
<!--spring boot 2.2.2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<type>pom</type>
<!--<scope>import</scope>,import只能用在dependencyManagement块中,它将spring-boot-dependencies
中dependencyManagement下的dependencies插入到当前工程的dependencyManagement中,所以不存在依赖传递。-->
<scope>import</scope>
</dependency>
<!--spring cloud Hoxton.SR1-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud 阿里巴巴-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
<scope>runtime</scope>
</dependency>
<!-- druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.boot.version}</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<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>
</project>
调配环境:
1、java版本
2、注解生效激活

3、编码

第一个微服务架构
- 建模块 module
- 改 pom
- 写yml
- 主启动
- 业务类
提供者
这里面的 lombok 这个包,引入以后,实体类不用再写set 和 get
可以如下写实体类:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {
private Integer id;
private String serial;
}
cloud-provider-payment8001 子工程的pom文件:
<?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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.atguigu.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-provider-payment8001</artifactId>
<dependencies>
<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.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<!--不写就跟主父配置版本一样-->
<version>1.1.16</version>
</dependency>
<!--mysql-connector-java-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</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
server:
port: 8001
#服务名
spring:
application:
name: cloud-payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource #当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver #mysql驱动包
url: jdbc:mysql://localhost:3306/jsp?serverTimezone=GMT&useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 6723237zh
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.atguigu.springcloud.entities # mybatis引用的实体类的共同前缀(所有Entity 别名类所在包)
主启动类
@SpringBootApplication
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class,args);
}
}
热部署配置
- 在具体子模块里添加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
- 添加plus到父工程的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>
- 把这4个打勾

- shift + ctrl + alt + / 四个按键一块按,选择1.Registry项:

- 需要重启才能有效
消费者
-
消费者现在只模拟调用提供者的Controller方法,没有持久层配置,只有Controller和实体类
当然也要配置主启动类和启动端口 80 -
pom.xml
<?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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.atguigu.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-comsumer-order80</artifactId>
<dependencies>
<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.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<!--不写就跟主父配置版本一样-->
<version>1.1.16</version>
</dependency>
<!--mysql-connector-java-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</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>
-
然后把CommonResult 和 Payment 两个 实体类也复制过来
-
两个子模块之间,消费者调用提供者的服务需要通过 RestTemplate ,如下:
ApplicationContextConfig 内容:
package com.dkf.springcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ApplicationContextConfig {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
- 第4之后注入contrroller类才能跨子模块调用
@RestController
@Slf4j
public class OrderController {
//远程调用的 地址
public static final String PAYMENT_URL = "http://localhost:8001";
@Resource
private RestTemplate restTemplate;
//消费者这里都用GetMapping 不用post
@GetMapping("/comsumer/payment/create")
public CommonResult<Payment> create(Payment payment){
//提交用postForObject
//param1 请求地址,param2 请求参数, param3 返回类型
return restTemplate.postForObject(PAYMENT_URL+"/payment/create",payment,CommonResult.class);
}
@GetMapping("/comsumer/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") Long id){
//获取用getForObject
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
}
}
工程重构
上面 两个子项目,有多次重复的 导入 jar,和重复的 Entity 实体类。可以把 多余的部分,加入到一个独立的模块中,将这个模块打包,并提供给需要使用的 module
- 新建一个 cloud-api-commons 子模块
- 将 entities 包里面的实体类复制到这个子模块中,也将 pom 文件中,重复导入的 jar包放到这个新建的 模块的 pom 文件中。如下:
<?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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.atguigu.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>
<!--
关于这个hutool 是个功能强大的工具包,官网:https://hutool.cn/
-->
</dependencies>
</project>
-
将此子模块clear,然后install, 自动就发布到maven仓库。
-
将 提供者 和 消费者 两个项目中的 entities 包删除。
-
将 打包到 maven 仓库的 cloud-api-commons 模块,引入到 提供者 和 消费者的 pom 文件中,如下所示
<dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
服务注册中心
如果是上面只有两个微服务,通过 RestTemplate ,是可以相互调用的,但是当微服务项目的数量增大,就需要服务注册中心。目前没有学习服务调用相关技术,使用 SpringCloud 自带的 RestTemplate 来实现RPC
Eureka
官方停更不停用,以后可能用的越来越少。
概念和理论




Server模块
server 模块使用 7001端口,下面是pom文件需要的依赖:
<dependencies>
<dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</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.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
yml
server:
port: 7001
eureka:
instance:
hostname: localhost # eureka 服务器的实例名称
client:
# false 代表不向服务注册中心注册自己,因为它本身就是服务中心
register-with-eureka: false
# false 代表自己就是服务注册中心,自己的作用就是维护服务实例,并不需要去检索服务
fetch-registry: false
# 设置与 Eureka Server 交互的地址,查询服务 和 注册服务都依赖这个地址
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
启动类
//(exclude = DataSourceAutoConfiguration.class)启动时不启用 DataSource的自动配置检查
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
//服务注册中心
@EnableEurekaServer
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class,args);
}
}
提供者
这里的提供者,还是使用 上面的 cloud-provider-payment8001 模块,做如下修改:
- 在 pom 文件的基础上引入 eureka 的client包,pom 的全部依赖如下所示:
<dependencies>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.atguigu.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.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<!--不写就跟主父配置版本一样-->
<version>1.1.16</version>
</dependency>
<!--mysql-connector-java-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</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>
- 主启动类 加上注解 : @EnableEurekaClient
- yml 文件添加关于 Eureka 的配置:
eureka:
client:
# true 代表向服务注册中心注册自己
register-with-eureka: true
# 是否从EurekaServer抓取已有的注册信息,默认true,单节点无所谓,集群必须true并不需要去检索服务
fetch-registry: true
# 设置与 Eureka Server 交互的地址,查询服务 和 注册服务都依赖这个地址
service-url:
defaultZone: http://localhost:7001/eureka/
消费者
这里的消费者 也是上面 的 cloud-customer-order80 模块
- 修改 pom 文件,加入Eureka 的有关依赖, 全部 pom 依赖如下:
<dependencies>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.atguigu.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.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<!--不写就跟主父配置版本一样-->
<version>1.1.16</version>
</dependency>
<!--mysql-connector-java-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</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>
- 主启动类 加上注解 : @EnableEurekaClient
- yml 文件必须添加的内容:
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka/
spring:
application:
name: cloud-order-service
Eureka集群

Eureka 集群的原理,就是 相互注册,互相守望
进入windows下的路径 C:\Windows\System32\drivers\etc
修改hosts文件如下
# localhost name resolution is handled within DNS itself.
127.0.0.1 localhost
::1 localhost
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
修改没有权限的话,鼠标右键hosts文件->属性->安全,再如下图操作

现在创建 cloud-eureka-server7002 ,也就是第二个 Eureka 服务注册中心,pom 文件和 主启动类,与第一个Server一致。
现在修改这两个 Server 的 yml 配置:
7001 端口的Server yml文件:
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com # eureka 服务器的实例地址
client:
register-with-eureka: false
fetch-registry: false
service-url:
## 一定要注意这里的地址,这是搭建集群的关键
# 把7001注册进7002 ,相互注册,相互守望
defaultZone: http://eureka7002.com:7002/eureka/
7002 端口的Server yml文件:
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com # eureka 服务器的实例地址
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
eureka.instance.hostname 才是启动以后 本 Server 的注册地址,而 service-url 是 map 类型,只要保证 key:value 格式就行,它代表 本Server 指向了那些 其它Server 。利用这个,就可以实现Eureka Server 相互之间的注册,从而实现集群的搭建。
提供者集群
为提供者,即 cloud-provider-payment8001 模块创建集群,新建模块为 cloud-provider-payment8002,项目结构一致,pom文件依赖一样
8002 yml
server:
port: 8002
#服务名和8001一样
spring:
application:
name: cloud-payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource #当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver #mysql驱动包
url: jdbc:mysql://localhost:3306/jsp?serverTimezone=GMT&useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 1234567
eureka:
client:
# true 代表向服务注册中心注册自己
register-with-eureka: true
# 是否从EurekaServer抓取已有的注册信息,默认true,单节点无所谓,集群必须true并不需要去检索服务
fetch-registry: true
# 设置与 Eureka Server 交互的地址,查询服务 和 注册服务都依赖这个地址
service-url:
#defaultZone: http://localhost:7001/eureka/
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集群版
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.atguigu.springcloud.entities # mybatis引用的实体类的共同前缀(所有Entity 别名类所在包)
8001,8002控制类加serverPort 提示访问到的具体端口
@RestController
//@Slf4j 日志输出log.info()
@Slf4j
public class PaymentController {
@Autowired
private PaymentService paymentService;
//获取配置文件的端口号
@Value("${server.port}")
private String serverPort;
@PostMapping("/payment/create")
//如果不用@RequestBody,跨模块传入的数据会无效,插入数据库虽返回成功,但内容是null
public CommonResult create(@RequestBody Payment payment){
int result = paymentService.create(payment);
log.info("****插入结果:"+ result);
if (result>0){
return new CommonResult(200,"插入成功,serverPort:"+serverPort,result);
}else {
return new CommonResult(444,"插入失败,serverPort:"+serverPort,null);
}
}
/**
* @PathVariable 可以将URL中占位符参数{xxx}绑定到处理器类的方法形参中@PathVariable(“xxx“)
* 不加@PathVariable("id") 前端传入的数值将接收不到,id默认为null
* 且不可用@Param 替代
*/
@GetMapping("/payment/get/{id}")
public CommonResult getPaymentById(@PathVariable("id") Long id){
Payment payment = paymentService.getPaymentById(id);
log.info("****查询结果:"+ payment);
if (payment != null){
return new CommonResult(200,"查询成功,serverPort:"+serverPort,payment);
}else {
return new CommonResult(444,"没有对应记录,serverPort:"+serverPort,null);
}
}
}
修改消费者80 的配置类如下
@Configuration
public class ApplicationContextConfig {
@Bean
//底层是HttpClient 远程调用
//负载均衡机制
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
修改消费者80 的控制类
//public static final String PAYMENT_URL = "http://localhost:8001";//多个服务不能写死
//写Eureka下的服务名,配置负载均衡机制后 它会自动分配给8001或8002
public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
然后运行测试。
actuator信息配置
修改 在Eureka 注册中心显示的 主机名:
显示微服务所在 的主机地址:

服务发现Discovery
对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息
- 例子,在8001 主启动类上添加注解:@EnableDiscoveryClient
- 在 Controller 里面打印信息:
//通过注册发现 暴露给对方一些自己的服务信息
@Resource
private DiscoveryClient discoveryClient;
@GetMapping(value = "/payment/discovery")
public Object discovery(){
List<String> services = discoveryClient.getServices();//服务列表 比如CLOUD-PAYMENT-SERVICE 等
for (String element : services) {
log.info("***element:"+element);
}
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");//某个服务的实例列表 比如8001等
for (ServiceInstance instance : instances) {
log.info(instance.getServiceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
}
return this.discoveryClient;
}
访问后查看控制台

Eureka 自我保护机制
故障现象:

为什么有自我保护机制:




禁止自我保护:
在 Eureka Server 7001的模块中的 yml 文件 eureka下 进行配置:

修改 Eureka Client 模块的 心跳间隔时间:

Zookeeper
临时性的服务节点,约定的心跳时间内无反应直接删除
springCloud 整合 zookeeper

提供者
创建一个提供者,和之前的一样即可,使用 8004端口
pom文件如下:
<artifactId>cloud-provider-payment8004</artifactId>
<dependencies>
<!--springcloud 整合 zookeeper 组件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</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.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!--mysql-connector-java-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</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>
<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.dkf.cloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
主启动类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain8004 {
public static void main(String[] args){
SpringApplication.run(PaymentMain8004.class, args);
}
}
Controller 打印信息:
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;
@Value("${server.port}")
private String serverPort;
@RequestMapping("/payment/zk")
public String paymentzk(){
return "springcloud with zookeeper :" + serverPort + "\t" + UUID.randomUUID().toString();
}
}
如果 zookeeper 的版本和导入的jar包版本不一致,启动就会报错,由jar包冲突的问题。
解决这种冲突,需要在 pom 文件中,排除掉引起冲突的jar包,添加和服务器zookeeper版本一致的 jar 包,
但是新导入的 zookeeper jar包 又有 slf4j 冲突问题,于是再次排除引起冲突的jar包
<!--springcloud 整合 zookeeper 组件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<!-- 排除与zookeeper版本不一致到导致 冲突的 jar包 -->
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 添加对应版本的jar包 -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
<!-- 排除和 slf4j 冲突的 jar包 -->
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
yml文件:
server:
port: 8004
spring:
application:
name: cloud-provider-service
cloud:
zookeeper:
connect-string:192.168.50.03:2181
消费者
创建测试zookeeper作为服务注册中心的 消费者 模块 cloud-customerzk-order80
主启动类、pom文件、yml文件和 提供者 的类似
config类,注入 RestTemplate
@SpringBootConfiguration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getTemplate(){
return new RestTemplate();
}
}
controller层也是和之前类似:
@RestController
@Slf4j
public class CustomerZkController {
public static final String INVOKE_URL="http://cloud-provider-service";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/customer/payment/zk")
public String paymentInfo(){
String result = restTemplate.getForObject(INVOKE_URL + "/payment/zk",String.class);
return result;
}
}
关于 zookeeper 的集群搭建,目前使用较少,而且在 yml 文件中的配置也是类似,以列表形式写入 zookeeper 的多个地址即可,而且zookeeper 集群,在 hadoop的笔记中也有记录。总而言之,只要配合zookeeper集群,以及yml文件的配置就能完成集群搭建
Consul
consul也是服务注册中心的一个实现,是由go语言写的。官网地址: https://www.consul.io/intro
中文地址: https://www.springcloud.cc/spring-cloud-consul.html
功能:
安装并运行
下载地址:https://www.consul.io/downloads.html
打开下载的压缩包,只有一个exe文件,实际上是不用安装的,在exe文件所在目录打开dos窗口使用即可。
使用开发模式启动:consul agent -dev
访问8500端口,即可访问首页
提供者
新建提供者模块:cloud-providerconsul-service8006
pom 文件:
<artifactId>cloud-providerconsul-service8006</artifactId>
<dependencies>
<!--springcloud consul server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- springboot整合Web组件 -->
<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>
<!-- 日常通用jar包 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</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>
<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.dkf.cloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
yml 文件:
server:
port: 8006
spring:
application:
name: consul-provider-service
cloud:
consul:
host: localhost
port: 8500
discovery: # 指定注册对外暴露的服务名称
service-name: ${spring.application.name}
主启动类:
@SpringBootApplication
@EnableDiscoveryClient
public class ConsulProviderMain8006 {
public static void main(String[] args) {
SpringApplication.run(ConsulProviderMain8006.class,args);
}
}
controller也是简单的写一下就行。
消费者
新建 一个 在80端口的 消费者模块。pom和yml和提供者的类似,主启动类不用说,记得注入RestTemplate
controller层:
@RestController
public class CustomerConsulController {
public static final String INVOKE_URL="http://consul-provider-service";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/customer/payment/consul")
public String paymentInfo(){
String result = restTemplate.getForObject(INVOKE_URL + "/payment/consul",String.class);
return result;
}
}
如果提示错,可以把RestTemplate写在启动类上
springcloud 出现A component required a bean of type ‘org.springframework.web.client.RestTemplate’ that could not be found.

对比总结





服务调用
都是使用在 client端,即有 ”消费者“ 需求的模块中。
Ribbon
我们这里提前启动好之前在搭建的 eureka Server 集群(5个模块)
简介




上面在eureka时,确实实现了负载均衡机制,那是因为 eureka-client包里面自带着ribbon:
一句话,Ribbon 就是 负载均衡 + RestTemplate 调用。实际上不止eureka的jar包有,zookeeper的jar包,还有consul的jar包都包含了他,就是上面使用的服务调用。
如果自己添加,在 模块的 pom 文件中引入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
对于RestTemplate 的一些说明:
有两种请求方式:post和get ,还有两种返回类型:object 和 Entity
如果使用 ForObject 得到的就是提供者返回的对象,而如果要使用 ForEntity 得到时 ResponstEntity对象,使用getBody()才能得到提供者返回的数据。
//使用forEnriry示例:
@GetMapping("/comsumer/payment/getForEntity/{id}")
public CommonResult<Payment> getForEntity(@PathVariable("id") Long id){
//获取用getForObject
ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
if (entity.getStatusCode().is2xxSuccessful()){
log.info(entity.getStatusCode()+"\t"+entity.getHeaders());
return entity.getBody();
}else{
return new CommonResult<Payment>(444,"失败");
}
负载均衡
Ribbon 自带7种负载均衡规则类型:
7种规则的 类和接口关系:


注意上面说的,而Springboot主启动类上的 @SpringBootApplication 注解,包含了@ComponentScan注解,会自动扫描当前包及子包,所以注意不要放在SpringBoot主启动类的包内。
创建包:

在myrule这个包下新建 MySelfRule类:
@Configuration
public class MySelfRule {
@Bean
public IRule myrule(){
return new RandomRule(); //负载均衡规则定义为随机
}
}
然后在主启动类上添加如下注解 @RibbonClient:
import com.dkf.myrule.MySelfRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
//指定该负载均衡规则对哪个提供者服务使用 加载自定义规则的配置类
@RibbonClient(name="CLOUD-PROVIDER-SERVICE", configuration = MySelfRule.class)
public class OrderMain80 {
public static void main(String[] args){
SpringApplication.run(OrderMain80.class, args);
}
}
轮询算法原理

OpenFeign
概述



使用
新建一个消费者募模块。feign自带负载均衡配置,所以不用手动配置
<!-- Open Feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
主启动类:
@SpringBootApplication
@EnableFeignClients //关键注解
public class CustomerFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(CustomerFeignMain80.class, args);
}
}
新建一个service
@Component
@FeignClient(value = "CLOUD-PROVIDER-SERVICE") //服务名称,要和eureka上面的一致才行
public interface PaymentFeignService {
//这个就是provider 的controller层的方法定义。
@GetMapping(value = "/payment/{id}")
public CommonResult getPaymentById(@PathVariable("id")Long id);
}
Controller层:
//使用起来就相当于是普通的service。
@RestController
public class CustomerFeignController {
@Resource
private PaymentFeignService paymentFeignService;
@GetMapping("customer/feign/payment/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
return paymentFeignService.getPaymentById(id);
}
}
Openfeign默认超时等待为一秒,在消费者里面配置超时时间
接口里的方法和路径与 8001等服务的controller层一致
超时控制

开启日志打印
首先写一个config配置类:

然后在yml文件中开启日志打印配置

中级部分
主要是服务降级、服务熔断、服务限流的开发思想和框架实现
Hystrix 断路器
官方地址:https://github.com/Netflix/Hystrix/wiki/How-To-Use
概述


服务降级:
服务器忙碌或者网络拥堵时,不让客户端等待并立刻返回一个友好提示,fallback
服务熔断:
服务限流:
上面的技术不论是消费者还是提供者,根据真实环境都是可以加入配置的。
案例
首先构建一个eureka作为服务中心的单机版微服务架构 ,这里使用之前eureka Server 7001模块,作为服务中心
新建 提供者 cloud-provider-hystrix-payment8001 模块:
pom 文件:
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
Main:
@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
Service:
@Service
public class PaymentService {
//正常访问
public String paymentInfo_OK(Integer id){
return "线程池: "+ Thread.currentThread().getName()+"paymentInfo_OK,id "+id;
}
public String paymentInfo_TimeOut(Integer id){
//int n=10/0;//
int time = 3;
try {
TimeUnit.SECONDS.sleep(time);//睡3秒
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池: "+ Thread.currentThread().getName()+"paymentInfo_TimeOut,id "+id+" 耗时(秒)"+time;
}
controller层:
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;
@Value("${server.port}")
private String serverPort;
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
String result = paymentService.paymentInfo_OK(id);
log.info(" *** result: "+result);
return result;
}
@GetMapping("/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 压力测试器
下载安装操作博客:https://editor.csdn.net/md/?articleId=114655397



从测试可以看出,当模拟的超长请求被高并发以后,访问普通的小请求速率也会被拉低。
新建消费者 cloud-customer-feign-hystrix-order80 模块:以feign为服务调用,eureka为服务中心的模。
测试可见,当启动高并发测试时,消费者访问也会变得很慢,甚至出现超时报错。
解决思路:
服务降级
一般服务降级放在客户端,即 消费者端 ,但是提供者端一样能使用。
首先提供者,即8001 先从自身找问题,设置自身调用超时的峰值,峰值内正常运行,超出峰值需要有兜底的方法处理,作服务降级fallback
首先 对 8001 的service进行配置(对容易超时的方法进行配置) :
//====服务降级
//非正常访问
//value = "3000" 规定3秒内走方法 String paymentInfo_TimeOut()
//访问出错或 时间超3秒则执行 paymentInfo_TimeOutHandler()
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000")
})
public String paymentInfo_TimeOut(Integer id){
//int n=10/0;//
int time = 3;
try {
TimeUnit.SECONDS.sleep(time);//睡3秒
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池: "+ Thread.currentThread().getName()+"paymentInfo_TimeOut,id "+id+" 耗时(秒)"+time;
}
public String paymentInfo_TimeOutHandler(Integer id){
return "线程池: "+ Thread.currentThread().getName()+"失败,id "+id+" 哭了";
}
主启动类添加注解: @EnableCircuitBreaker
然后对 80 进行服务降级:很明显 service 层是接口,所以我们对消费者,在它的 controller 层进行降级
@HystrixCommand(fallbackMethod = "paymentInfo_timeoutHandler", commandProperties = {
//设置峰值,超过 3 秒,就会调用兜底方法
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value = "3000")
})
@GetMapping("/customer/payment/hystrix/timeout/{id}")
public String paymentInfo_Timeout(@PathVariable("id")Integer id){
log.info("paymentInfo_timeout");
return orderService.paymentInfo_Timeout(id);
}
//兜底方法,注意,兜底方法参数随意
public String paymentInfo_timeoutHandler(@PathVariable("id")Integer id){
log.info("paymentInfo_timeout--handler");
return "访问 payment 失败----人工报错";
}
主启动类添加注解: @EnableCircuitBreaker
完成测试! 注意,消费者降级设置的超时时间和提供者的没有任何关系,就算提供者峰值是 5 秒,而消费者峰值是 3秒,那么消费者依然报错。就是每个模块在服务降级上,都是独立的。
全局服务降级
上面的降级策略,很明显造成了代码的杂乱,提升了耦合度,而且按照这样,每个方法都需要配置一个兜底方法,很繁琐。现在将降级处理方法(兜底方法)做一个全局的配置,设置共有的兜底方法和独享的兜底方法。
问题-每个方法配置一个,解决:
@RestController
@Slf4j
//全局配置降级方法的注解
@DefaultProperties(defaultFallback = "paymentInfo_timeoutHandler")
public class OrderController {
.....
// 不写自己的 fallbackMethod 属性,就使用全局默认的
@HystrixCommand(commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value = "3000")
})
@GetMapping("/customer/payment/hystrix/timeout/{id}")
public String paymentInfo_Timeout(@PathVariable("id")Integer id){
......
}
//兜底方法
public String paymentInfo_timeoutHandler(){
log.info("paymentInfo_timeout--handler");
return "访问 payment 失败----人工报错";
}
}
问题-跟业务逻辑混合,解决(解耦):
在这种方式一般是在客户端,即消费者端,首先上面再controller中添加的 @HystrixCommand 和 @DefaultProperties 两个注解去掉。就是保持原来的controller
- yml文件配置
server:
port: 80
spring:
application:
name: cloud-customer-feign-hystrix-service
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
# 用于服务降级 在注解@FeignClient 中添加 fallback 属性值
feign:
hystrix:
enabled: true # 在feign中开启 hystrix
- 修改service 接口:
@Component // 这里是重点
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT", fallback = OrderFallbackService.class)
public interface OrderService {
@GetMapping("/payment/hystrix/{id}")
public String paymentInfo_OK(@PathVariable("id")Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_Timeout(@PathVariable("id")Integer id);
}
- fallback 指向的类:
package com.dkf.springcloud.service;
import org.springframework.stereotype.Component;
@Component //注意这里,它实现了service接口
public class OrderFallbackService implements OrderService{
@Override
public String paymentInfo_OK(Integer id) {
return "OrderFallbackService --发生异常";
}
@Override
public String paymentInfo_Timeout(Integer id) {
return "OrderFallbackService --发生异常--paymentInfo_Timeout";
}
}
服务熔断
实际上服务熔断 和 服务降级 没有任何关系
服务熔断,有自我恢复的功能


配置参数的来源:

以 8001 项目为示例:
service层的方法设置服务熔断:
//==== 服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),//是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "9"),//请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),//时间10秒
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60")//在10秒内请求9次失败率达到60% 跳闸
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
if (id<0){
throw new RuntimeException("**** id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();//用了hutool工具包
return Thread.currentThread().getName()+"调用成功, 流水号 : " +serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
return "服务降级兜底函数 id不能负数 id: "+id;
}
controller:
//==== 服务熔断
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
String result = paymentService.paymentCircuitBreaker(id);
log.info("****** result: " +result);
return result;
}
关于解耦以后的全局配置说明:
例如上面提到的全局服务降级,并且是feign+hystrix整合,即 service 实现类的方式,如何做全局配置?
上面有 做全局配置时,设置超时时间的方式,我们可以从中获得灵感,即在yml文件中 进行熔断配置:
hystrix: command: default: circuitBreaker: enabled: true requestVolumeThreshold: 10 sleepWindowInMilliseconds: 10000 errorThresholdPercentage: 60
Hystrix DashBoard
新建模块 cloud-hystrix-dashboard9001 :
pom 文件:
<dependencies>
<!-- hystrix Dashboard-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<!-- 常规 jar 包 -->
<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>
yml文件只需要配置端口号,主启动类加上这样注解:@EnableHystrixDashboard
启动测试:访问 http://ocalhost:9001/hystrix
监控实战
下面使用上面 9001 Hystrix Dashboard 项目,来监控 8001 项目 Hystrix 的实时情况:
修改Main8001
SpringBootApplication
@EnableEurekaClient
//激活Hystrix使用
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
/**
* 此配置是为了服务监控而配置,与服务容错无关,springvloud升级后的坑
* ServletRegistrationBean 因为springboot的默认路径不是 "/hystrix.stream"
* 只要在自己的项目里配置下面的servlet就可以了
*/
@Bean
public ServletRegistrationBean getServlet(){
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(streamServlet);
servletRegistrationBean.setLoadOnStartup(1);
servletRegistrationBean.addUrlMappings("/hystrix.stream");
servletRegistrationBean.setName("HystrixMetricsStreamServlet");
return servletRegistrationBean;
}
}

# 服务网关
Gateway
内容过多,开发可参考 https://docs.spring.io/ 官网文档
简介



微服务架构图





Gateway 基于WebFlux
Gateway 三大核心概念:

总结:
### 入门配置
新建模块 cloud-gateway-gateway9527
现在实现,通过Gateway (网关) 来访问其它项目,这里选择之前8001项目,要求注册进Eureka Server 。其它没要求。
pom文件:
<dependencies>
<!--gateway-->
<!-- gateway和web不能同时存在,即web相关jar包不能导入 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--eureka-client gateWay作为网关,也要注册进服务中心-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- gateway和web不能同时存在,即web相关jar包不能导入 -->
<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>
<dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
yml文件:
server:
port: 9527
spring:
application:
name: cloud-gateway
## GateWay配置
cloud:
gateway:
routes:
- id: payment_routh # 路由ID , 没有固定的规则但要求唯一,建议配合服务名
uri: http://localhost:8001 # 匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_routh2 # 路由ID , 没有固定的规则但要求唯一,建议配合服务名
uri: http://localhost:8001 # 匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
# 注册进 eureka Server
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
register-with-eureka: true
fetch-registry: true
主启动类
@SpringBootApplication
@EnableEurekaClient
public class GatewayMain9527 {
public static void main(String[] args) {
SpringApplication.run(GatewayMain9527.class,args);
}
}
访问测试:1 启动eureka Server,2 启动 8001 项目,3 启动9527(Gateway项目)
可见,当我们访问 http://localhost:9527/payment/get/1 时,即访问网关地址时,会给我们转发到 8001 项目的请求地址,以此作出响应。
加入网关前:http://localhost:8001/payment/get/31
加入网关后:http://localhost:9527/payment/get/31

上面是以 yml 文件配置的路由,也有使用config类配置的方式:
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator (RouteLocatorBuilder routeLocatorBuilder){
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
//访问localhost:9527/guonei,会转发到 http://news.baidu.com/guonei
routes.route("path-route",
r -> r.path("/guonei")
.uri("http://news.baidu.com/guonei")).build();
return routes.build();
}
}
动态配置
这里所谓的动态配置就是利用服务注册中心,来实现 负载均衡 的调用 多个微服务。
注意,这是GateWay 的负载均衡
对yml进行配置:
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh # 路由ID , 没有固定的规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 # 匹配后提供服务的路由地址
uri: lb://CLOUD-PROVIDER-SERVICE
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_routh2 # 路由ID , 没有固定的规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 # 匹配后提供服务的路由地址
uri: lb://CLOUD-PROVIDER-SERVICE
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
# uri: lb://CLOUD-PROVIDER-SERVICE 解释:lb 属于GateWay 的关键字,代表是动态uri,即代表使用的是服务注册中心的微服务名,它默认开启使用负载均衡机制
下面可以开启 8002 模块,并将它与8001同微服务名,注册到 Eureka Server 进行测试。
Predicate
注意到上面yml配置中,有个predicates 属性值。





Filter
主要是配置全局自定义过滤器
自定义全局过滤器配置类:
@Component
public class GateWayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("******* come in MyLogGatewayFilter: " + new Date());
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
if (uname == null){
log.info("****** 非法用户");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
//返回值是加载顺序,一般全局的都是第一位加载
@Override
public int getOrder() {
return 0;
}
}
服务配置
概述




### 服务端配置
首先在github上新建一个仓库 springcloud-config
然后使用git命令克隆到本地,命令:git clone https://github.com/LZXYF/springcloud-config
注意上面的操作不是必须的,只要github上有就可以,克隆到本地只是修改文件。
新建 cloud-config-center3344 模块:
pom文件:
<dependencies>
<!-- config Server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<!--eureka-client config Server也要注册进服务中心-->
<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>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
</dependencies>
yml 配置:
server:
port: 3344
spring:
application:
name: cloud-config-center # 注册进eureka Server 的微服务名
cloud:
config:
server:
git:
uri: https://github.com/LZXYF/springcloud-config # github 仓库位置
## 搜索目录
search-paths:
- springcloud-config
# 读取的分支
label: master
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
主启动类:
@SpringBootApplication
@EnableConfigServer //关键注解
public class ConfigCenterMain3344 {
public static void main(String[] args) {
SpringApplication.run(ConfigCenterMain3344.class,args);
}
}
添加模拟映射:【C:\Windows\System32\drivers\etc\hosts】文件中添加: 127.0.0.1 config-3344.com
启动微服务3344,访问http://config-3344.com:3344/master/config-dev.yml 文件(注意,要提前在git上弄一个这文件)
文件命名和访问的规则:

客户端配置
这里的客户端指的是,使用 Config Server 统一配置文件的项目。既有之前说的消费者,又有提供者
新建 cloud-config-client-3355 模块:
pom文件:
<dependencies>
<!-- config Client 和 服务端的依赖不一样 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!--eureka-client config Server也要注册进服务中心-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</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>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
</dependencies>
bootstrap.xml


内容:
server:
port: 3355
spring:
application:
name: config-client
cloud:
# config 客户端配置
config:
label: master # 分支名称
name: config # 配置文件名称,文件也可以是client-config-dev.yml这种格式的,这里就写 client-config
profile: dev # 使用配置环境
uri: http://config-3344.com:3344 # config Server 地址
# 综合上面四个 即读取配置文件地址为: http://config-3344.com:3344/master/config-dev.yml
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
主启动:
@SpringBootApplication
@EnableEurekaClient
public class ConfigClientMain3355
public static void main(String[] args) {
SpringApplication.run(ConfigClientMain3355.class, args);
}
}
controller层,测试读取配置信息
package com.dkf.springcloud.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ConfigClientController {
@Value("${config.info}")
private String configInfo;
@GetMapping("/configInfo")
public String getConfigInfo(){
return configInfo;
}
}
启动测试完成!如果报错,注意github上的 yml 格式有没有写错!
动态刷新
问题:

github上面配置更新了,config Server 项目上是动态更新的,但是,client端的项目中的配置,目前还是之前的,它不能动态更新,必须重启才行。
解决:
-
client端一定要有如下依赖:

-
client 端增加 yml 配置如下,即在 bootstrap.yml 文件中:
# 暴露监控端点
management:
endpoints:
web:
exposure:
include: "*"
- 在controller 上添加如下注解:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9ediUUnJ-1615816878116)(images\1597650236798.png)]](https://i-blog.csdnimg.cn/blog_migrate/605d61a786594923b3deef9f8e0f3c3e.png)
到此为止,配置已经完成,但是测试仍然不能动态刷新,需要下一步。
- 一般运维工程师 向 client 端发送一个 POST 请求
如 curl -X POST “http://localhost:3355/actuator/refresh”
两个必须:1.必须是 POST 请求,2.请求地址:http://localhost:3355/actuator/refresh
成功!
但是又有一个问题,就是要向每个微服务发送一次POST请求,当微服务数量庞大,又是一个新的问题。
就有下面的消息总线!
消息总线
Bus

安装RabbitMQ
在windows 上安装RabbitMQ
- 安装RabbitMQ的依赖环境 Erlang 下载地址: http://erlang.org/download/otp_win64_21.3.exe
- 安装RabbitMQ 下载地址: http://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.14/rabbitmq-server-3.7.14.exe
- 进入 rabbitMQ安装目录的sbin目录下,打开cmd窗口,执行 【rabbitmq-plugins enable rabbitmq_management】
- 访问【http://localhost:15672/】,输入密码和账号:默认都为 guest
广播式刷新配置
还是按照之前的 3344(config Server)和 3355(config client)两个项目来增进。
首先给 config Server 和 config client 都添加如下依赖:
<!-- 添加rabbitMQ的消息总线支持包 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
config Server 的yml文件增加如下配置:
# rabbitMq的相关配置
rabbitmq:
host: localhost
port: 5672 # 这里没错,虽然rabbitMQ网页是 15672
username: guest
password: guest
# rabbitmq 的相关配置2 暴露bus刷新配置的端点
management:
endpoints:
web:
exposure:
include: 'bus-refresh'
config Client 的yml文件修改成如下配置:(注意对齐方式,和config Server不一样)
spring:
application:
name: config-client
cloud:
# config 客户端配置
config:
label: master # 分支名称
name: client-config # 配置文件名称
profile: test # 使用配置环境
uri: http://config-3344.com:3344 # config Server 地址
# 综合上面四个 即读取配置文件地址为: http://config-3344.com:3344/master/config-dev.yml
# rabbitMq的相关配置
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
可在github上修改yml文件进行测试,修改完文件,向 config server 发送 请求:
【curl -X POST “http://localhost:3344/actuator/bus-refresh”】
注意,之前是向config client 一个个发送请求,但是这次是向 config Server 发送请求,而所有的config client 的配置也都全部更新。
定点通知

持续更新中。。。








3977

被折叠的 条评论
为什么被折叠?



