入门业务
第一步:创建sca-gateway模块,pom.xml文件中加入依赖
<!--添加api网关依赖,添加此依赖以后不要再添加spring-cloud-starter-web依赖
否则有冲突-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--添加服务发现依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--添加限流依赖(以下两个)-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
第二步:创建application.yml,添加相关配置
server:
port: 9000
spring:
application:
name: sca-gateway
cloud:
nacos:
discovery: #服务注册和发现,若不需要注册可不写
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8180
eager: true #true表示服务启动时,即可在sentinel控制台看到服务信息
gateway: #网关配置
routes: # 路由配置(routes下可以有多个路由,用id做唯一标识)
- id: route01
#uri: http://localhost:8081/ #请求转发路径
uri: lb://sca-provider #lb表示负载均衡,sca-provider为服务注册中心的一个服务名
predicates: #谓词判断逻辑(定义请求转发条件,谓词所有条件都满足才会执行请求转发)
- Path=/nacos/provider/echo/**
# - After=2021-12-10T23:59:59.789+08:00[Asia/Shanghai]
# - Header=X-Request-Id, \d+
# - Method=Get
# - Query=token, \d+
filters: #局部过滤器,针对于当前路由进行设计,谓词逻辑返回值都为true则执行这里filter
- StripPrefix=1 #去除前缀过滤器,这里1表示去掉Path中第一层目录
#流程分析: RoutePredicateHandlerMapping-->PathRoutePredicateFactory-->Xxx WebHandler-->Filters()
其中:路由(Route) 是 gateway 中最基本的组件之一,表示一个具体的路由信息载体。主要定义了下面的几个信息:
- id,路由标识符,区别于其他 Route。
- uri,路由指向的目的地 uri,即客户端请求最终被转发到的微服务。
- predicate,断言(谓词)的作用是进行条件判断,只有断言都返回真,才会执行路由。
- filter,过滤器用于修改请求和响应信息。
第三步:创建项目启动类
第四步:启动sca-provider、sca-gateway服务进行访问测试
(localhost:9000/nacos/provider/echo/***)
负载均衡
第一步:添加服务发现依赖
第二步:修改其配置文件
lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略。同时建议开发阶段打开gateway日志,代码如下:
logging:
level:
org.springframework.cloud.gateway: debug
第三步:启动8081、8082的provider端口服务,进行访问测试,并反复刷新分析
全局过滤器设计及实现
package com.jt;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 自定义全局过滤器
*/
//@Component 将过滤器交给spring管理,实验过后,注释掉,否则后续每次请求,都需要传指定参数
public class AuthGlobalFilter implements GlobalFilter, Ordered {
/**Ordered 优先级,值越小,优先级越高
* 请求过滤方法
* @param exchange 基于此对象获取请求和响应对象
* @param chain 过滤链
* @return 这里的返回值代表一个Publisher对象(消息的发布者),
* 可以由此对象帮你进行请求传递。
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
//1.获取请求对象
ServerHttpRequest request = exchange.getRequest();
//2.获取请求数据
String username=
request.getQueryParams().getFirst("username");
//3.进行认证分析
//3.1认证失败则拒绝请求继续传递
if(!"admin".equals(username)){
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//3.2认证成功继续处理(传递)
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
限流设计
还有最后
第一步:添加依赖
第二步:添加sentinel及路由规则
第三步:启动网关项目,检测sentinel控制台的网关菜单
启动时,添加sentinel的jvm参数,通过此菜单可以让网关服务在sentinel控制台显示不一样的菜单,代码如下。
-Dcsp.sentinel.app.type=1
第五步:通过url进行访问检测是否实现了限流操作
限流结果自定义
package com.jt;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.fastjson.JSON;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
网关流控的默认实现是DefaultBlockRequestHandler
@Configuration
public class GatewayConfig {
public GatewayConfig(){
GatewayCallbackManager.setBlockHandler(new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
Map<String,Object> map=new HashMap<>();
map.put("status", 429);
map.put("message","blocked by sentinel ....");
String jsonStr= JSON.toJSONString(map);
return ServerResponse.ok()
.body(Mono.just(jsonStr),
String.class);
}
});
}
}
其中,Mono 是一个发出(emit)0-1个元素的Publisher对象