1. 项目说明

当前这篇教程是:

1. Gateway集成Sentinel做限流、熔断降级(超时、异常比例、异常数),集成Sentinel控制台动态配置策略

2. SpringCloud 2020版之后就集成LoadBalancer,默认是轮询策略。自定义负载均衡策略,比如某台服务器性能好,可以多分配

简单创建一个SpringCloud2021.0.3项目(一)

简单创建一个SpringCloud2021.0.3项目(二)

简单创建一个SpringCloud2021.0.3项目(三)

简单创建一个SpringCloud2021.0.3项目(四)

1. 版本

  1. SpringCloud版本为2021.0.3
  2. SpringBoot版本为2.7.2

2. 用到组件

  1. 注册中心:暂时用Eureka,后面再改成Nacos
  2. 网关:Gateway
  3. 权限:Security,Gateway集成
  4. 负载均衡:LoadBalancer,SpringCloud2020版之后就集成LoadBalancer
  5. 限流、熔断降级:Sentinel
  6. 配置中心:暂时用Config,后面改成Nacos
  7. 服务间访问:Feign

3. 功能

  1. 项目最基本功能,权限控制,在分布式系统中基于Token的身份验证。
  2. 前端登陆,做了2种方式。用户、密码、验证码;邮箱、验证码、图片滑块;并且前端加密传给后端解密;登陆异常次数限制;
  3. 限流、负载均衡,应对高并发情况,降低系统负载;
  4. 服务熔断降级:避免系统雪崩,提高系统可用性;
  5. 两种方式的多数据源,一种是通过AOP方式动态切换数据源,另一种是不同数据源管理的数据各不相同;
  6. 日志系统Logback,是SpringBoot默认集成

2. 上俩篇教程

简单创建一个SpringCloud2021.0.3项目(一)

简单创建一个SpringCloud2021.0.3项目(二)

  1. 新建Eureka注册中心
  2. 新建Config配置中心,producerService服务读取参数
  3. 2个业务服务(producerService和webService),webService通过Feign调用producerService的服务
  4. webService用到多数据源,不同的数据源管理不同的数据;security模块测试通过AOP方式动态切换数据源
  5. 抽取公共模块common,集成redis
  6. 新建Gateway网关,集成Security,做登陆和资源权限控制
  7. 前端登陆,做了2种方式。用户、密码、验证码;邮箱、验证码、图片滑块;并且前端加密传给后端解密;登陆异常次数限制
  8. 在分布式系统中基于Token的身份验证
  9. 每次请求刷新用户会话有效时间

3. Gateway集成sentinel,网关层做熔断降级

1. 超时熔断降级

  1. 添加sentinel依赖

<!-- SpringCloud Alibaba Sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>${sentinel.version}</version>
</dependency>
<!-- SpringCloud Alibaba Sentinel Gateway -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
<version>${sentinel.version}</version>
</dependency>
  1. 配置熔断规则,配置类

点击查看代码
package com.xiaostudy.gateway.sentinel;

import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List; @Configuration
public class SentinelConfig implements InitFunc {
@Value("${web.application.name}")
private String webName; /**
* 网关规则
*/
@PostConstruct
@Override
public void init() {
initDegradeRule();
} // 自定义熔断规则
private void initDegradeRule() {
List<DegradeRule> rules = new ArrayList<>();
DegradeRule rule = new DegradeRule();
rule.setResource(webName);
// set threshold rt, 500 ms
rule.setCount(500);
// 设置降级规则RT, 平均响应时间
rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
// 设置时间窗口
rule.setTimeWindow(10);
rules.add(rule);
DegradeRuleManager.loadRules(rules);
} }
  1. 配置文件添加参数

