微服务二gateway网关的使用
目录
1:什么是网关
是整个微服务API请求的入口,负责拦截所有请求,分发到服务上去;
作用:分发请求、权限控制(鉴权)、限流、统一返回数据、日志拦截、缓存、灰度发布
1.1:网关组件有哪些
-
- OpenResty:就是基于nginx lua脚本集成的组件,通过在不同的阶段挂在定义的lua脚本来实现自定义处理行为
- kong:基于OpenResty编写的网关,nginx支持并大在5w左右,Nginx更适合做外部一层的网关
- zuul:全称springbloud zuul1.x采用servlet进行通信,底层是同步io,新来一个请求就会新增一个线程,并且不会进行回收,所以占用资源,所以资源占用较高,也就意味着支持的并发量不高。虽然在zuul2.x将通信调整为了netty servlet来实现,并且支持异步,但是性能上差别不是很大
- spring cloud gateway:gateway底层是Netty,支持的请求数在1w~1.5w左右,性能要比zuul高很多
1.2:spring cloud gateway简介
Spring Cloud Gateway是Spring Cloud推出的用来替代Zuul的网关产品,如同zuul综合了ribbon、hystrix的负载均衡、熔断、降级、限流能力,Spring Cloud Gateway也整合了hystrix
2:gateway的使用
构建gateway项目不需要引入spring web依赖
导入与springboot版本相对应的依赖:
-
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-gateway -->
-
<dependency>
-
<groupId>org.springframework.cloud</groupId>
-
<artifactId>spring-cloud-starter-gateway</artifactId>
-
<version>3.1.1</version>
-
</dependency>
2.1:创建三个模块,一个为网关,两个为服务
使用spring boot项目创建
2.2:创建多个启动实例的方法
复制一份yml文件修改端口
启动
这样两个启动实例就成功了
接下来我们在controller层验证一下是否可以访问成功
访问成功
3:进行断言
表示的意思就是通过配置规则来判断请求会分发到哪个服务上。
yml配置项
-
server:
-
port: 8083
-
spring:
-
cloud:
-
gateway:
-
discovery:
-
locator:
-
# 开启从注册中心动态创建路由的功能,利用微服务名进行路由,默认false
-
enabled: false
-
routes:
-
# 路由ID,唯一即可,一般建议服务名
-
- id: product-server
-
uri: http://localhost:9090
-
predicates:
-
- Path=/product-server/**
-
filters:
-
- StripPrefix=1
-
- id: order
-
uri: http://localhost:8080
-
predicates:
-
- Path=/order/**
-
filters:
-
# 去除路径前缀的个数
-
- StripPrefix=2
就可以使用网关的端口访问order的端口了,这里为什么多了个”xxx“,因为配置文件中StripPrefix去除前缀的个数;如果为一的话,就可以不要”xxx“即可访问
4:负载均衡
负载均衡(Load Balance,简称 LB)是高并发、高可用系统必不可少的关键组件,目标是 尽力将网络流量平均分发到多个服务器上,以提高系统整体的响应速度和可用性
导入依赖:
-
<dependency>
-
<groupId>org.springframework.cloud</groupId>
-
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
-
</dependency>
yml配置文件:
-
#负载均衡
-
- id: prder-server-lb
-
uri: lb://order-server
-
#断言
-
predicates:
-
- Path=/order-lb/**
-
filters:
-
- StripPrefix=1
-
order-server:
-
ribbon:
-
#随机策略
-
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
-
listOfServers: http://localhost:8080,http://localhost:8081,http://localhost:8082
5:自定义网关过滤器
自定义网关过滤器需要实现以下两个接口 :GatewayFilter,Ordered
新建一个filter文件夹:
-
import com.alibaba.fastjson.JSONObject;
-
import com.alibaba.fastjson.serializer.SerializerFeature;
-
import lombok.AllArgsConstructor;
-
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
-
import org.springframework.cloud.gateway.filter.GlobalFilter;
-
import org.springframework.core.Ordered;
-
import org.springframework.core.io.buffer.DataBuffer;
-
import org.springframework.http.HttpStatus;
-
import org.springframework.http.server.reactive.ServerHttpResponse;
-
import org.springframework.stereotype.Component;
-
import org.springframework.util.StringUtils;
-
import org.springframework.web.server.ServerWebExchange;
-
import reactor.core.publisher.Flux;
-
import reactor.core.publisher.Mono;
-
import org.springframework.util.AntPathMatcher;
-
-
import java.nio.charset.StandardCharsets;
-
import java.util.Arrays;
-
import java.util.List;
-
-
/**
-
* @author lcy
-
* @version 1.0
-
* @date 2023/3/7 17:51
-
*/
-
@Component
-
@AllArgsConstructor
-
public class AuthFilter implements Ordered, GlobalFilter {
-
//路径匹配器
-
private final AntPathMatcher matcher=new AntPathMatcher();
-
-
//跳过token校验的url
-
static List<String> skipUrlList= Arrays.asList("/user-server/login/*");
-
-
@Override
-
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
-
//拿到本次响应的数据,响应体
-
ServerHttpResponse response = exchange.getResponse();
-
//请求路径
-
String path = exchange.getRequest().getURI().getPath();
-
if (isSkip(path)){
-
return chain.filter(exchange);
-
}
-
//鉴权
-
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
-
if (StringUtils.isEmpty(token)){
-
return unAuth(response,"token为空");
-
}
-
//判断token是否合法
-
-
return chain.filter(exchange);
-
}
-
-
@Override
-
public int getOrder() {
-
return 0;
-
}
-
-
/**
-
* 判断path是否在跳过校验的路径中
-
* @param path
-
* @return
-
*/
-
private boolean isSkip(String path){
-
return skipUrlList.stream().anyMatch(pattern->matcher.match(pattern,path));
-
}
-
-
/**
-
* 返回鉴权不通过结果
-
* @param response
-
* @param message
-
* @return
-
*/
-
private Mono<Void> unAuth(ServerHttpResponse response,String message){
-
response.setStatusCode(HttpStatus.UNAUTHORIZED);
-
DataBuffer wrap = response.bufferFactory().wrap(buildResult(HttpStatus.UNAUTHORIZED.value(),message).getBytes(StandardCharsets.UTF_8));
-
return response.writeWith(Flux.just(wrap));
-
}
-
-
/**
-
* 包装返回体
-
* @param code
-
* @param message
-
* @return
-
*/
-
private String buildResult(int code,String message){
-
JSONObject jsonObject = new JSONObject();
-
jsonObject.put("code",code);
-
jsonObject.put("message",message);
-
jsonObject.put("data",null);
-
return jsonObject.toString(SerializerFeature.WriteMapNullValue);
-
}
-
}
6:限流ratelimiter
防止一大批请求进来,出现服务宕机;防止恶意攻击
6.1:常见的限流算法:
计数器算法
漏桶算法
令牌桶算法:一定速率产生令牌,拿到才可以访问,桶有最大阈值,装满就不能生产了,适用于突发流量;假设桶最大1000个令牌,同时来了1001个,另外一个只能放弃或者等待
6.2:限流实现
1:gateway可以结合sentine组件实现限流
2:也可以通过KeyResolver类来结合Redis实现令牌桶限流
3:Guava ReteLimiter限流
使用第二种来实现限流,创建限流类,用于说明根据什么规则进行限流,比如根据ip进行限流,设置一个ip每秒只能访问3次
依赖:
-
<dependency>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
-
</dependency>
yml配置文件:
-
spring:
-
redis:
-
host: localhost
-
port: 6379
-
-
-
- id: order-server-limit
-
uri: lb://order-server
-
predicates:
-
- Path=/order-lb-limit/**
-
filters:
-
#去除路径前缀的个数
-
- StripPrefix=1
-
- name: RequestRateLimiter #过滤器工厂RequestRateLimiter
-
args:
-
#通过SPEL表达式来指定使用哪一个KeyResolver
-
key-resolver: '#{@ipKeyResolver}'
-
#一共3个牌子,每秒发1个,领到牌子请求才能正常转发到服务
-
redis-rate-limiter.replenishRate: 1 #生产令牌的速率,每秒允许访问个数
-
redis-rate-limiter.burstCapacity: 3 #桶大小,即峰值流量来临时最大访问数
config配置类:
-
-
public class RateLimitConfig {
-
-
-
public KeyResolver ipKeyResolver(){
-
return new KeyResolver() {
-
-
public Mono<String> resolve(ServerWebExchange exchange) {
-
return Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
-
}
-
};//根据ip地址来限流
-
}
-
}
6.3:自定义限流异常返回数据:
继承RequestRateLimiterGatewayFilterFactory,实现apply方法,配置文件修改filters.name为新工厂名称(剔除后缀GatewayFilterFactory)
在过滤器中统一返回结果单独提取出来,定义在工具类中:
-
package com.example.Util;
-
-
import com.alibaba.fastjson.JSONObject;
-
import com.alibaba.fastjson.serializer.SerializerFeature;
-
-
/**
-
* @author lcy
-
* @version 1.0
-
* @date 2023/3/9 16:41
-
*/
-
public class AuthResultUtil {
-
/**
-
* 包装返回体
-
* @param code
-
* @param message
-
* @return
-
*/
-
public static String buildResult(int code,String message){
-
JSONObject jsonObject = new JSONObject();
-
jsonObject.put("code",code);
-
jsonObject.put("message",message);
-
jsonObject.put("data",null);
-
return jsonObject.toString(SerializerFeature.WriteMapNullValue);
-
}
-
}
接下来自定义异常限流:
-
package com.example.config;
-
-
import org.springframework.cloud.gateway.filter.GatewayFilter;
-
import org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory;
-
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
-
import org.springframework.cloud.gateway.route.Route;
-
import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
-
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
-
import org.springframework.core.io.buffer.DataBuffer;
-
import org.springframework.http.HttpStatus;
-
import org.springframework.http.server.reactive.ServerHttpResponse;
-
import reactor.core.publisher.Mono;
-
import org.springframework.stereotype.Component;
-
-
import java.nio.charset.StandardCharsets;
-
import java.util.Map;
-
-
import static com.example.Util.AuthResultUtil.buildResult;
-
-
/**
-
* @author lcy
-
* @version 1.0
-
* @date 2023/3/9 16:28
-
*/@Component
-
public class GatewayRequestRateLimiterGatewayFilterFactory extends RequestRateLimiterGatewayFilterFactory {
-
private final RateLimiter defaultRateLimiter;
-
private final KeyResolver defaultKeyResolver;
-
-
public GatewayRequestRateLimiterGatewayFilterFactory(RateLimiter defaultRateLimiter, KeyResolver defaultKeyResolver) {
-
super(defaultRateLimiter, defaultKeyResolver);
-
this.defaultRateLimiter = defaultRateLimiter;
-
this.defaultKeyResolver = defaultKeyResolver;
-
}
-
-
@Override
-
public GatewayFilter apply(Config config) {
-
KeyResolver resolver = getOrDefault(config.getKeyResolver(), defaultKeyResolver);
-
RateLimiter<Object> limiter = getOrDefault(config.getRateLimiter(), defaultRateLimiter);
-
return (exchange, chain) -> resolver.resolve(exchange).flatMap(key -> {
-
String routeId = config.getRouteId();
-
if (routeId == null) {
-
Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
-
routeId = route.getId();
-
}
-
return limiter.isAllowed(routeId, key).flatMap(response -> {
-
for (Map.Entry<String, String> header : response.getHeaders().entrySet()) {
-
exchange.getResponse().getHeaders().add(header.getKey(), header.getValue());
-
}
-
if (response.isAllowed()) {
-
return chain.filter(exchange);
-
}
-
ServerHttpResponse httpResponse = exchange.getResponse();
-
// 设置响应code为500
-
httpResponse.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
-
if (!httpResponse.getHeaders().containsKey("Content-Type")) {
-
httpResponse.getHeaders().add("Content-Type", "application/json");
-
}
-
// 自定义返回信息
-
DataBuffer buffer = httpResponse.bufferFactory().wrap(buildResult(HttpStatus.INTERNAL_SERVER_ERROR.value(), "请求繁忙,请稍后重试").getBytes(StandardCharsets.UTF_8));
-
return httpResponse.writeWith(Mono.just(buffer));
-
});
-
});
-
-
}
-
-
/**
-
* 设置默认值
-
* @param configValue
-
* @param defaultValue
-
* @param <T>
-
* @return
-
*/
-
private <T> T getOrDefault(T configValue, T defaultValue){
-
return configValue != null ? configValue : defaultValue;
-
}
-
}
更改yml文件name
7:灰度发布:
体验服和正式服的差别,小部分体验服,测试bug。权重来声明
-
# 权重,灰度发布
-
- id: order1
-
uri: http://localhost:8080
-
predicates:
-
- Path=/order/**
-
# 分组名,权重
-
- Weight=order,80
-
filters:
-
- StripPrefix=2
-
- id: order2
-
uri: http://localhost:8081
-
predicates:
-
- Path=/order/sss/**
-
- Weight=order,20
-
filters:
-
- StripPrefix=2
百分之二十体验服
个人笔记-----方便温习
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhfjbjef
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
photoshop蒙版画笔没反应怎么办
PHP中文网 06-24