1.新建工程父类
GetEE:git@gitee.com:lushengcheng/springcloud-alibaba.git 源码下载地址
1.新建maven项目:cloud2020
2.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>org.example</groupId>
<artifactId>cloud-2021</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!--统一管理jar包和版本-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.16.18</lombok.version>
<mysql.version>8.0.18</mysql.version>
<druid.verison>1.1.16</druid.verison>
<mybatis.spring.boot.verison>1.3.0</mybatis.spring.boot.verison>
</properties>
<dependencyManagement>
<dependencies>
<!--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>
</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 alibaba 2.1.0.RELEASE-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- MySql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- Druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.verison}</version>
</dependency>
<!-- mybatis-springboot整合 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.boot.verison}</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.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>
3.添加表数据
CREATE TABLE `payment` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`serial` VARCHAR(200) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `payment`(`id`,`serial`) VALUES (1,'广西大学行健文理学院'),(2,'alibaba'),(3,'京东'),(4,'头条');
SELECT * FROM payment WHERE id=1;
1.创建cloud-provider-payment8001项目
1.编写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>cloud-2021</artifactId>
<groupId>org.example</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>
<!--SpringBoot热部署配置 -->
<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>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
2.编写启动类
package com.xjggb.cloud;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan(basePackages = "com.xjggb.cloud.mapper") //包扫描
public class Payment8001 {
public static void main(String[] args) {
SpringApplication.run(Payment8001.class,args);
}
}
3.编写配置文件
server:
port: 8001 #服务端口
spring:
application:
name: cloud-provider-payment8001 #服务名
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/cloud?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
username: root
password: root
devtools:
restart:
enabled: true # 是否支持热部署
freemarker:
cache: false # 页面不加载缓存,修改即生效
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.xjggb.cloud.entity # 所有的entity别名所在包
4.编写Mybatis的xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xjggb.cloud.mapper.PaymentMapper">
<!--添加数据-->
<!--
useGeneratedKeys 主键
keyProperty 实体类属性的命
-->
<insert id="add" parameterType="payment" useGeneratedKeys="true" keyProperty="id">
insert into payment (serial) values (#{serial});
</insert>
<!--字段映射-->
<resultMap id="BaseResultMap" type="com.xjggb.cloud.entity.Payment">
<id column="id" property="id" jdbcType="BIGINT"/>
<id column="serial" property="serial" jdbcType="VARCHAR"/>
</resultMap>
<!--查询数据-->
<select id="getPayment" parameterType="Long" resultMap="BaseResultMap">
select * from payment where id=#{id}
</select>
</mapper>
5.编写业务类
1.编写mapper接口
package com.xjggb.cloud.mapper;
import com.xjggb.cloud.entity.Payment;
import org.apache.ibatis.annotations.Param;
public interface PaymentMapper
{
//插入
int add(Payment payment);
//查询
Payment getPayment(@Param("id") Long id);
}
2.编写业务接口
package com.xjggb.cloud.service;
import com.xjggb.cloud.entity.Payment;
public interface PaymentService {
/*
* 添加数据
* */
int add(Payment payment);
/*
* 根据id查询
* */
Payment getPayment(Long id);
}
3.编写实体类
package com.xjggb.cloud.entity;
import lombok.Data;
import java.io.Serializable;
@Data
public class Payment implements Serializable {
private Long id;
private String serial;
}
4.编写响应数据类
package com.xjggb.cloud.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {
private Integer code;
private String message;
private T data;
public CommonResult(Integer code,String message){
this(code,message,null);
}
}
5.编写业务实现
package com.xjggb.cloud.service.impl;
import com.xjggb.cloud.entity.Payment;
import com.xjggb.cloud.mapper.PaymentMapper;
import com.xjggb.cloud.service.PaymentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PaymentServiceImpl implements PaymentService {
@Autowired
private PaymentMapper paymentMapper;
@Override
public int add(Payment payment) {
return paymentMapper.add(payment);
}
@Override
public Payment getPayment(Long id) {
return paymentMapper.getPayment(id);
}
}
3.编写控制器
package com.xjggb.cloud.controller;
import com.xjggb.cloud.entity.CommonResult;
import com.xjggb.cloud.entity.Payment;
import com.xjggb.cloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@Slf4j
public class PaymentController {
@Autowired
private PaymentService paymentService;
@PostMapping("/payment/add")
public CommonResult add(@RequestBody Payment payment){
System.out.println("payment = " + payment.getSerial());
int add = paymentService.add(payment);
log.info("插入数据的ID:\t" +payment.getId());
log.info("插入的结果:\t"+payment);
if (add>0){
return new CommonResult(200,"插入成功",add);
}else {
return new CommonResult(444,"插入数据失败",null);
}
}
@GetMapping("/payment/get/{id}")
public CommonResult agePaymentById(@PathVariable("id") Long id){
Payment payment = paymentService.getPayment(id);
log.info("查询结果chchjaiocnacacacascaca"+payment);
if (payment!=null){
return new CommonResult(200,"查询成功",payment);
}else {
return new CommonResult(444,"没有对应记录",null);
}
}
}
6.查询结果
小结
开发步骤:
- 创建项目
- 编写pom.xml文件
- 编写启动类
- 编写配置文件
- 编写业务类
2.创建cloud-consumer-order80项目
1.编写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>cloud-2021</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-consumer-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.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>
</dependency>
</dependencies>
</project>
2.编写配置文件
package com.xjggb.cloud.controller;
import com.xjggb.cloud.entity.CommonResult;
import com.xjggb.cloud.entity.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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;
@RestController
@Slf4j
public class OrderController {
private final static String PAYMENT_URL = "http://localhost:8001";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id, CommonResult.class, id);
}
@GetMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment) {
return restTemplate.postForObject(PAYMENT_URL + "/payment/add", payment, CommonResult.class);
}
}
3.编写启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Consumer80 {
public static void main(String[] args) {
SpringApplication.run(Consumer80.class,args);
}
}
4.编写业务类
1.编写配置类
package com.xjggb.cloud.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();
}
}
2.编写控制类
package com.xjggb.cloud.controller;
import com.xjggb.cloud.entity.CommonResult;
import com.xjggb.cloud.entity.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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;
@RestController
@Slf4j
public class OrderController {
private final static String PAYMENT_URL = "http://localhost:8001";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id, CommonResult.class, id);
}
@GetMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment) {
return restTemplate.postForObject(PAYMENT_URL + "/payment/add", payment, CommonResult.class);
}
}
3.重构项目
1.新建模块 cloud-api-commons
2.编写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>cloud-2021</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-api-commons</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.1.0</version>
</dependency>
</dependencies>
</project>
3.将消费者和服者种的entities拷贝至新模块中,
删除原来的entities包,clean、install cloud-api-commons 模块,在消费则服务者pom.xml中分别引入依赖,测试运行。
2.Eureka
1.Eureka是什么?
Eureka 是 Netflix 开发的,一个基于 REST 服务的,服务注册与发现的组件,以实现中间层服务器的负载平衡和故障转移。
它主要包括两个组件:Eureka Server 和 Eureka Client
- Eureka Client:一个java客户端,简化与Eureka Server 的交互(就是微服务的客户端和服务端)
- Eureka Server:提供服务注册和发现的能力(微服务的注册中心)
系统架构图
服务在Eureka上注册,然后每个30秒发送心跳来更新他们的租约,如果客户端不能多次续订租约,那么它将在大约90秒内从服务器注册表中剔除,注册信息和更新被复制到集群中的所有eureka节点,来自任何区域的客户端都可以查询注册表信息(每30秒发生一次),来定位他们的服务,并进行远程调用
1.cloud-eureka-server7001
1.编写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>cloud-2021</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-eureka-server7001</artifactId>
<dependencies>
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!--自定义api通用包-->
<dependency>
<groupId>org.example</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--boot web acctuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</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>
</dependencies>
</project>
2.编写配置文件
server:
port: 7001 # 端口号
eureka:
instance:
hostname: localhsot # eureka服务实例名称
client:
register-with-eureka: false # 表示不注册中心不注册自己
fetch-registry: false #false 表示自己就是注册中心,我的职责就是维护服务实例,并不检索服务
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
4.成功显示
3.编写启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer //开启eureka服务端
public class EurekaApplication7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication7001.class,args);
}
}
2.微服务8001入驻7001
1.编写pom.xml文件
<!--eureka-server -client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.编写配置文件
eureka:
client:
register-with-eureka: true # 表示向注册中心注册自己 默认为true
fetch-registry: true #是否从EurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
service-url:
defaultZone: http://localhost:7001/eureka/ # 入驻地址
3.编写启动类
package com.xjggb.cloud;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient //开启客户端
@MapperScan(basePackages = "com.xjggb.cloud.mapper") //包扫描
public class Payment8001 {
public static void main(String[] args) {
SpringApplication.run(Payment8001.class,args);
}
}
4.成功显示
3.微服务80入驻7001
1.编写pom.xml文件
<!--eureka-server-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.编写配置文件
eureka:
client:
register-with-eureka: true # 表示向注册中心注册自己 默认为true
fetch-registry: true #是否从EurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
service-url:
defaultZone: http://localhost:7001/eureka/ # 入驻地址
3.编写启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient // 开启eureka客户端
public class Consumer80 {
public static void main(String[] args) {
SpringApplication.run(Consumer80.class,args);
}
}
4.结果显示
4.Eureka集群
1.原理说明
服务注册:将服务信息注册到注册中心
服务发现:从注册中心获取服务信息
实质:存key服务名,取value调用地址
步骤:
1.先启动eureka注册中心
2.启动服务提供者payment支付服务
3.支付服务启动后,会把自身信息注册到eureka
4.消费者order服务在需要调用接口时,使用服务别名去注册中心获取实际的远程调用地址
5.消费者获得调用地址后,底层实际是调用httpclient技术实现远程调用
6.消费者获得服务地址后会缓存在本地jvm中,默认每30秒更新异常服务调用地址
**问题:**微服务RPC远程调用最核心的是说明?
高可用: 如果一个注册中心只有一个,出现故障就麻烦了,会导致整个服务环境不可用,
解决办法: 搭建eureka注册忠中心集群,实现负载均衡+故障容错
相互注册,相互守望
2.集群搭建步骤
1.按照7001新建7002,除了主启动类和yml配置文件外,其他都一样
2.修改C:\Windows\System32\drivers\etc下的hosts
加上
# springcloud2020
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
3.新建cloud-eureka-server7002项目
4.编写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>cloud-2021</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-eureka-server7002</artifactId>
<dependencies>
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!--自定义api通用包-->
<dependency>
<groupId>org.example</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--boot web acctuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</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>
</dependencies>
</project>
5.编写配置文件
server:
port: 7002 # 端口号
eureka:
instance:
hostname: eureka7002.com #eureka服务端实例名称
client:
register-with-eureka: false # 表示不注册中心不注册自己
fetch-registry: false #false 表示自己就是注册中心,我的职责就是维护服务实例,并不检索服务
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
6.编写启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer //开启eureka服务端
public class EurekaApplication7002 {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication7002.class,args);
}
}
7.修改7001的配置文件
server:
port: 7001 # 端口号
eureka:
instance:
hostname: eureka7001.com #eureka服务端实例名称
client:
register-with-eureka: false # 表示不注册中心不注册自己
fetch-registry: false #false 表示自己就是注册中心,我的职责就是维护服务实例,并不检索服务
service-url:
defaultZone: http://eureka7002.com:7002/eureka/
8.集群效果
9.80和8001两个微服务注册到eureka集群
eureka:
client:
register-with-eureka: true # 表示向注册中心注册自己 默认为true
fetch-registry: true #是否从EurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
service-url:
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7001.com:7001/eureka/
#defaultZone: http://localhost:7001/eureka/ # 入驻地址
5.搭建支付服务集群
1.新建 cloud-provider-payment8002
2.编写pom.xml
<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>
<!--SpringBoot热部署配置 -->
<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>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--eureka-server -client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
3.复制8001的配置文件
4.编写启动类
package com.xjggb.cloud;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient //开启客户端
@MapperScan(basePackages = "com.xjggb.cloud.mapper") //包扫描
public class Payment8002 {
public static void main(String[] args) {
SpringApplication.run(Payment8002.class,args);
}
}
5.复制8001的业务类
6.修改8001和8002的控制器
package com.xjggb.cloud.controller;
import com.xjggb.cloud.entity.CommonResult;
import com.xjggb.cloud.entity.Payment;
import com.xjggb.cloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
@RestController
@Slf4j
public class PaymentController {
@Value("${server.port}")
private int port;
@Autowired
private PaymentService paymentService;
@PostMapping("/payment/add")
public CommonResult add(@RequestBody Payment payment){
System.out.println("payment = " + payment.getSerial());
int add = paymentService.add(payment);
log.info("插入数据的ID:\t" +payment.getId());
log.info("插入的结果:\t"+payment);
if (add>0){
return new CommonResult(200,"插入成功 ",add);
}else {
return new CommonResult(444,"插入数据失败",null);
}
}
@GetMapping("/payment/get/{id}")
public CommonResult agePaymentById(@PathVariable("id") Long id){
Payment payment = paymentService.getPayment(id);
log.info("查询结果"+payment);
if (payment!=null){
return new CommonResult(200,"查询成功 端口号为"+port,payment);
}else {
return new CommonResult(444,"没有对应记录",null);
}
}
}
7.80项目的controller层中
修改请求路径,我们把路径写死了
@RestController
@Slf4j
public class OrderController {
// private final static String PAYMENT_URL = "http://localhost:8001";
private final static String PAYMENT_URL = "http://CLOUD-PROVIDER-PAYMENT8001";
访问http://localhost:8080/consumer/payment/get/1
会报错
原因是:我们配置了以服务名的方式访问,但不能确定那个服务,我们需要给restTemplate开启负载均衡,默认是轮询
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced //开启负载均衡
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
8.访问成功
9.小结
从结果来看,负载均衡算法:是轮询
6.actuator微服务信息完善
eureka:
client:
register-with-eureka: true # 表示向注册中心注册自己 默认为true
fetch-registry: true #是否从EurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
service-url:
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7001.com:7002/eureka/
#defaultZone: http://localhost:7001/eureka/ # 入驻地址
instance:
instance-id: payment8001 #配置服务名
prefer-ip-address: true #访问路径可是显示ip地址
7.Eureka自我保护
1.概述
保护模式要用于一组客户端和Eureka Server之间在网络分区场景下的保护,一旦进入保护模式,EurekaServer将会尝试保护其他服务注册表中的信息,不再删除服务注册表中的数据,也不会注销任何微服务
看到下面这段话,说明EurekaServer进入了保护模式
2.导致原因
某时刻某一微服务不可用了,Eureka不会立即清理,依旧会对该微服务的信息进行保存
为什么会产生Eureka自我保护?
为了防止EurekaClient可以正常运行,但是与EurekaServer网络不通的情况下,EurekaServer不会立刻将EurekaClient服务剔除
3.什么是自我保护模式
默认情况下,EurekaServer在一定的时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)但是网络分区发生故障(延迟,卡顿,拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了,因为微服务本身很健康,此时不应该注销这个微服务。Eureka通过"自我保护模式" 来解决这个问题–当EurekaServer节点在短时间丢失过多的客户端(可能发生了网络分区故障),那么这个节点就会进入自我保护模式
4.自我保护机制
默认情况下EurekaClient定时向EurekaServer端发送心跳包,如果Eureka在Server端在一定的时间内(默认60秒)没有收到EurekaClient发送心跳包,便会直接从服务列表中剔除该服务,,短时间内(90秒)丢失大量的服务实例心跳包,EurekaServer会开启自我保护机制,不会剔除微服务,
自我保护中,EurekaServer会保护服务注册表中的信息,不会注销任何实例
他的设计哲学就是宁可保留错误的服务注册信息,也不能盲目注销任何有可能健康的实例,(好死不如赖活着)
小结:
自我保护模式是一种对应网络异常的安全保护措施,他的架构哲学是宁同时保留所有的微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务,使用自我保模式,可以让Eureka集群更加的健壮,稳定
8.禁止自我保护
1.Eureka关闭自我保护
eureka:
instance:
hostname: eureka7002.com #eureka服务端实例名称
client:
register-with-eureka: false # 表示不注册中心不注册自己
fetch-registry: false #false 表示自己就是注册中心,我的职责就是维护服务实例,并不检索服务
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
server:
enable-self-preservation: false # 关闭自我保护机制,保证服务被及时剔除
eviction-interval-timer-in-ms: 2000
2.客户端修改心跳时间
eureka:
client:
register-with-eureka: true # 表示向注册中心注册自己 默认为true
fetch-registry: true #是否从EurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
service-url:
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7001.com:7002/eureka/
#defaultZone: http://localhost:7001/eureka/ # 入驻地址
instance:
instance-id: payment8001 #配置服务名
prefer-ip-address: true #访问路径可是显示ip地址
lease-expiration-duration-in-seconds: 2 #Eureka服务端最后一次心跳等待时间上限(默认为90秒)超时剔除
lease-renewal-interval-in-seconds: 1 #Eurekak客户端向服务端发送心跳的间隔单位为秒(默认30)
3.关闭8002
可以看出立马剔除
3.zookeeper
1.创建提供者
新建项目 cloud-provider-payment8004
1.编写pom.xml文件
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging-api</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--SpringBoot整合Zookeeper客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<exclusions>
<!--先排除自带的zookeeper3.5.3-->
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--添加zookeeper3.4.6版本 -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</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>
2.编写配置文件
server:
port: 8004 #服务端口
spring:
application:
name: cloud-provider-payment8004
cloud:
zookeeper:
connect-string: localhost:2181
3.编写启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class Payment8004 {
public static void main(String[] args) {
SpringApplication.run(Payment8004.class,args);
}
}
4.编写业务类
package com.xjggb.cloud.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
@RestController
@Slf4j
public class PaymentController {
@Value("${server.port}")
private int SERVER_PORT;
@RequestMapping("/payment/zk")
public String show(){
return "Spring-cloud zookeeper:" + SERVER_PORT + "\t" + UUID.randomUUID().toString() ;
}
}
2.创建消费者
新建项目 cloud-consumer-order80
1.编写pom.xml文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--SpringBoot整合Zookeeper客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<exclusions>
<!--先排除自带的zookeeper3.5.3-->
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--添加zookeeper3.4.6版本 -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</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>
2.编写配置文件
server:
port: 8080 #服务端口
spring:
application:
name: cloud-consumer-zk-order80
cloud:
zookeeper:
connect-string: localhost:2181
3.编写启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class OrderZKMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderZKMain80.class,args);
}
}
4.编写业务类
package com.xjggb.cloud.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ApplicationConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
package com.xjggb.cloud.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class OrdeController {
public static final String URL="http://cloud-provider-payment8004";
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/consumer/payment/zk")
public String show(){
String object = restTemplate.getForObject(URL+"/payment/zk", String.class);
return object;
}
}
3.启动zookeeper
小结:
zookeeper中的节点是持久的还是临时的?
答: 临时的
4.consul
Consul下载地址https://www.consul.io/downloads.html
启动consul命
consul agent -dev -ui -node=cy
-dev开发服务器模式启动,-node结点名为cy,-ui可以用界面访问,默认能访问。
解压后到consul目录下打开cmd输入命令就直接启动了
Consul是什么?
Consul是一个服务网格(微服务间的 TCP/IP,负责服务之间的网络调用、限流、熔断和监控)解决方案,它是一个一个分布式的,高度可用的系统,而且开发使用都很简便。它提供了一个功能齐全的控制平面,主要特点是:服务发现、健康检查、键值存储、安全服务通信、多数据中心。
与其它分布式服务注册与发现的方案相比,Consul 的方案更“一站式”——内置了服务注册与发现框架、分布一致性协议实现、健康检查、Key/Value 存储、多数据中心方案,不再需要依赖其它工具。Consul 本身使用 go 语言开发,具有跨平台、运行高效等特点,也非常方便和 Docker 配合使用
1.新建提供者
1.新建模块
cloud-provider-payment8006
2.编写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>cloud-2021</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-provider-payment8006</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>
<!--SpringBoot热部署配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!-- SpringCloud consul- server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
</dependencies>
</project>
3.编写配置文件
server:
port: 8006 #服务端口
spring:
application:
name: cloud-provider-payment8006
cloud:
consul:
host: localhost #服务中心地址
port: 8500
discovery:
service-name: ${spring.application.name}
4.编写启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class}) //关闭数据源查询
@EnableDiscoveryClient
public class Payment8006 {
public static void main(String[] args) {
SpringApplication.run(Payment8006.class,args);
}
}
2.新建消费者
1.新建模块
cloud-consumer-consul-order80
2.编写pom.xml
<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>
<!--SpringBoot热部署配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!-- SpringCloud consul- server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
</dependencies>
3.编写配置文件
server:
port: 8080 #服务端口
spring:
application:
name: cloud-consumer-consul-order80
cloud:
consul:
host: localhost #服务中心地址
port: 8500
discovery:
service-name: ${spring.application.name}
4.编写启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
@EnableDiscoveryClient
public class ConsulOrder80 {
public static void main(String[] args) {
SpringApplication.run(ConsulOrder80.class,args);
}
}
5.注册中心的异同点
1.AP架构
2.CP架构
6.Ribbon
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的,包括后续我们将要介绍的Feign,它也是基于Ribbon实现的工具。所以,对Spring Cloud Ribbon的理解和使用,对于我们使用Spring Cloud来构建微服务非常重要。
1.负载均衡算法
1.IRule接口
/*
*
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.netflix.loadbalancer;
/**
* Interface that defines a "Rule" for a LoadBalancer. A Rule can be thought of
* as a Strategy for loadbalacing. Well known loadbalancing strategies include
* Round Robin, Response Time based etc.
*
* @author stonse
*
*/
public interface IRule{
/*
* choose one alive server from lb.allServers or
* lb.upServers according to key
*
* @return choosen Server object. NULL is returned if none
* server is available
*/
public Server choose(Object key);
public void setLoadBalancer(ILoadBalancer lb);
public ILoadBalancer getLoadBalancer();
}
2.IRule实现类图
2.负载规则替换
由图看出这个自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下,所以我们得另外创建包
1.编写配置类
package com.xjggb.myrule;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MySelfRule {
@Bean
public IRule myRule(){
return new RandomRule(); //定义随机
}
}
2.修改启动类
package com.xjggb.cloud;
import com.xjggb.myrule.MySelfRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
@SpringBootApplication
@EnableEurekaClient // 开启eureka客户端
@RibbonClient(name = "CLOUD-PROVIDER-PAYMENT8001",configuration = MySelfRule.class) //name 微服务名 configuration 配置类名
public class Consumer80 {
public static void main(String[] args) {
SpringApplication.run(Consumer80.class,args);
}
}
3.轮询原理
4.自定义轮询
-
首先8001、8002服务controller层加上
@GetMapping("/payment/lb") public String getPaymentLB() { return SERVER_PORT; }
-
80端编写接口
package com.xjggb.cloud.lb; import org.springframework.cloud.client.ServiceInstance; import java.util.List; public interface LoadBalancer { ServiceInstance instances(List<ServiceInstance> serviceInstances); }
-
80端编写实现类
package com.xjggb.cloud.lb; import org.springframework.cloud.client.ServiceInstance; import org.springframework.stereotype.Component; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @Component public class MyLB implements LoadBalancer{ private AtomicInteger atomicInteger = new AtomicInteger(0); @Override public ServiceInstance instances(List<ServiceInstance> serviceInstances) { int index = getAndIncrement() % serviceInstances.size(); return serviceInstances.get(index); } private final int getAndIncrement() { int current; int next; do { current = this.atomicInteger.get(); next = current >= Integer.MAX_VALUE ? 0 : current + 1; } while (!atomicInteger.compareAndSet(current, next)); System.out.println("第几次访问,次数next:" + next); return next; } }
-
注释注解
package com.xjggb.cloud.config; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class ApplicationContextConfig { @Bean //@LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
-
80端编写Cuntroller
package com.xjggb.cloud.controller; import com.xjggb.cloud.lb.LoadBalancer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.net.URI; import java.util.List; @RestController public class DemoController01 { @Autowired private DiscoveryClient discoveryClient; @Autowired private LoadBalancer loadBalancer; @Autowired private RestTemplate restTemplate; @GetMapping("/lb") public String getPaymentLB() { List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PROVIDER-PAYMENT8001"); 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); } }
6.结果
7.OpenFeign
1.OpenFeign能干什么?
Feign是一个声明式的Web Service客户端。它的出现使开发Web Service客户端变得很简单。使用Feign只需要创建一个接口加上对应的注解,比如:FeignClient注解。Feign有可插拔的注解,包括Feign注解和JAX-RS注解。Feign也支持编码器和解码器,Spring Cloud Open Feign对Feign进行增强支持Spring MVC注解,可以像Spring Web一样使用HttpMessageConverters等。
Feign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用Feign,可以做到使用HTTP请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问HTTP请求。
功能可插拔的注解支持,包括Feign注解和JAX-RS注解。支持可插拔的HTTP编码器和解码器(Gson,Jackson,Sax,JAXB,JAX-RS,SOAP)。支持Hystrix和它的Fallback。支持Ribbon的负载均衡。支持HTTP请求和响应的压缩。灵活的配置:基于 name 粒度进行配置支持多种客户端:JDK URLConnection、apache httpclient、okhttp,ribbon)支持日志支持错误重试url支持占位符可以不依赖注册中心独立运行
2.OpenFeign服务调用
1.新建模块
新建 cloud-consumer-feign-order80 项目
2.编写pom.xml文件
添加openfeign的依赖
<?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>cloud-2021</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-consumer-feign-order80</artifactId>
<dependencies>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</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>
3.编写yml文件
server:
port: 8080 #端口号
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/ # 入驻地址
spring:
application:
name: cloud-consumer-feign-order80
4.编写启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableEurekaClient //开启Eureka客户端
@EnableFeignClients //开启feign客户端
public class OrderFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain80.class,args);
}
}
5.编写业务类
编写接口
package com.xjggb.cloud.service;
import com.xjggb.cloud.entity.CommonResult;
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;
@Component
@FeignClient(value = "CLOUD-PROVIDER-PAYMENT8001")
public interface PaymentFeignService {
@GetMapping("/payment/get/{id}")
public CommonResult agePaymentById(@PathVariable("id") Long id);
}
编写控制器
package com.xjggb.cloud.controller;
import com.xjggb.cloud.entity.CommonResult;
import com.xjggb.cloud.service.PaymentFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class FeignController {
@Autowired
private PaymentFeignService paymentFeignService;
@GetMapping("/feign/{id}")
public CommonResult show(@PathVariable("id") Long id){
CommonResult commonResult = paymentFeignService.agePaymentById(id);
return commonResult;
}
}
6.小结:
OpenFeign的使用步骤:
- 添加依赖 :spring-cloud-starter-openfeign
- 启动类开启openfeign :@EnableFeignClients //开启feign客户端
- 接口添加注解 :@FeignClient(value = “CLOUD-PROVIDER-PAYMENT8001”) // value表示微服务名
3.OpenFeign超时控制
1.修改8001端控制类
//模拟超时
@GetMapping("/feign/timeout")
public int timeout(){
try {
Thread.sleep(5000); //设置五秒钟 超时默认为1秒钟
} catch (InterruptedException e) {
e.printStackTrace();
}
return port;
}
2.编写8080的接口
@Component
@FeignClient(value = "CLOUD-PROVIDER-PAYMENT8001")
public interface PaymentFeignService {
@GetMapping("/payment/get/{id}")
CommonResult agePaymentById(@PathVariable("id") Long id);
//模拟超时
@GetMapping("/feign/timeout")
int timeout();
}
3.编写8080控制类
//模拟超时
@GetMapping("/payment/feign/timeout")
public int timeout(){
return paymentFeignService.timeout();
}
4.报错信息
5.解决超时
因为OpenFeign的默认超时时间为1秒钟,有些业务可能要执行三秒,为了不让报超时一样,就得在配置文件中配置超时时间
server:
port: 8080 #端口号
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/ # 入驻地址
spring:
application:
name: cloud-consumer-feign-order80
# 设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
# 指的是建立连接所用的时间,适用于网络状态正常的情况下,两端连接所用的时间
ReadTimeout: 6000
# 指的是建立连接后从服务器读取到可用资源所用的时间
ConnectTimeout: 6000
小结:
OpenFeign的默认超时时间为1秒
4.OpenFeign日志增强
openfeign提供了日志打印功能。
Logger有四种类型:NONE(默认)
、BASIC
、HEADERS
、FULL
,通过注册Bean来设置日志记录级别
1.编写配置类
package com.xjggb.cloud.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignConfig {
@Bean
public Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
2.编写配置文件
配置需要监控的接口
#feign以什么级别监控那个接口
logging:
level:
com.xjggb.cloud.service.PaymentFeignService: debug
小结
OpenFein的功能?
- 服务调用简化了Ribbon和restTemplate服务调用
- 服务调用的超时配置
- 服务调用的日志详情
8.Hystrix
官网:https://github.com/Netflix/Hystrix/wiki
1.概述
1.分布式系统面临的问题
当一切正常时,请求看起来是这样的:
当其中有一个系统有延迟时,它可能阻塞整个用户请求
在高流量的情况下,一个后端依赖项的延迟可能导致所有服务器上的所有资源在数秒内饱和(PS:意味着后续再有请求将无法立即提供服务)
2.Hystrix是什么?
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免调用失败,比如超时调用失败,比如超时,异常等。Hystrix能够保证在一个依赖出问题的情况下,不会呆滞整体服务失败,避免整体服务失败,避免级联故障,以提高分布式系统的弹性
"断路器"本身就是一种开关装置,当某个服务单元发生故障之后,通过短路器的故障控制监控(类似熔断保险丝)**向调用方返回一个不符合预期的,可处理的备选响应(FallBack)而不是长时间的等待或者抛出调用方法无法处理异常,**这就保证了服务调用方的线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩
3.能干嘛
-
服务降级
1.返回友好提示: 服务器忙,请稍等再试,不让客户端等待立刻返回一个友好的提示,fallback
2.那些情况会发生降级
程序运行异常
超时
服务熔断触发服务降级
线程池/信号量打满也会导致服务降级
-
服务熔断
类比保险丝达到最大访问后,直接拒接访问,拉闸限电,然后调用服务降级的方法并返回友好的提示
就是保险丝 -》服务降级-》进而熔断-》恢复调用链路
-
服务限流
秒杀高并发等操作,严谨一窝蜂的过来拥挤,大家排队,一秒钟处理N个,有序进行
4.搭建环境
1.提供者
新建模块
cloud-provider-hystrix-payment8001
编写pom.xml文件
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</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>
<!--SpringBoot热部署配置 -->
<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>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--eureka-server -client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
编写配置文件
server:
port: 8001
spring:
application:
name: cloud-provider-hystrix-payment8001
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka/
编写业务类
service
package com.xjggb.cloud.servlce;
import org.springframework.stereotype.Service;
@Service
public class PaymentService {
/*
* 正常访问
* */
public String paymentInfo_ok(Integer id){
return "线程"+Thread.currentThread().getName()+"paymentInfo_ok"+"^_^"+id;
}
/*
* 超时访问
* */
public String timeOut(Integer id){
int timeNumber=3;
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程"+Thread.currentThread().getName()+"timeOut"+"^_^"+id;
}
}
controller
package com.xjggb.cloud.controller;
import com.xjggb.cloud.servlce.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class PaymentController {
@Autowired
private PaymentService paymentService;
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_ok(@PathVariable("id") Integer id){
log.info("程序正常进行");
return paymentService.paymentInfo_ok(id);
}
@GetMapping("/payment/hystrix/timeOut/{id}")
public String timeOut(@PathVariable("id") Integer id){
log.info("程序延迟进行");
return paymentService.timeOut(id);
}
}
编写启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class}) //关闭数据源查询
@EnableEurekaClient
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
}
2.消费者
新建项目
cloud-consumer-feign-hystrix-order80
编写pom.xml文件
<dependencies>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</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>
<!--SpringBoot热部署配置 -->
<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>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--eureka-server -client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
编写配置文件
server:
port: 8080
spring:
application:
name: cloud-consumer-feign-hystrix-order80
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka/
# 设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
# 指的是建立连接所用的时间,适用于网络状态正常的情况下,两端连接所用的时间
ReadTimeout: 6000
# 指的是建立连接后从服务器读取到可用资源所用的时间
ConnectTimeout: 6000
编写业务类
service
package com.xjggb.cloud.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;
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT8001")
public interface OrderService {
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_ok(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeOut/{id}")
public String timeOut(@PathVariable("id") Integer id);
}
controller
package com.xjggb.cloud.controller;
import com.xjggb.cloud.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderHystrixController {
@Autowired
private OrderService orderService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_ok(@PathVariable("id") Integer id){
return orderService.paymentInfo_ok(id);
}
@GetMapping("/consumer/payment/hystrix/timeOut/{id}")
public String timeOut(@PathVariable("id") Integer id){
return orderService.timeOut(id);
}
}
启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class}) //关闭数据源查询
@EnableEurekaClient
@EnableFeignClients //开启feign客户端
public class OrderHystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderHystrixMain80.class,args);
}
}
测试
当8001同一层次的接口服务被困死,因为tomcat线程池里面的工作线程已经被挤占完毕
80端口此时调用8001,客户端访问响应慢转圈圈
小结:
正是因为有上述故障或不佳表现,才有我们的降级/容错/限流等技术诞生
5.如何解决
1.超时导致服务器变慢(转圈):超时不再等待
2.出错(宕机或者程序运行出错):出错要有兜底
3.解决
1.对方服务(8001)超时了,调用者(80)不能一直卡死等待,必须服务降级
2.对方服务(8001)down机了,调用者(80)不能一直卡死等待,必须服务降级
3.对方服务(8001)ok 调用者(80)自己出现故障或者自我要求(自己的等待时间小于服务提供者)自己处理降级
6.服务降级
1.8001先从自身找问题
降级配置
超时配置
/*
* 超时访问
* */
@HystrixCommand(fallbackMethod = "paymentInfo_timeOut",commandProperties = {
@HystrixProperty(name ="execution.isolation.thread.timeoutInMilliseconds",value = "4000")
})
public String timeOut(Integer id){
int timeNumber=3;
try {
Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }
return "线程"+Thread.currentThread().getName()+"timeOut"+"^_^"+id+"耗时"+timeNumber;
}
//出来问题扛罪的
public String paymentInfo_timeOut(Integer id){
return "线程"+Thread.currentThread().getName()+"paymentInfo_timeOut"+"/(ㄒoㄒ)/~~"+id;
}
异常配置
/*
* 超时访问
* */
@HystrixCommand(fallbackMethod = "paymentInfo_timeOut",commandProperties = {
@HystrixProperty(name ="execution.isolation.thread.timeoutInMilliseconds",value = "4000")
})
public String timeOut(Integer id){
//int timeNumber=5000;
//try { Thread.sleep(timeNumber); } catch (InterruptedException e) { e.printStackTrace(); }
int i=10/0;
return "线程"+Thread.currentThread().getName()+"timeOut"+"^_^"+id+"耗时";
}
//出来问题扛罪的
public String paymentInfo_timeOut(Integer id){
return "线程"+Thread.currentThread().getName()+"paymentInfo_timeOut"+"/(ㄒoㄒ)/~~"+"异常处理";
}
8001而言,无论超时或者异常都会触发降级
8080的熔断配置
控制器
@GetMapping("/consumer/payment/hystrix/timeOut/{id}")
@HystrixCommand(fallbackMethod = "paymentInfo_timeOut",commandProperties = {
@HystrixProperty(name ="execution.isolation.thread.timeoutInMilliseconds",value = "1500")
})
public String timeOut(@PathVariable("id") Integer id){
return orderService.timeOut(id);
}
//出来问题扛罪的
public String paymentInfo_timeOut(Integer id){
return "我是消费者80,对方支付系统繁忙" ;
}
启动类添加注解
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class}) //关闭数据源查询
@EnableEurekaClient
@EnableFeignClients //开启feign客户端
@EnableCircuitBreaker //开启服务降级
public class OrderHystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderHystrixMain80.class,args);
}
}
配置文件 添加
feign:
hystrix:
enabled: true
缺点,每个方法都要配置兜底的方法,这样会显得带吗膨胀
解决代码膨胀
配置全局的服务降级
@RestController
@DefaultProperties(defaultFallback = "paymentInfo_timeOut") //服务降级方法
public class OrderHystrixController {
@Autowired
private OrderService orderService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_ok(@PathVariable("id") Integer id){
return orderService.paymentInfo_ok(id);
}
@GetMapping("/consumer/payment/hystrix/timeOut/{id}")
// @HystrixCommand(fallbackMethod = "paymentInfo_timeOut",commandProperties = {
// @HystrixProperty(name ="execution.isolation.thread.timeoutInMilliseconds",value = "1500")
// })
@HystrixCommand
public String timeOut(@PathVariable("id") Integer id){
int o=10/0;
return orderService.timeOut(id);
}
//出来问题扛罪的
public String paymentInfo_timeOut(){
return "我是消费者80,对方支付系统繁忙" ;
}
}
通过接口进行服务降级
常见的异常:
- 运行
- 超时
- 宕机
接口做服务降级
编写实现类
package com.xjggb.cloud.service;
import org.springframework.stereotype.Component;
@Component
public class PaymentFallbackService implements OrderService {
@Override
public String paymentInfo_ok(Integer id) {
return "PaymentInfo_ok---服务器忙";
}
@Override
public String timeOut(Integer id) {
return "PaymentInfo_ok---服务器忙";
}
}
编写接口
package com.xjggb.cloud.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;
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT8001",fallback = PaymentFallbackService.class)
public interface OrderService {
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_ok(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeOut/{id}")
public String timeOut(@PathVariable("id") Integer id);
}
7.服务熔断
服务熔断概述
熔断机制是应对雪崩效应的一种微服务链路保护机制,当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息
当检测到该节点微服务调用响应正常后,恢复调用链路
@GetMapping("/payment/hystrix/rongDuan/{id}")
@HystrixCommand(
fallbackMethod = "paymentCircuitBreaker_fallback", commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),// 是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),// 请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),// 时间窗口期/时间范文
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60")// 失败率达到多少后跳闸
}
)
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 不能负数,请稍后重试,o(╥﹏╥)o id:" + id;
}
小结:
熔断打开:请求不再进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时间达到所设时钟进入伴熔断状态
熔断关闭: 熔断关闭不会进行服务熔断
熔断半开: 部分请求根据规则调用当前服务,如果请求成功且符合鬼规则认为当前恢复正常,关闭熔断
8.搭建web客户端
1.创建工程
cloud-consumer-hystrix-dashbord9001
2.编写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>cloud-2021</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-consumer-hystrix-dashbord9001</artifactId>
<dependencies>
<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-starter-web</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>
3.编写yaml文件
server:
port: 9001
4.编写启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableHystrixDashboard //开启视图监控
public class HystrixDashboardMain9001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardMain9001.class,args);
}
}
5.测试
8001启动类添加配置
@Bean
public ServletRegistrationBean getServlet(){
HystrixMetricsStreamServlet hystrixMetricsStreamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean<HystrixMetricsStreamServlet> registrationBean = new ServletRegistrationBean<>(hystrixMetricsStreamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
输入url
9.Gateway
1.概述
在微服务架构里,服务的粒度被进一步细分,各个业务服务可以被独立的设计、开发、测试、部署和管理。这时,各个独立部署单元可以用不同的开发测试团队维护,可以使用不同的编程语言和技术平台进行设计,这就要求必须使用一种语言和平 台无关的服务协议作为各个单元间的通讯方式。
为什么用Gateway
Spring Cloud Gateway 可以看做是一个 Zuul 1.x 的升级版和代替品,比 Zuul 2 更早的使用 Netty 实现异步 IO,从而实现了一个简单、比 Zuul 1.x 更高效的、与 Spring Cloud 紧密配合的 API 网关。
Spring Cloud Gateway 里明确的区分了 Router 和 Filter,并且一个很大的特点是内置了非常多的开箱即用功能,并且都可以通过 SpringBoot 配置或者手工编码链式调用来使用。
比如内置了 10 种 Router,使得我们可以直接配置一下就可以随心所欲的根据 Header、或者 Path、或者 Host、或者 Query 来做路由。
比如区分了一般的 Filter 和全局 Filter,内置了 20 种 Filter 和 9 种全局 Filter,也都可以直接用。当然自定义 Filter 也非常方便。
API 网关的定义
网关的角色是作为一个 API 架构,用来保护、增强和控制对于 API 服务的访问。
API 网关是一个处于应用程序或服务(提供 REST API 接口服务)之前的系统,用来管理授权、访问控制和流量限制等,这样 REST API 接口服务就被 API 网关保护起来,对所有的调用者透明。因此,隐藏在 API 网关后面的业务系统就可以专注于创建和管理服务,而不用去处理这些策略性的基础设施
2.三大核心
-
Route(路由)
路由是构建网关的基本模块,他是由ID,目标URL,一系列的断言和过滤器组成,如果断言为true则匹配该路由
-
Predicate(断言)
-
filte(过滤)
开发人员可以匹配TTTP请求中的所有内容,如果请求与断言相匹配者进行路由
3.工作流程
4.入门配置
1.创建项目
cloud-gateway-gateway9527
2.编写配置文件
<?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>cloud-2021</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-gateway-gateway9527</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--eureka-server -client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
3.配置文件
server:
port: 9527 #端口号
spring:
application:
name: cloud-gateway-gateway9527
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心创建路由的功能,利用微服务名进行路由
routes:
- id: user_royte #指定路由唯一
uri: http://localhost:8001/ #指定路由服务地址
predicates:
- Path=/payment/get/** #指定路由规则
- After=2021-09-15T10:48:44.253+08:00[Asia/Shanghai]
eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
4.编写启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class GateWayMain9527 {
public static void main(String[] args) {
SpringApplication.run(GateWayMain9527.class,args);
}
}
5.动态路由
server:
port: 9527 #端口号
spring:
application:
name: cloud-gateway-gateway9527
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心创建路由的功能,利用微服务名进行路由
routes:
- id: user_royte #指定路由唯一
#uri: http://localhost:8001/ #指定路由服务地址
uri: lb://CLOUD-PROVIDER-PAYMENT8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** #指定路由规则
- After=2021-09-15T10:48:44.253+08:00[Asia/Shanghai]
eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
10.Spring Config
简介:在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。Spring Cloud Config项目是就是这样一个解决分布式系统的配置管理方案。它包含了Client和Server两个部分,server提供配置文件的存储、以接口的形式将配置文件的内容提供出去,client通过接口获取数据、并依据此数据初始化自己的应用。
Spring Cloud Config 有它的一套访问规则,我们通过这套规则在浏览器上直接访问就可以。
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
{application} 就是应用名称,对应到配置文件上来,就是配置文件的名称部分,例如我上面创建的配置文件。
{profile} 就是配置文件的版本,我们的项目有开发版本、测试环境版本、生产环境版本,对应到配置文件上来就是以 application-{profile}.yml 加以区分,例如application-dev.yml、application-sit.yml、application-prod.yml。
{label} 表示 git 分支,默认是 master 分支,如果项目是以分支做区分也是可以的,那就可以通过不同的 label 来控制访问不同的配置文件了。
1.服务端搭建
1.创建项目
cloud-config-center3344
2.编写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>cloud-2021</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-config-center3344</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>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.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>
3.编写配置文件
server:
port: 3344
spring:
application:
name: cloud-config-center3344
cloud:
config:
server:
git:
skip-ssl-validation: true
uri: https://gitee.com/lushengcheng/springcloud-config.git #gitEE上厂库的名字
search-paths:
- springcloud-config # 收索的目录
label: master #读取的分支
eureka:
client:
register-with-eureka: true # 表示向注册中心注册自己 默认为true
fetch-registry: true #是否从EurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
4.编写启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableConfigServer
public class MainConfigCenter3344 {
public static void main(String[] args) {
SpringApplication.run(MainConfigCenter3344.class,args);
}
}
5.测试
2.客户端搭建
application.yml是用户级的资源配置wenjian
bootstrap.yml是系统级的,优先级更高
Spring Cloud会创建一个"Bootstrap Context",作为Spring应用的Application Context的父上下文,初始化的时候"Bootstrap Context "负责从外部资源加载配置属性并解析配置,这两个上下文共享一个外部获取的Environment.
Client模块下的application.yml文件改为bootstrap.yml这个很关键
因为bootstrap.yml是比application.yml先加载的。bootstrap.yml优先级高于application.yml
1.创建模块
cloud-config-client-3355
2.编写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>cloud-2021</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-config-client-3355</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</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-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
</project>
3.编写配置文件
spring:
cloud:
config:
label: master #分支名称
name: config #配置文件名称
profile: dev #读取后缀名称 http://localhost:3344/master/config-dev.yml
uri: http://localhost:3344 #配置中心地址
4.编写启动类
package com.xjggb;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class ConfigClientMain3355 {
public static void main(String[] args) {
SpringApplication.run(ConfigClientMain3355.class,args);
}
}
5.编写业务类
package com.xjggb.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("${server.port}")
private int port;
@GetMapping("/helloConfig")
public String show(){
return "端口号为"+port;
}
}
6.测试
3.动态刷新
1.问题而来,分布式配置的动态刷新问题
- linux运维工程师修改GitHub上的配置文件做调整
- 刷新3344,发现ConfigServer配置中心立刻响应
- 刷新3355,发现ConfigClient客户端没有任何响应
- 3355没有变化除非自己重启或者重新加载
- 难道每次运维修改配置文件,客户段都需要重启
2.配置
添加注解@RefreshScope
package com.xjggb.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RefreshScope
public class ConfigClientController {
@Value("${server.port}")
private int port;
@Value("${spring.application.name}")
private String name;
@GetMapping("/helloConfig")
public String show(){
return "端口号为"+port+"微服务名称为"+name;
}
}
在cmd中添加
curl -X POST “http://localhost:3355/actuator/refresh”
就可以避免服务重启
11.BUS总线
1.是什么?
什么是总线
在微服务架构的系统中,通常会使用轻量级的消息代理来构建一个共同的消息主题,并让所有的微服务实例都连接起来,由于该主题产生消息会被所哟的实例监听和消费,所以称它为消息总线,
2.能干什么
3.总线设计思想
1.利用消息总线触发一个客户端/bus/refresh而刷新所有客户端的配置
2.利用消息总线触发一个服务端ConfigServer的/bus/refrsh端点,而刷新所有的客户端的配置
4.配置
1.ConfigServer3344添加消息总线
<!--添加消息总线RabbitMQ-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
yml配置
server:
port: 3344
spring:
application:
name: cloud-config-center3344
cloud:
config:
server:
git:
skip-ssl-validation: true
uri: https://gitee.com/lushengcheng/springcloud-config.git #gitEE上厂库的名字
search-paths:
- springcloud-config # 收索的目录
label: master #读取的分支
rabbitmq: #rabbitmq配置
port: 5672
host: http://192.168.93.222
username: guest
password: guest
eureka:
client:
register-with-eureka: true # 表示向注册中心注册自己 默认为true
fetch-registry: true #是否从EurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
#rabbitmq相关配置
#rebbitmq相关配置暴露bus刷新配置=端点
management:
endpoints:
web:
exposure:
include: 'bus-refresh'
cmd
curl -X POST "http://localhost:3355/actuator/refresh"
curl -X POST "http://localhost:3344/actuator/bus-refresh"
5.配置广播刷新
1.搭建 RabbitMQ 环境
参照3355模块新建3366模块
2.服务端3344,3355,3366添加依赖
<!--添加消息总线RabbitMQ-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
3344新添yml:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
# 暴露bus刷新配置的端点
management:
endpoints:
web:
exposure:
include: "bus-refresh"
3355,3366新添yml
rabbitmq: #rabbitmq相关配置,15672是web管理端口,5672是mq访问端口
port: 5672
host: localhost
username: guest
password: guest
#暴露监控端点
management:
endpoints:
web:
exposure:
include: "*"
2、cmd 刷新3344
修改服务名
curl -X POST "http://localhost:3344/actuator/bus-refresh"
一次修改广播通知,到处运行
12.Stream
**问题:**为什么要引入SpringCloud Stream
举例:对于我们Java程序员来说,可能有时要使用ActiveMQ,有时要使用RabbitMQ,甚至还有RocketMQ以及Kafka,这之间的切换似乎很麻烦,我们很难,也没有太多时间去精通每一门技术,那有没有一种新技术的诞生,让我们不再关注具体MQ的细节,自动的给我们在各种MQ内切换。
1.是什么?
什么是SpringCloudStream
官方定义 Spring Cloud Stream是一个构建消息驱动微服务的框架。
应用程序通过inputs或者outputs来与 Spring Cloud Stream中binder对象交互。
通过我们配置来binding(绑定),而Spring Cloud Stream 的 binder对象负责与消息中间件交互。所以,我们只需要搞清楚如何与Spring Cloud Stream交互就可以方便使用消息驱动的方式。
通过使用Spring Integration来连接消息代理中间件以实现消息事件驱动。
Spring Cloud Stream为一些供应商的消息中间件产品提供了个性化的自动化配置实现,引用了发布-订阅、消费组、分区的三个核心概念。
日前们吉扶DahhitMKafla
2.能干什么?
这些中间件的差异性导致我们实际项目开发给我们造成了一定的困扰,我们如果用了两个消息队列的其中一种,后面的业务需求,我想往另外一种消息队列进行迁移,这时候无疑就是一个灾难性的,一大堆东西都要重新推倒重新做,因为它跟我们的系统耦合了,这时候springcloud Stream给我们提供了—种解耦合的方式。
为什么要用CloudStream
比方说我们用到了RabbitMQ和Kafka,由于这两个消息中间件的架构上的不同,像RabbitMQ有exchange,kafka有Topic和Partitions分区。
这些中间件的差异性导致我们实际项目开发给我们造成了一定的困扰,我们如果用了两个消息队列的其中一种,后面的业务需求,我想往另外一种消息队列进行迁移,这时候无疑就是一个灾难性的,一大堆东西都要重新推倒重新做,因为它跟我们的系统耦合了,这时候Spring Cloud Stream给我们提供了—种解耦合的方式
Stream凭什么可以统一底层的差异?
在没有绑定器这个概念的情况下,我们的SpringBoot应用要直接与消息中间件进行信息交互的时候,由于各消息中间件构建的初衷不同,它们的实现细节上会有较大的差异性通过定义绑定器作为中间层,完美地实现了应用程序与消息中间件细节之间的隔离。通过向应用程序暴露统一的Channel通道,使得应用程序不需要再考虑各种不同的消息中间件实现。
通过定义绑定器Binder作为中间层,实现了应用程序与消息中间件细节之间的隔离
3.怎么玩
1.消息驱动生产者
1.新建项目
cloud-stream-rabbitmq-provider8801
2.编写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>cloud-2021</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-stream-rabbitmq-provider8801</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.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</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>
3.编写配置文件
server:
port: 8801
spring:
application:
name: cloud-stream-provider
cloud:
stream:
binders: # 在此处配置要绑定的rabbitMQ的服务信息
defaultRabbit: # 表示定义的名称,用于binding的整合
type: rabbit # 消息中间件类型
environment: # 设置rabbitMQ的相关环境配置
spring:
rabbitmq:
host: 192.168.93.222
port: 5672
username: guest
password: guest
bindings: # 服务的整合处理
output: # 这个名字是一个通道的名称
destination: studyExchange # 表示要使用的exchange名称定义
content-type: application/json # 设置消息类型,本次为json,文本则设为text/plain
binder: defaultRabbit # 设置要绑定的消息服务的具体设置
eureka:
client:
service-url:
defaultZone: http://eureka7001.com: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 # 显示i
4.编写业务类
编写接口
package com.xjggb.cloud.service;
public interface IMessageProvider {
String send();
}
编写实现类
package com.xjggb.cloud.service.impl;
import com.xjggb.cloud.service.IMessageProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.MessageBuilder;
import java.util.UUID;
@EnableBinding(Source.class) //定义消息的推送管道
public class MessageProviderImpl implements IMessageProvider {
@Autowired
private MessageChannel output; //消息发送管道
@Override
public String send() {
String string = UUID.randomUUID().toString();
output.send(MessageBuilder.withPayload(string).build());
System.out.println("*******************************************" + string);
return string;
}
}
package com.xjggb.cloud.controller;
import com.xjggb.cloud.service.IMessageProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class StreamController {
@Autowired
private IMessageProvider messageProvider;
@GetMapping("/senMessage")
public String show(){
return messageProvider.send();
}
}
启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class StreamRabbitmq8801 {
public static void main(String[] args) {
SpringApplication.run(StreamRabbitmq8801.class,args);
}
}
测试
2.消息驱动消费者1
1.创建项目
cloud-stream-rabbitmq-consumer8802
2.编写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>cloud-2021</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-stream-rabbitmq-consumer8802</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.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</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>
3.编写配置文件
server:
port: 8802
spring:
application:
name: cloud-stream-provider
cloud:
stream:
binders: # 在此处配置要绑定的rabbitMQ的服务信息
defaultRabbit: # 表示定义的名称,用于binding的整合
type: rabbit # 消息中间件类型
environment: # 设置rabbitMQ的相关环境配置
spring:
rabbitmq:
host: 192.168.93.222
port: 5672
username: guest
password: guest
bindings: # 服务的整合处理
input:
destination: studyExchange # 表示要使用的exchange名称定义
content-type: application/json # 设置消息类型,本次为json,文本则设为text/plain
binder: defaultRabbit # 设置要绑定的消息服务的具体设置
eureka:
client:
service-url:
defaultZone: http://eureka7001.com: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 # 显示i
4.编写启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class StreamRabbitmq8802 {
public static void main(String[] args) {
SpringApplication.run(StreamRabbitmq8802.class,args);
}
}
5.编写业务类
package com.xjggb.cloud.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;
@Component
@EnableBinding(Sink.class)
public class ReceiveMessageListController {
@Value("${server.port}")
private int port;
@StreamListener(Sink.INPUT)
public void show(Message<String> message){
System.out.println("消费者一号,------》接收到消息 = " + message.getPayload()+"\t port"+port);
}
}
测试
3.消息驱动消费者2
跟消费者以一样,修改端口号即可
4.Stream之消息重复消费
原理:微服务应用置于同一个group中,就能保证消息只会被其中一个应用消费一次,不同得到组是可以消费的,同一组内会发生竞争关系,只有其中一个可以消费
修改8801配置文件
server:
port: 8804
spring:
application:
name: cloud-stream-provider
cloud:
stream:
binders: # 在此处配置要绑定的rabbitMQ的服务信息
defaultRabbit: # 表示定义的名称,用于binding的整合
type: rabbit # 消息中间件类型
environment: # 设置rabbitMQ的相关环境配置
spring:
rabbitmq:
host: 192.168.93.222
port: 5672
username: guest
password: guest
bindings: # 服务的整合处理
input:
destination: studyExchange # 表示要使用的exchange名称定义
content-type: application/json # 设置消息类型,本次为json,文本则设为text/plain
binder: defaultRabbit # 设置要绑定的消息服务的具体设置
group: atguiguB #配置分组 《------------------
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka
instance:
lease-renewal-interval-in-seconds: 2 # 设置心跳的间隔时间,默认30
lease-expiration-duration-in-seconds: 5 # 超过5秒间隔,默认90
instance-id: send-8803.com # 主机名
prefer-ip-address: true # 显示i
同样位置,为8803修改分组为`group: atguiguB
修改8802和8803的分组为同一个组,那么同一个组的多个微服务实例,每次只会有一个拿到
5.Stream之消息持久化
通过上述,解决了重复消费问题,再看看持久化。
停止8802/8803并去除掉8802的分组group: A_Group,8803的分组group: A_Group没有去掉。
8801先发送4条消息到RabbitMq。
先启动8802,无分组属性配置,后台没有打出来消息。
再启动8803,有分组属性配置,后台打出来了MQ上的消息。(消息持久化体现)
13.SpringCloudAlibaba
1.Nacos
1.简介
官网:https://nacos.io/zh-cn/
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。
2.什么是Nacos?
服务(Service)是 Nacos 世界的一等公民。Nacos 支持几乎所有主流类型的“服务”的发现、配置和管理:
3.能干嘛
- 能做注册中心
- 能做配置中心
4.官网地址
https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
父工程依赖
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
5.下载
nacos下载和安装
访问 localhost:8848/nacos 出现界面代表成功 默认账号密码 nacos/nacos
6.服务提供者
1.创建工程
cloudalibaba-provider-payment9001
2.编写pom.xml文件
<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>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</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>
</dependency>
</dependencies>
3.编写配置文件
server:
port: 9001 #端口号
spring:
application:
name: nacos-provider
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #配置 Nacos地址
#暴露监控
management:
endpoints:
web:
exposure:
include: '*'
4.编写启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain9001.class,args);
}
}
5.编写业务类
package com.xjggb.cloud;
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;
@RestController
public class controllerPaymentController {
@Value("${server.port}")
private int port;
@GetMapping(value = "/payment/nacos/{id}")
public String show(@PathVariable("id") Integer id){
return "nacos register, serverPort:"+port+"\t id:"+id;
}
}
以上步骤重复一次做提供者集群服务
7.服务消费者
1.创建项目
cloud-consumer-nacos-order83
2.编写pom.xml文件
<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>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</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>
</dependency>
</dependencies>
3.编写配置文件
server:
port: 8080
Spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
# 消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-provider
4.编写配置类
package com.xjggb.cloud.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced //结合ribbon做负载均衡的时候需要加入注解
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
5.编写业务类
package com.xjggb.cloud.controller;
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 org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
public class OrderNacosController {
@Resource
private RestTemplate restTemplate;
@Value("${service-url.nacos-user-service}")
private String serverUrl;
@GetMapping("/consumer/payment/nacos/{id}")
public String show(@PathVariable("id") Integer id){
String object = restTemplate.getForObject(serverUrl + "/payment/nacos/" + id, String.class);
return object;
}
}
6.编写启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class OrderNacosMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderNacosMain80.class,args);
}
}
启动
8.Nacos自带负载功能
9.Nacos 服务中心对比提升
Nacos支持Cp+Ap的切换
10.服务配置中心
1.创建项目
cloud-config-nacos-client3377
2.编写pom.xml
<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>
<!--nacos-config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- nacos-discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</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>
</dependency>
</dependencies>
3.编写配置文件
application.yaml文件
spring:
profiles:
active: dev #表示开发环境
bootstrap.yaml
#nacos配置
server:
port: 3377
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 # Nacos服务注册中心地址
config:
server-addr: localhost:8848 # Nacos服务注册中心地址
file-extension: yaml # 指定Yaml格式的配置 3377就可以去配置中心读指定后缀名是yaml的文件
# ${spring.application.name}-${spring.profile.active}.${Spring.cloud.nacos.config.file.extension}
4.编写启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class NacosClientConfigMain3377 {
public static void main(String[] args) {
SpringApplication.run(NacosClientConfigMain3377.class,args);
}
}
5.编写业务类
package com.xjggb.cloud.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RefreshScope //支持Nacos动态刷新功能
public class ConfigClientController {
@Value("${config.info}")
private String info;
@GetMapping("/config/info")
public String show(){
return info;
}
}
6.配置规则
# ${spring.application.name}-${spring.profile.active}.${Spring.cloud.nacos.config.file.extension}
${spring.application.name}
${spring.profile.active}
${Spring.cloud.nacos.config.file.extension}
7.配置新增
8.测试
如果报这个错误
9.访问成功读取配置
小结:
11.Nacos之服务配置中心–分类
问题1:
问题1:
实际开发中通常一个系统会准备
1.dev开发环境
2.test测试环境
3.prod生产环境
如何保证指定的环境启动服务能正确读取到Nacos相应的环境配置文件呢?
问题2:
一个大型为分布式微服务系统会有很多微服务子项目
每个微服务项目又都会有相应的开发环境,测试环境,预发环境,正式环境。。。。
那怎么对这些微服务配置进行管理?
Nacos之命名空间分组和 Goup Data ID三者之间的关系
大致结构
1.Data ID方案
新建text 配置DataID
修改application.yml配置文件
重启3377服务
2.Group方案
修改配置文件
测试
3.Namespace命名空间方案
新建dev命名空间
新建test命名空间
dev命名空间
启动测试
test命名空间
修改配置文件
启动测试
14.Sentinel
1.简介
2.官网
https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
3.主要特性
4.下载
https://github.com/alibaba/Sentinel/tags
启动
5.测试
1.新建项目
cloudalibaba-sentinel-service8401
2.编写pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- nacos-discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--sentinel datasource nacos 持久化会用到-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</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>
</dependency>
</dependencies>
3.编写配置文件
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
# Nacos作为服务中心的地址
server-addr: localhost:8848
sentinel:
transport:
# 配置Sentinel dashboard地址
dashboard: localhost:8080
# 默认8719端口,加入被占用 自动+1寻找未被占用的端口
port: 8719
#监控
management:
endpoints:
web:
exposure:
include: '*'
4.编写启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient //进行注册
public class MainSentinel8401 {
public static void main(String[] args) {
SpringApplication.run(MainSentinel8401.class,args);
}
}
5.编写业务类
package com.xjggb.cloud.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class HelloController {
@GetMapping("/testA")
public String show(){
return "-----------testA";
}
@GetMapping("/testB")
public String show1(){
return "-----------testB";
}
}
6.启动nacos 启动sentinel在启动8401
7.sentinel页面还没有东西,因为他是懒加载模式
6.流控规则简介
- 资源名:唯一名称,默认请求路径
- 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认defauit(不区分来源)
- 阈值类型/单机阈值:
- QPS(每秒钟的请求数量):当调用该api的QPS达到阈值的时候,进行限流
- 线程数:当调用该api线程数达到阈值的时候,进行限流
- 是否集群:不需要集群
- 流控模式:
- 直接:api达到限流条件时,直接限流
- 关联:当关联的资源达到阈值时,就限流自己
- 链路:只记录指定的链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)(api级别的针对来源)
- 流控效果:
- 快速失败:直接失败,报异常
- Warm Up:根据condeFactor (冷加载因子,默认3)的值,从阈值codeFactor,经过预热时长,才达到设置QPS阈值
- 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,是否无效
1.QPS(每秒请求数)直接失败
超过阈值(当前配置一秒一次 )就会失败
2.线程数直接失败
线程数:当调用该API的线程数达到阈值的时候,进行限流
让线程睡一秒
3.流控模式
- 直接:api达到限流条件时,直接限流
- 关联:当关联的资源达到阈值时,就就限流自己
- 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量。如果达到阈值,就进行限流)【api级别的针对来源】
关联失败
- 当关联的资源达到阈值时,就限流自己
- 当与A关联的资源B达到阈值后,就限流A自己 ,B惹事,A挂了
当关联资源/testB的qps阈值超过1时,就会限流/testA的访问地址,当关联资源阈值后限制配置好的资源名
小结:
当B的阈值达到后,就会限流A
4…流控效果
1.直接:快速失败(默认的流控处理)
2.预热:
请求刷新
刚刚开始不行,预热五秒后,后续慢慢的ok
案 例: 秒杀系统在开启的瞬间,会有很多流量上来,很可能把系统杀死,预热方式就是为了保护系统,可慢慢的把流量放进来,慢慢的把阈值增长到设置的阈值
3.排队等待:
这种方式主要用于处理间隔突发的流量,例如消息队列,想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间主键处理这些请求,而不是在第一秒直接拒接多余的请求
7.熔断降级
1.RT(平均响应时间,秒级)
平均响应时间,超出阈值且在时间窗口内通过的请求 >=5,两个同时满足后触发降级,窗口期过后关闭短路器
平均响应时间0.2秒 等待回复时间5秒
@GetMapping("/testD")
public String show2(){
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
log.info("test 测试RT");
return "---------------- testD ";
}
按照上面的配置,
永远一秒钟进来20线程(大于5个了)调用tsetD,我们希望200毫秒处理完基本业务,如果超过200毫秒还没有处理完,在未来5秒钟的时间窗口内,短路器打开(保险丝跳闸)微服务不可用,保险丝跳闸断电
2.异常比例(秒级)
QPS>=5且异常比例(秒级统计)超过阈值时,触发降级:时间窗口结束后,关闭降级
@GetMapping("/testD")
public String show2(){
// try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
log.info("test 测试RT");
int i=10/0;
return "---------------- testD ";
}
每秒钟超过五个请求,异常大于20% 就会发生熔断降级
停止后正常报错
3.异常数(分钟级)
异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YnZgJDyX-1632890348008)(H:\学习笔记\java笔记\图片文件\spring-cloud图片\157.png)]
@GetMapping("/testE")
public String show3(){
log.info("test 测试异常数");
int i=10/0;
return "---------------- testE ";
}
第一次访问报错
http://localhost:8401/testE ,第一次访问绝对报错,因为除数不能为零,刷新五次后,就会进入熔断降级
8.热点key限流
根据请求传入的参数进行限流
何为热点?热点及经常访问的数据,很多时候希望统计某个热点数据中热点数据访问频次最高的Top K数据,并对其访问进行限制,比如:
- 商品ID为参数,统计一段时间内最常购买的商品ID并行进行限制
- 用户ID为参数,针对一段时间内频繁访问的用户ID进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流,热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效
Sentinel利用LRU策列统计最近常访问的热点参数,结合令牌通算法来进行参数级别的流控热点参数限流支持集群模式
兜底方法
分为系统默认和客户自定义,两种
//热点key
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey") //vale和blockHandler:没有斜杠,名字唯一
public String show4(@RequestParam(value = "p1",required = false) String p1,
@RequestParam(value = "p2",required = false) String p2){
return "-------> testHotKey ";
}
public String deal_testHotKey(String p1, String p2, BlockException blockException){
return "-------> testHotKey o(╥﹏╥)o";
}
第0个参数的访问次数超过QPS的阈值触发熔断降级
如果将 blockHandler去掉
异常会显示到页面 blockHandler是一个兜底的方法
9.参数例外项
特殊情况
- 普通:超过1秒钟一个后,达到阈值1后马上就会被限流
- 我们期望wuP1参数当他是某个特殊值时,他的限流值和平时不一样
- 特例,假如p1的值等于5时,他的阈值可以达到200
请求 http://localhost:8401/testHotKey?p1=5
请求 http://localhost:8401/testHotKey?p1=4
当参数不是5时就会触发熔断降级
10.Sentinel系统规则
总的系统规则 QPS 对cloudalibaba-sentinel-service系统中的所有请求应用都生效
当单台机器所有入口流量的QPS达到阈值立即触发系统保护
11.@SentinelResource的配置
1.修改8401的pom.xml文件
<dependency>
<groupId>org.example</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
2.新建一个Controller类
package com.xjggb.cloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.xjggb.cloud.entity.CommonResult;
import com.xjggb.cloud.entity.Payment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RateLimitController {
@GetMapping("/byResource")
@SentinelResource(value = "byResource",blockHandler = "handleException")
public CommonResult show(){
return new CommonResult(200,"按资源限流ok",new Payment());
}
//兜底方法
public CommonResult handleException(BlockException blockException){
return new CommonResult(404,blockException.getClass().getCanonicalName()+"\t服务不可用");
}
}
3.访问成功
有斜杠的流控
测试结果 直接进入sentinel默认的降级
没有斜杠的
测试 会进入自定义兜底的方法
4.面临的一些问题
- 系统默认的,没有体现我们自己的业务要求
- 按照现有的条件,我们自定义的处理方法又和业务代码耦合在一起,不直观,
- 每个业务方法都添加一个兜底的,那么代码膨胀加剧
- 全局统一的处理方式没有体现
12.客户自定义限流处理逻辑
1.创建自定义限流处理逻辑
package com.xjggb.cloud;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.xjggb.cloud.entity.CommonResult;
public class MyHandler {
public static CommonResult handlerException1(BlockException blockException){
return new CommonResult(333,"按客户自定义 handlerException---------1");
}
public static CommonResult handlerException2(BlockException blockException){
return new CommonResult(444,"按客户自定义 handlerException---------2");
}
}
2.RateLimitController编写请求
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(
value = "customerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class, //自定义全局兜底的类
blockHandler = "handlerException1" //兜底的方法
)
public CommonResult show3(){
return new CommonResult(200,"客户自定义",new Payment(200L,"来了老弟"));
}
3.启动微服务
4.Sentinel控制台配置
5.测试定义结果
6.进一步说明
13.Sentinel服务熔断
1.Ribbon环境预说明
sentinel整合ribbon+openfeign+fallback
- 启动nacos和sentinel
- 提供者9003/9004
- 消费者
新建提供者9003/9004
pom.xml文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- nacos-discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--sentinel datasource nacos 持久化会用到-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--引入自己定义的api通用包, 可以使用Payment支付Entity-->
<dependency>
<groupId>org.example</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</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>
</dependency>
</dependencies>
application.yml
server:
port: 9003
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848
management:
endpoints:
web:
exposure:
include: '*'
启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class Payment9003 {
public static void main(String[] args) {
SpringApplication.run(Payment9003.class,args);
}
}
业务类
package com.xjggb.cloud.controller;
import com.xjggb.cloud.entity.CommonResult;
import com.xjggb.cloud.entity.Payment;
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 java.util.HashMap;
@RestController
public class PaymentController {
@Value("server.port")
private String serverPort;
public static HashMap<Long, Payment> map= new HashMap<>();
//静态代码块
static {
map.put(1L,new Payment(1L,"123456789"));
map.put(2L,new Payment(2L,"123456789"));
map.put(3L,new Payment(3L,"123456789"));
}
@GetMapping(value = "/payment/{id}")
public CommonResult<Payment> show(@PathVariable("id") Long id){
Payment payment = map.get(id);
return new CommonResult<Payment>(200,"成功查询"+serverPort,payment);
}
}
新建消费者84
pom.xml和9003/9004一样
yml文件
server:
port: 84
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080
# 默认端口 加入8719被占用 会自动+1扫描直到找到没有被占用的端口
port: 8719
service-url:
nacos-user-service: http://nacos-payment-provider
启动类
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class OrderMain84 {
public static void main(String[] args) {
SpringApplication.run(OrderMain84.class,args);
}
}
业务类
package com.xjggb.cloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.xjggb.cloud.entity.CommonResult;
import org.springframework.beans.factory.annotation.Autowired;
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;
@RestController
public class CircleBreakerController {
public static final String SERVER_URL="http://nacos-payment-provider";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback")
public CommonResult show(@PathVariable("id") Long id) throws IllegalAccessException {
CommonResult result = restTemplate.getForObject(SERVER_URL + "/payment/" + id, CommonResult.class, id);
if (id==4){
throw new IllegalAccessException("IllegalAccessException非法参数异常");
}else if (result.getData()==null){
throw new NullPointerException("NullPointerException,该id没有对应记录,空指针异常");
}
return result;
}
}
测试
2.sentinel服务熔断无配置
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback")
public CommonResult show(@PathVariable("id") Long id) throws IllegalAccessException {
CommonResult result = restTemplate.getForObject(SERVER_URL + "/payment/" + id, CommonResult.class, id);
if (id==4){
throw new IllegalAccessException("IllegalAccessException非法参数异常");
}else if (result.getData()==null){
throw new NullPointerException("NullPointerException,该id没有对应记录,空指针异常");
}
return result;
}
启动/84 /9003 /9004 测试业务和负载均衡
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lBpJbiPw-1632890348043)(H:\学习笔记\java笔记\图片文件\spring-cloud图片\184.png)]
没有设置blockHandier
id大于3都是异常页面 页面很不友好
3.sentinel服务熔断只配置fallback
@GetMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback",fallback = "handlerFallback")//fallback只负责业务异常
public CommonResult show(@PathVariable("id") Long id) throws IllegalAccessException {
CommonResult result = restTemplate.getForObject(SERVER_URL + "/payment/" + id, CommonResult.class, id);
if (id==4){
throw new IllegalAccessException("IllegalAccessException非法参数异常");
}else if (result.getData()==null){
throw new NullPointerException("NullPointerException,该id没有对应记录,空指针异常");
}
return result;
}
//fallback的兜底方法
public CommonResult<Payment> handlerFallback(@PathVariable("id") Long id, Throwable throwable){
Payment payment = new Payment(404L, "页面找不到");
return new CommonResult<>(400,"兜底异常类"+throwable.getMessage(),payment);
}
访问id大于4后进入兜底方法
小结 :fallback 可以管理java的运行异常
4.sentinel 服务熔断只配置blockHandler
package com.xjggb.cloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.xjggb.cloud.entity.CommonResult;
import com.xjggb.cloud.entity.Payment;
import org.springframework.beans.factory.annotation.Autowired;
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;
@RestController
public class CircleBreakerController {
public static final String SERVER_URL="http://nacos-payment-provider";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规
public CommonResult show(@PathVariable("id") Long id) throws IllegalAccessException {
CommonResult result = restTemplate.getForObject(SERVER_URL + "/payment/" + id, CommonResult.class, id);
if (id==4){
throw new IllegalAccessException("IllegalAccessException非法参数异常");
}else if (result.getData()==null){
throw new NullPointerException("NullPointerException,该id没有对应记录,空指针异常");
}
return result;
}
//本例是blockHandler的兜底
public CommonResult blockHandler(@PathVariable Long id, BlockException e){
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"blockHandler-sentinel限流,无此流水,BlockException: "+ e.getMessage(),payment);
}
}
第一次访问
错误两次以上就会服务降级
小结:当错误超过两次就会触发服务降级
5.sentinel 服务熔断blockHandler和fallback都配置
package com.xjggb.cloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.xjggb.cloud.entity.CommonResult;
import com.xjggb.cloud.entity.Payment;
import org.springframework.beans.factory.annotation.Autowired;
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;
@RestController
public class CircleBreakerController {
public static final String SERVER_URL="http://nacos-payment-provider";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consumer/fallback/{id}")
//@SentinelResource(value = "fallback") //无配置
//@SentinelResource(value = "fallback",fallback = "handlerFallback")//fallback只负责业务异常
// @SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规
@SentinelResource(value = "fallback",blockHandler = "blockHandler",fallback = "handlerFallback")
public CommonResult show(@PathVariable("id") Long id) throws IllegalAccessException {
CommonResult result = restTemplate.getForObject(SERVER_URL + "/payment/" + id, CommonResult.class, id);
if (id==4){
throw new IllegalAccessException("IllegalAccessException非法参数异常");
}else if (result.getData()==null){
throw new NullPointerException("NullPointerException,该id没有对应记录,空指针异常");
}
return result;
}
//fallback的兜底方法
public CommonResult<Payment> handlerFallback(@PathVariable("id") Long id, Throwable throwable){
Payment payment = new Payment(404L, "页面找不到");
return new CommonResult<>(400,"兜底异常类"+throwable.getMessage(),payment);
}
//本例是blockHandler的兜底
public CommonResult blockHandler(@PathVariable Long id, BlockException e){
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"blockHandler-sentinel限流,无此流水,BlockException: "+ e.getMessage(),payment);
}
}
新增限流规则
1.正常访问
2.限流
当QPS每秒超过1次就会触发限流 进入 blockHandler 流控规则
- fallback
14.OpenFeign
1.引入OpenFeign
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.application.yml加入
feign:
sentinel:
enabled: true #激活sentinel 对Fenign的支持
3.启动类添加注解
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients //启动Feign服务
public class OrderMain84 {
public static void main(String[] args) {
SpringApplication.run(OrderMain84.class,args);
}
}
4.新增接口
service feign 接口+注解
package com.xjggb.cloud.service;
import com.xjggb.cloud.entity.CommonResult;
import com.xjggb.cloud.entity.Payment;
import com.xjggb.cloud.service.impl.PaymentFallBackServiceImpl;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallBackServiceImpl.class) //value微服务名称 fallback 兜底的方法类
public interface PaymentService {
@GetMapping(value = "/payment/{id}")
public CommonResult<Payment> show(@PathVariable("id") Long id);
}
5.兜底实现类
package com.xjggb.cloud.service.impl;
import com.xjggb.cloud.entity.CommonResult;
import com.xjggb.cloud.entity.Payment;
import com.xjggb.cloud.service.PaymentService;
import org.springframework.stereotype.Component;
@Component
public class PaymentFallBackServiceImpl implements PaymentService {
@Override
public CommonResult<Payment> show(Long id) {
return new CommonResult<>(55555,"服务降级返回-----PaymentFallBackServiceImpl",new Payment(123L,"errorSerial"));
}
}
6.controller类
@Resource
private PaymentService paymentService;
@GetMapping(value = "/consumer/payment/{id}")
public CommonResult<Payment> show3(@PathVariable("id") Long id){
return paymentService.show(id);
}
7.测试
http://localhost:84/consumer/payment/1
8.关闭提供者
关闭9003服务提供者,看84消费者自动降级,不会耗死
服务降级成功
15.规则持久化
将限流的配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的控流规则就能看到,只要Nacos里面的配置不删除,针对8401上的sentinel的流控规则持续有效
1.修改8401添加依赖
<!--sentinel datasource nacos 持久化会用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
2.添加yml配置
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
3.启动8401
4.配置sentioe
5.测试
当点击每秒超过设定的QPS就会发生服务降级
6.新建配置
[
{
"resource":"/byResource",
"limitApp":"default",
"grade":1,
"count":1,
"strategy":0,
"controlBehavior":0,
"clusterMode":false
}
]
json说明
- resource:资源名称
- limitApp:来源应用
- grade: 阈值类型,0表示线程数,1表示QPS
- count: 单机阈值;
- strategy:流控效果。0表示直接 ,1表示关联,2表示链路
- controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待
- clusterMode: 是否集群
启动8401当前没有规则
测试 当每秒的QPS大于1就会触发降级 说明持久化成功
"+ e.getMessage(),payment);
}
}
[外链图片转存中...(img-GCAwfyI7-1632890348046)]
第一次访问
[外链图片转存中...(img-uK2JWdU0-1632890348047)]
错误两次以上就会服务降级
[外链图片转存中...(img-n40smJ86-1632890348048)]
小结:当错误超过两次就会触发服务降级
##### 5.sentinel 服务熔断blockHandler和fallback都配置
```java
package com.xjggb.cloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.xjggb.cloud.entity.CommonResult;
import com.xjggb.cloud.entity.Payment;
import org.springframework.beans.factory.annotation.Autowired;
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;
@RestController
public class CircleBreakerController {
public static final String SERVER_URL="http://nacos-payment-provider";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consumer/fallback/{id}")
//@SentinelResource(value = "fallback") //无配置
//@SentinelResource(value = "fallback",fallback = "handlerFallback")//fallback只负责业务异常
// @SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规
@SentinelResource(value = "fallback",blockHandler = "blockHandler",fallback = "handlerFallback")
public CommonResult show(@PathVariable("id") Long id) throws IllegalAccessException {
CommonResult result = restTemplate.getForObject(SERVER_URL + "/payment/" + id, CommonResult.class, id);
if (id==4){
throw new IllegalAccessException("IllegalAccessException非法参数异常");
}else if (result.getData()==null){
throw new NullPointerException("NullPointerException,该id没有对应记录,空指针异常");
}
return result;
}
//fallback的兜底方法
public CommonResult<Payment> handlerFallback(@PathVariable("id") Long id, Throwable throwable){
Payment payment = new Payment(404L, "页面找不到");
return new CommonResult<>(400,"兜底异常类"+throwable.getMessage(),payment);
}
//本例是blockHandler的兜底
public CommonResult blockHandler(@PathVariable Long id, BlockException e){
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"blockHandler-sentinel限流,无此流水,BlockException: "+ e.getMessage(),payment);
}
}
新增限流规则
[外链图片转存中…(img-o9rHYeqM-1632890348049)]
1.正常访问
[外链图片转存中…(img-U0Uscw0D-1632890348050)]
2.限流
当QPS每秒超过1次就会触发限流 进入 blockHandler 流控规则
[外链图片转存中…(img-zLNNCNvo-1632890348051)]
-
fallback
[外链图片转存中…(img-72K7zMbO-1632890348053)]
14.OpenFeign
1.引入OpenFeign
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.application.yml加入
feign:
sentinel:
enabled: true #激活sentinel 对Fenign的支持
3.启动类添加注解
package com.xjggb.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients //启动Feign服务
public class OrderMain84 {
public static void main(String[] args) {
SpringApplication.run(OrderMain84.class,args);
}
}
4.新增接口
service feign 接口+注解
package com.xjggb.cloud.service;
import com.xjggb.cloud.entity.CommonResult;
import com.xjggb.cloud.entity.Payment;
import com.xjggb.cloud.service.impl.PaymentFallBackServiceImpl;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallBackServiceImpl.class) //value微服务名称 fallback 兜底的方法类
public interface PaymentService {
@GetMapping(value = "/payment/{id}")
public CommonResult<Payment> show(@PathVariable("id") Long id);
}
5.兜底实现类
package com.xjggb.cloud.service.impl;
import com.xjggb.cloud.entity.CommonResult;
import com.xjggb.cloud.entity.Payment;
import com.xjggb.cloud.service.PaymentService;
import org.springframework.stereotype.Component;
@Component
public class PaymentFallBackServiceImpl implements PaymentService {
@Override
public CommonResult<Payment> show(Long id) {
return new CommonResult<>(55555,"服务降级返回-----PaymentFallBackServiceImpl",new Payment(123L,"errorSerial"));
}
}
6.controller类
@Resource
private PaymentService paymentService;
@GetMapping(value = "/consumer/payment/{id}")
public CommonResult<Payment> show3(@PathVariable("id") Long id){
return paymentService.show(id);
}
7.测试
http://localhost:84/consumer/payment/1
8.关闭提供者
关闭9003服务提供者,看84消费者自动降级,不会耗死
服务降级成功
15.规则持久化
将限流的配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的控流规则就能看到,只要Nacos里面的配置不删除,针对8401上的sentinel的流控规则持续有效
1.修改8401添加依赖
<!--sentinel datasource nacos 持久化会用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
2.添加yml配置
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
3.启动8401
4.配置sentioe
5.测试
当点击每秒超过设定的QPS就会发生服务降级
6.新建配置
[
{
"resource":"/byResource",
"limitApp":"default",
"grade":1,
"count":1,
"strategy":0,
"controlBehavior":0,
"clusterMode":false
}
]
json说明
- resource:资源名称
- limitApp:来源应用
- grade: 阈值类型,0表示线程数,1表示QPS
- count: 单机阈值;
- strategy:流控效果。0表示直接 ,1表示关联,2表示链路
- controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待
- clusterMode: 是否集群
启动8401当前没有规则
[外链图片转存中…(img-0ivLOab2-1632890348062)]
测试 当每秒的QPS大于1就会触发降级 说明持久化成功