此时的配置文件
server:
port: '@gateway.port@' eureka:
port: '@eureka.port@'
ip: '@eureka.ip@'
url-name: '@eureka.url.name@'
instance:
# 把本机IP注册到eureka而不是本机机器名
preferIpAddress: true
# 把本机IP注册到eureka,由下面参数组成
instance-id: ${spring.cloud.client.ip-address}:${server.port}
client:
serviceUrl:
defaultZone: http://@eureka.user.name@:@eureka.user.password@@${eureka.ip}:${eureka.port}/${eureka.url-name}/ spring:
application:
name: '@gateway.application.name@'
cloud:
loadbalancer:
retry:
# 关闭重试
enabled: false
gateway:
routes:
# 路由的id,没有规定规则但要求唯一,建议配合服务名
- id: '@producer.application.name@'
# 匹配后提供服务的路由地址,LoadBalancer做负载均衡
uri: lb://@producer.application.name@
predicates:
# 断言,路径相匹配的进行路由
- Path=/producer/**
filters:
# 去掉url一级前缀,例如http://localhost:9904/producer/producerTest/getByName,等同于http://localhost:9901/producerTest/getByName
- StripPrefix=1
- id: '@web.application.name@'
uri: lb://@web.application.name@
predicates:
- Path=/web/**
filters:
- StripPrefix=1
sentinel:
# 服务启动直接建立心跳连接,饿汉式
eager: true
filter:
# 手动注入Sentinel的过滤器,关闭Sentinel注入CommonFilter实例
enabled: false
redis:
host: localhost
port: 6379
# 默认0
database: 1
lettuce:
pool:
# 连接池最大连接数(使用负值表示没有限制),默认值:8
max-active: 20
# 连接池中的最大空闲连接,默认值:8
max-idle: 10
# 连接池中的最小空闲连接,默认值:0
min-idle: 1
# 连接池最大阻塞等待时间(使用负值表示没有限制),默认值:-1,单位:毫秒
max-wait: 2000 profiles:
# 使用的配置文件后缀application-security.yml。一个或多个,中间英文逗号分开
active: security
web:
application:
name: '@web.application.name@'
  1. 生产者这个接口睡眠600毫秒,一个从web通过feign读取,一个直接从producer读取,web的我们已经设置超时熔断,producer没有设置

  2. 重启producerService和gateway

  3. 用Jmeter做压测,设置请求头、HTTP请求、监听结果









  4. 看看没有设置熔断的producer请求







  5. 修改一下producer测试接口睡眠时间和web熔断的RT时间









这表示熔断正常,设置监听的资源超时熔断

2. 异常熔断

web加入sentinel依赖







3. 集成sentinel-dashboard控制台

  1. 下载sentinel-dashboard-1.8.3.jar

    https://github.com/alibaba/Sentinel/releases/tag/1.8.3

    先下载一个sentinel-dashboard-1.8.3.jar

  2. 启动命令

java -Dserver.port=9910 -jar sentinel-dashboard-1.8.3.jar
  1. 登陆

    用户密码都是sentinel

    http://localhost:9910/

  2. webService的配置文件添加配置

sentinel:
transport:
# 指定应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer。如果端口被占用会往后+1,直到没有占用
port: '@sentinel.port@'
dashboard: localhost:9910

  1. 注释异常熔断返回

  2. 添加熔断规则



  3. 测试异常比例熔断降级



  4. 测试异常数熔断降级,效果也是一样的,因为这里每一个请求都是异常





4. 限流

  1. 控制台方式





不过用sentinel控制台这种方式设置限流、熔断,随着服务停止而销毁,配合nacos的话就可以保存。

  1. gateway设置

sentinel:
# 服务启动直接建立心跳连接,饿汉式
eager: true
filter:
# 手动注入Sentinel的过滤器,关闭Sentinel注入CommonFilter实例
enabled: false
# 限流时,自定义返回内容
scg:
fallback:
response-body: '{"code":200,"status":1,"msg":"服务器暂不可用啦!"}'
mode: response
response-status: 200
transport:
# 指定应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer。如果端口被占用会往后+1,直到没有占用
port: '@sentinel.port@'
dashboard: localhost:9910







5. 负载均衡

SpringCloud 2020版之后就集成LoadBalancer,所以无需再加入LoadBalancer依赖

目前支持2中负载均衡机制:1、轮询机制,也是默认使用的;2、随机机制;

gateway路由中使用lb开头的就是使用轮询负载均衡,lb就是LoadBalancer的简写;

这里重写轮询方式,比如我们的服务器性能是不一样的,性能好的我们就给他多处理

  1. 重写ReactorServiceInstanceLoadBalancer
点击查看代码
package com.xiaostudy.gateway.loadbalancer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono; import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger; // 自定义轮询
public class MyRoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private static final Logger log = LoggerFactory.getLogger(MyRoundRobinLoadBalancer.class); final AtomicInteger position; final String serviceId; ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider; /**
* @param serviceInstanceListSupplierProvider a provider of
* {@link ServiceInstanceListSupplier} that will be used to get available instances
* @param serviceId id of the service for which to choose an instance
*/
public MyRoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
String serviceId) {
this(serviceInstanceListSupplierProvider, serviceId, new Random().nextInt(1000));
} /**
* @param serviceInstanceListSupplierProvider a provider of
* {@link ServiceInstanceListSupplier} that will be used to get available instances
* @param serviceId id of the service for which to choose an instance
* @param seedPosition Round Robin element position marker
*/
public MyRoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
String serviceId, int seedPosition) {
this.serviceId = serviceId;
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
this.position = new AtomicInteger(seedPosition);
} @SuppressWarnings("rawtypes")
@Override
// see original
// https://github.com/Netflix/ocelli/blob/master/ocelli-core/
// src/main/java/netflix/ocelli/loadbalancer/RoundRobinLoadBalancer.java
public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get(request).next()
.map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));
} private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,
List<ServiceInstance> serviceInstances) {
Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances);
if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
}
return serviceInstanceResponse;
} private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
if (instances.isEmpty()) {
if (log.isWarnEnabled()) {
log.warn("No servers available for service: " + serviceId);
}
return new EmptyResponse();
} // Ignore the sign bit, this allows pos to loop sequentially from 0 to
// Integer.MAX_VALUE
int pos = this.position.incrementAndGet() & Integer.MAX_VALUE; // TODO 原来的算法
// int i = pos % instances.size();
// instances是指某个资源有几个服务,也就是集群数。pos是记录某个资源被访问次数
// ServiceInstance instance = instances.get(i); // TODO 自定义算法,比如指定服务器多处理一次循环
int i = pos % (instances.size() + 1);
ServiceInstance instance = null;
if (i == instances.size()) {
for (ServiceInstance serviceInstance : instances) {
if (serviceInstance.getHost().equals("192.168.1.6") && "9906".equals(serviceInstance.getPort())) {
instance = serviceInstance;
}
}
if (null == instance) {
instance = instances.get(i - 1);
}
} else {
// instances是指某个资源有几个服务,也就是集群数。pos是记录某个资源被访问次数
instance = instances.get(i);
} return new DefaultResponse(instance);
}
}
  1. 自定义负载均衡策略配置类
点击查看代码
package com.xiaostudy.gateway.loadbalancer;

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment; // 自定义负载策略
public class MyLoadBalancerConfiguration {
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
// return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
return new MyRoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}
  1. 启动类添加

// 开启负载均衡
// 这种是所有资源都统一配置
//@LoadBalancerClients(defaultConfiguration = MyLoadBalancerConfiguration.class)
// 这种方式是指定资源
@LoadBalancerClients({
@LoadBalancerClient(value = "WEB-SERVICE", configuration = MyLoadBalancerConfiguration.class)
,@LoadBalancerClient(value = "PRODUCER-SERVICE", configuration = MyLoadBalancerConfiguration.class)
})
  1. 启动gateway

  2. 复制多个服务,改一下端口。并把服务启动





  3. 看效果

简单创建一个SpringCloud2021.0.3项目(三)的更多相关文章

  1. 简单创建一个SpringCloud2021.0.3项目(四)

    目录 1. 项目说明 1. 版本 2. 用到组件 3. 功能 2. 上三篇教程 3. 日志处理 1. 创建日志公共模块 2. Eureka引入日志模块 4. 到此的功能代码 5. 注册中心换成naco ...

  2. 简单创建一个SpringCloud2021.0.3项目(二)

    目录 1. 项目说明 1. 版本 2. 用到组件 3. 功能 2. 上一篇教程 3. 创建公共模块Common 4. 网关Gateway 1. 创建Security 2. Security登陆配置 3 ...

  3. 简单创建一个SpringCloud2021.0.3项目(一)

    目录 1. 项目说明 1. 版本 2. 用到组件 3. 功能 2. 新建父模块和注册中心 1. 新建父模块 2. 新建注册中心Eureka 3. 新建配置中心Config 4. 新建两个业务服务 1. ...

  4. 【Vue-Cli3.0】【1】创建一个Vue-Cli3.0的项目

    最近在做爬虫,然后要爬好多数据,代码写完了,就让它在爬了.不想闲着就复习一下Vue吧! 开始开始! ***正式讲解之前 先下载一个node.js吧! 一.首先检查一下 版本 PS D:\徐孟林\D D ...

  5. 通过beego快速创建一个Restful风格API项目及API文档自动化

    通过beego快速创建一个Restful风格API项目及API文档自动化 本文演示如何快速(一分钟内,不写一行代码)的根据数据库及表创建一个Restful风格的API项目,及提供便于在线测试API的界 ...

  6. 以sb7code为基础创建一个基本的OpenGL项目

      以sb7code为基础创建一个基本的OpenGL项目   从github上面下载sb7code代码: https://github.com/openglsuperbible/sb7code 打开H ...

  7. 通过beego快速创建一个Restful风格API项目及API文档自动化(转)

    通过beego快速创建一个Restful风格API项目及API文档自动化 本文演示如何快速(一分钟内,不写一行代码)的根据数据库及表创建一个Restful风格的API项目,及提供便于在线测试API的界 ...

  8. 如何使用maven建一个web3.0的项目

    使用eclipse手动建一个maven的web project可能会有版本不合适的情况,例如使用spring的websocket需要web3.0什么的,不全面的修改可能会出现各种红叉,甚是苦恼.我从我 ...

  9. 简单创建一个完整的struts2框架小程序

    要完成一个struts2框架的搭建, 1.首先应该从官网上下载最新的jar包,网络连接:http://struts.apache.org/download.cgi#struts2514.1,选择下载F ...

随机推荐

  1. SAP 时区转换

    DATA:l_tzone TYPE tzonref-tzone.   "TIME ZONE    DATA:l_timesp TYPE tzonref-tstamps."TIME  ...

  2. 前端下载图片的N种方法

    前几天一个简单的下载图片的需求折腾了我后端大佬好几天,最终还是需要前端来搞,开始说不行的笔者最后又行了,所以趁着这个机会来总结一下下载图片到底有多少种方法. 先起个服务 使用expressjs起个简单 ...

  3. 【Python】和【Jupyter notebook】的正确安装方式?

    学了那么久Python,你的Python安装方式正确吗?今天给你看看什么才是Python正确的安装方式,教程放在下面了,喜欢的记得点赞. Python安装 Python解答Q群:660193417## ...

  4. java: 程序包org.springframework.boot不存在

    如果你的settings中的maven配置没问题的话,尝试下面这个 在控制台输入  mvn idea:idea  重构一下

  5. dotnet 控制台 使用 Microsoft.Maui.Graphics 配合 Skia 进行绘图入门

    本文将告诉大家如何在 dotnet 的控制台模式下,采用 MAUI 自绘库 Microsoft.Maui.Graphics 进行绘图,设置 Microsoft.Maui.Graphics 底层调用 M ...

  6. git 删除、合并多次commit提交记录

    合并多次记录 1. git log找到要合并的记录的数量. 2. git rebase -i HEAD~5 将最上面一个的记录选为pack,下面记录都改为s. ================= 删除 ...

  7. CANN算子:利用迭代器高效实现Tensor数据切割分块处理

    摘要:本文以Diagonal算子为例,介绍并详细讲解如何利用迭代器对n维Tensor进行基于位置坐标的大批量数据读取工作. 本文分享自华为云社区<CANN算子:利用迭代器高效实现Tensor数据 ...

  8. 笔记本USB接口案例分析和是实现

    笔记本电脑 笔记本电脑(laptop)通常具备使用USB设备的功能.在生产时,笔记本都预留了可以插入USB设备的USB接口,但具体是什么USB设备,笔记本厂商并不关心,只要符合USB规格的设备都可以 ...

  9. Selenium指定浏览器路径

    ChromeOptions options = new ChromeOptions(); options.setBinary("C:\\Program Files (x86)\\Google ...

  10. 【C++】从设计原理来看string类

    1.一些C++基础知识 模板类string的设计属于底层,其中运用到了很多C++的编程技巧,比如模板.迭代器.友元.函数和运算符重载.内联等等,为了便于后续理解string类,这里先对涉及到的概念做个 ...