前言
这只是我的学习笔记,可能有错误,希望谅解!!!
和eureka 集成
server:
port: 9105
spring:
application:
name: auth-central-try-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #是否开启服务注册与发现代理,默认为false
lower-case-service-id: true #是否开启小写访问,默认为大写
eureka:
client:
register-with-eureka: true # 作为客户端需要注册到Eureka
fetch-registry: true # 作为客户端需要从Eureka获取注册信息
service-url: # 客户端注册地址
defaultZone: http://127.0.0.1:8888/eureka
instance:
# 优先使用该服务的IP地址注册到Eureka,在生产环境建议改为true
prefer-ip-address: true
谓词(predicate)
是一个java8接口,在gateway中的输入类型是ServerWebExchange.这允许匹配http请求中的任何内容,例如header parameter .
谓词工厂实现类所在包:org.springframework.cloud.gateway.handler.pridicate
1.Path
spring:
application:
name: mima-cloud-gateway
cloud:
gateway:
routes:
#访问/get/123 转发给 http://127.0.0.1:9905/get/123,其中123 是参数,{segment}对应一个参数
- id: route_test_1
uri: http://127.0.0.1:9905
predicates:
- Path=/get/{segment}
#访问/get/123 转发给 http://127.0.0.1:9905/get/123
#访问/get/test/123 转发给 http://127.0.0.1:9905/get/test/123
#访问/get/test/test/123 转发给 http://127.0.0.1:9905/get/test/test/123
- id: route_test_2
uri: http://127.0.0.1:9905
predicates:
- Path=/get/**
# 负载方式,从注册中心获取
#访问/get/123 转发给 http://mima-cloud-producer/get/123
- id: route_test_3
uri: lb://mima-cloud-producer
predicates:
- Path=/get/**
2.Method
- id: route_test_4
uri: lb://mima-cloud-producer
predicates:
#访问/get/123 转发给 http://mima-cloud-producer/get/123
- Path=/get/**
#只匹配GET请求
- Method=GET
- id: route_test_5
uri: lb://mima-cloud-producer
predicates:
#只匹配POST请求
- Method=POST
3.Query
# Query 是从paramter 中匹配
- id: route_test_6
uri: lb://mima-cloud-producer
predicates:
#请求参数为id的则匹配,/get/123?id=xxx都可以匹配
- Query=id
#匹配请求参数为name值为正则kevin.* , 所以xxxx?name=kevin123123可以匹配,xxx?name=123则不匹配
- Query=name,kevin.*
4.Cookie
#在cookie中匹配
- id: cookie_route
uri: lb://mima-cloud-producer
predicates:
#Cookie匹配key=name,value=正则
- Cookie= name,kevin.*
5.Header
#在header中匹配
- id: header_route
uri: lb://mima-cloud-producer
predicates:
#Header匹配key=reqId,value=正则
- Header= reqId,9090\d+
#匹配host(原来host是通过就是header中的一个参数,难怪nginx可以通过server_name去匹配,与此应该同理)
- id: host_route
uri: lb://mima-cloud-producer
predicates:
#请求来源地址,Heder中的Host进行匹配
#- Host= www.mkevin.com
#- Host= abc.mkevin.com,def.mkevin.com
- Host= **.mkevin.com,**.mkevin.org
6.RemoteAddr
#远程IP地址匹配
- id: host_route
uri: lb://mima-cloud-producer
predicates:
#远程IP地址匹配, IPV4/子网掩码 IPV6/子网掩码
- RemoteAddr= 10.48.247.1/24
7.Weight
- id: weight_route_1
uri: http://127.0.0.1:9905
predicates:
# 相同的组,权重为2,20%的交易转发到此处
- Weight= group1,2
- id: weight_route_2
uri: http://127.0.0.1:9906
predicates:
# 相同的组,权重为8,80%的交易转发到此处
- Weight= group1,8
8.After
#在指定时间之后才能访问
- id: after_route
uri: lb://mima-cloud-producer
predicates:
- After= 2020-01-15T17:52:00.789+08:00[Asia/Shanghai]
9.Before
#与after同理
- id: before_route
uri: lb://mima-cloud-producer
predicates:
- Before= 2020-01-15T17:57:00.789+08:00[Asia/Shanghai]
10.Before
#与after同理
- id: between_route
uri: lb://mima-cloud-producer
predicates:
#在这个时间区间内可以访问,否则404
- Between= 2020-01-16T10:33:00.789+08:00[Asia/Shanghai],2020-01-16T10:35:00.789+08:00[Asia/Shanghai]
11.自定义谓词
自定义谓词功能: url张红必须拥有参数userName=xxx,才可以访问 ,比如:http://192.168.31.80:8801/getheader?userName=kevin
配置文件
gateway:
routes:
#自定义谓词UserNameCheckRoutePredicateFactory的配置使用
- id: Auth_route
uri: lb://mima-cloud-producer
order: 1
predicates:
- Path=/**
# name配置为UserNameCheckRoutePredicateFactory类前缀UserNameCheck
# 只有访问http://192.168.31.80:8801/getheader?userName=kevin 才可以访问,否则为404
# 必须携带userName请求参数,并且值为kevin
- name: UserNameCheck
args:
name: kevin
自定义谓词工场类
/**
* 自定义谓词工场类
* 1、继承AbstractRoutePredicateFactory类,
* 2、重写apply方法
* 3、apply方法的参数是自定义的配置类,可以在apply方法中直接获取使用配置参数。
* 4、类的命名需要以RoutePredicateFactory结尾
* 本类例子:检查请求参数中的userName是否与配置的数据相同,如果相同则允许访问,否则不允许访问
*/
@Component
public class UserNameCheckRoutePredicateFactory extends AbstractRoutePredicateFactory<UserNameCheckRoutePredicateFactory.Config> {
public UserNameCheckRoutePredicateFactory() {
super(Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(UserNameCheckRoutePredicateFactory.Config config) {
// 写法1
/*return new Predicate<ServerWebExchange>() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
String userName = serverWebExchange.getRequest().getQueryParams().getFirst("userName");
if(StringUtils.isBlank(userName)){
return false;
}
//检查请求参数中的userName是否与配置的数据相同,如果相同则允许访问,否则不允许访问
if(userName.equals(config.getName())){
return true;
}
return false;
}
};*/
// 写法2
return serverWebExchange -> {
String userName = serverWebExchange.getRequest().getQueryParams().getFirst("userName");
if(StringUtils.isBlank(userName)){
return false;
}
//检查请求参数中的userName是否与配置的数据相同,如果相同则允许访问,否则不允许访问
if(userName.equals(config.getName())){
return true;
}
return false;
};
}
@Validated
public static class Config{
@NotEmpty
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
}
}
过滤器(filter)
在demo项目中有三个微服务 mima-cloud-gateway,mima-cloud-eureka,mima-cloud-producer
mima-cloud-gateway:loaclhost:8801
其中 mima-cloud-producer 中的代码
@RestController
public class ProducerController {
//打印header中的key和value
@GetMapping("/getheader")
public Map<String, String> getheader(HttpServletRequest request) {
Map<String,String> map = new HashMap<String,String>();
Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()){
String name = headerNames.nextElement();
String value = request.getHeader(name);
map.put(name,value);
}
return map;
}
//打印header中的key和value
@GetMapping("/getheader/{param}")
public Map<String, String> getheader2(HttpServletRequest request) {
Map<String,String> map = new HashMap<String,String>();
Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()){
String name = headerNames.nextElement();
String value = request.getHeader(name);
map.put(name,value);
}
return map;
}
}
1.操作 request header
1.1添加请求头,静态数据
server:
port: 9105
spring:
application:
name: auth-central-try-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #是否开启服务注册与发现代理,默认为false
lower-case-service-id: true #是否开启小写访问,默认为大写
routes:
- id: add_request_header_route1
uri: lb://auth-central-try-producer
predicates:
- Path= /getheader
filters:
- AddRequestHeader= X-Request-id, 8888
- AddRequestHeader= X-Request-author, jack
请求http://loaclhost:8801/getheader
1.2.动态添加header
- id: add_request_header_route2
uri: lb://auth-central-try-producer
predicates:
- Path= /getheader/{seq}
filters:
# 添加请求头,动态参数
- AddRequestHeader= X-Request-id, 8888-{seq}
- AddRequestHeader= X-Request-author, jack-{seq}
请求http://loaclhost:8801/getheader/666
1.3 set header
filters:
# 设置请求头,静态参数,没有则新增,如果有则修改
- SetRequestHeader= X-Request-a, abc
- SetRequestHeader= X-Request-b, def
# 添加请求头,动态参数,没有则新增,如果有则修改
- SetRequestHeader= X-Request-id, 999999-{seq}
- SetRequestHeader= X-Request-author, kevin-{seq}
1.4 remove header
filters:
# 移除请求头userName参数
- RemoveRequestHeader=reqId
# 移除请求头userName参数
- RemoveRequestHeader=userName
2.操作 response parameter
同理1.操作 request header
3. 操作 response header
同理 1.操作 request header
3.1.重写 response header
- id: RewriteResponseHeader_route
uri: lb://mima-cloud-producer
predicates:
- Path=/getheaderdupe
filters:
# 重写应答头,需要3个参数逗号分隔,分别为:参数名,要匹配的字符串正则, 需要替换为的字符
# 如果需要使用参数${},则必须写为$\{},否则与yaml冲突
# 例如:rep-url=/42?user=ford&password=omg!what&flag=true
# 下面的配置会替换为:/42?user=ford&password=***&flag=true
- RewriteResponseHeader=rep-url, password=[^&]+, password=***
3.2 去除重复 response header
- id: dedupe_response_header_route1
uri: lb://mima-cloud-producer
predicates:
- Path= /getheaderdupe
filters:
- AddResponseHeader= rep-id, 999999
- AddResponseHeader= rep-author, kevin
# 去除重复的应答头,多项使用空格分隔,逗号之后为去重策略:RETAIN_FIRST保留第一个(默认),RETAIN_LAST(保留最后一个),RETAIN_UNIQUE(保留唯一)
- DedupeResponseHeader= rep-id,RETAIN_FIRST
- DedupeResponseHeader= rep-author,RETAIN_LAST
3.3 RewriteLocationResponseHeader
很遗憾,没有搞懂这个过滤器是干嘛的…
4.Path 过滤器
4.1 PrefixPath
- id: PrefixPath_route
uri: lb://mima-cloud-producer
predicates:
- Path=/**
filters:
# 例如请求/get/123接口,实际上转发到/prefix/get/123接口
- PrefixPath=/prefix
4.2 RedirectTo
- Path=/xx 匹配到的url,跳转到指定url,并指定状态码为3xx
#300 Multiple Choices 多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择
#301 Moved Permanently 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
#302 Found 临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
#303 See Other 查看其它地址。与301类似。使用GET和POST请求查看
#304 Not Modified 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
#305 Use Proxy 使用代理。所请求的资源必须通过代理访问
#306 Unused 已经被废弃的HTTP状态码
#307 Temporary Redirect 临时重定向。与302类似。使用GET请求重定向
- id: RedirectTo_route
uri: lb://mima-cloud-producer
predicates:
- Path=/**
filters:
# 跳转到www.baidu.com,并指定状态码为302
- RedirectTo=302,http://www.baidu.com
# 则跳转到www.taobao.com,并指定状态码为301
- RedirectTo=301,http://www.taobao.com
4.3 SetStatus
只是修改转态码
- id: SetStatus_route1
uri: lb://mima-cloud-producer
predicates:
- Path=/get/{var}
filters:
# 修改状态码,可以org.springframework.http.HttpStatus枚举,也可以直接写状态码
- SetStatus=NOT_FOUND
#- SetStatus=200
4.4 RewritePath
访问/api/getheader 实际访问/getheader
支持正则匹配
- id: RewritePath_route
uri: lb://mima-cloud-producer
predicates:
- Path=/**
filters:
# 访问/api/getheader 实际访问/getheader
# 转发目标地址必须写/,否则启动报错 The path does not have a leading slash. 路径没有前导斜杠
- RewritePath=/api/getheader,/getheader
# http://192.168.31.80:8801/www/get/user/12123 实际访问/prefix/get/user/12323
# http://192.168.31.80:8801/www/getheader 实际访问/prefix/getheader
# 必须写为$\这种格式,否则变为YAML的变量
- RewritePath=/www/(?<segment>/?.*),/prefix/$\{segment}
4.5 SetPath
例如:请求/prefix/get/user/123123, 真实请求修改为:/get/user/123123
- id: SetPath_route1
uri: lb://mima-cloud-producer
predicates:
- Path=/prefix/{var1}/{var2}/{var3}
filters:
#允许使用请求路径的模板段来确定真实请求路径
#使用了Spring框架中的URI模板。允许多个匹配段
#例如:请求/prefix/get/user/123123, 真实请求修改为:/get/user/123123
- SetPath=/{var1}/{var2}/{var3}
这样也可以
- id: SetPath_route2
uri: lb://mima-cloud-producer
predicates:
- Path=/prefix/{var1}/{var2}/{var3}
filters:
# 允许使用请求路径的模板段来确定真实请求路径
# 使用了Spring框架中的URI模板。允许多个匹配段
# 例如:请求/prefix/get/user/123123, 真实请求修改为:/get/123123
- SetPath=/{var1}/{var3}
4.6 StripPrefix
- id: StripPrefix_route1
uri: lb://mima-cloud-producer
predicates:
- Path=/**
filters:
# 只有1个参数,从前向后去掉几个部件,请求/api/get/123, 实际请求/get/123
- StripPrefix=1
# 从前向后去掉2个部件,请求/api/get/123, 实际请求/123
#- StripPrefix=2
5.其他过滤器
5.1 Retry
- id: Retry_route1
uri: lb://mima-cloud-producer
predicates:
- Path=/testRetry
filters:
# 过滤器名称
- name: Retry
#参数
args:
# 重试次数
retries: 3
# 什么样的状态码进行重试
# org.springframework.http.HttpStatus.Series枚举,与statuses至少配置一个
series:
- SERVER_ERROR
# 什么样的状态码重试
statuses:
- BAD_GATEWAY
- METHOD_NOT_ALLOWED
# 什么样的方法需要重试
methods:
- GET
- POST
# 什么样的异常需要重试
exceptions:
- java.io.IOException
- java.util.concurrent.TimeoutException
# 退避配置,设置重试间隔,例如每隔多长时间重试一次,第一次立即重试,第二次10秒,第三次30秒
backoff:
firstBackoff: 10ms
maxBackoff: 50ms
factor: 2
basedOnPreviousValue: false
5.2 RequestSize
控制请求的大小
- id: RequestSize_route1
uri: lb://mima-cloud-producer
predicates:
- Path=/testRequestSize
filters:
# 过滤器名称
- name: RequestSize
#参数
args:
# 控制请求的大小,请求过大会被拦截,返回413 Request Entity Too Large
# 请求最大,5000000 Bytes,约5M
# 设置为1k,请求过大时提示: Request size is larger than permissible limit. Request size is 385.4 kB where permissible limit is 1.0 kB
maxSize: 1000
5.3 …
6.熔断过滤器
7.Gateway 的Redis 限流过滤器
8.自定义过滤器
自定义过滤器功能:添加request header
8.1 配置文集
gateway:
routes:
#自定义过滤器
- id: customer_filter_route
uri: lb://mima-cloud-producer
order: 1
predicates:
- Path=/**
filters:
- name: MyAddRequestHeader
args:
name: req-kevin-header
value: req-yin.hl
8.2 filter 类
/**
* 自定义过滤器,添加请求头
* 自定义Spring Cloud Gateway过滤器工厂需要继承AbstractGatewayFilterFactory类,
* 重写apply方法的逻辑。命名需要以GatewayFilterFactory结尾,
* MyAddRequestHeaderGatewayFilterFactory,那么在使用的时候MyAddRequestHeader就是这个过滤器工厂的名称
*/
@Component
public class MyAddRequestHeaderGatewayFilterFactory extends AbstractGatewayFilterFactory<MyAddRequestHeaderGatewayFilterFactory.Config> {
public MyAddRequestHeaderGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
//写法1
/*return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("MyAddRequestHeaderGatewayFilterFactory.apply is run....");
// exchange.getRequest().mutate() 目的是转化为装饰类,否则Request为只读的,不能操作
// header方法用来设置header的值
ServerHttpRequest request = exchange.getRequest().mutate().header(config.getName(), config.getValue()).build();
// 将request包裹继续向下传递
return chain.filter(exchange.mutate().request(request).build());
}
};*/
//写法2
return (exchange, chain) -> {
System.out.println("MyAddRequestHeaderGatewayFilterFactory.apply is run....");
// exchange.getRequest().mutate() 目的是转化为装饰类,否则Request为只读的,不能操作
// header方法用来设置header的值
ServerHttpRequest request = exchange.getRequest().mutate().header(config.getName(), config.getValue()).build();
// 将request包裹继续向下传递
return chain.filter(exchange.mutate().request(request).build());
};
}
@Validated
public static class Config {
@NotEmpty
private String name;
@NotEmpty
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue(){
return value;
}
public void setValue(String value){
this.value=value;
}
}
}
8.3 自定义 response header 过滤器
- name: MyAddResponseHeader
args:
name: rep-kevin-header
value: rep-yin.hl
/**
* 自定义过滤器添加应答头
* 通过AbstractGatewayFilterFactory实现
*/
@Component
public class MyAddResponseHeaderGatewayFilterFactory extends AbstractGatewayFilterFactory<MyAddResponseHeaderGatewayFilterFactory.Config> {
public MyAddResponseHeaderGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
//写法2
return (exchange, chain) -> {
System.out.println("MyAddResponseHeaderGatewayFilterFactory.apply is run....");
exchange.getResponse().getHeaders().set(config.getName(),config.getValue());
// 继续向上传递
return chain.filter(exchange);
};
}
@Validated
public static class Config {
@NotEmpty
private String name;
@NotEmpty
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue(){
return value;
}
public void setValue(String value){
this.value=value;
}
}
}
8.4 继承AbstractNameValueGatewayFilterFactory
如果继承这个AbstractNameValueGatewayFilterFactory,就可以直接 :
- MyAddRequestHeader2=req-a,1
- MyAddRequestHeader2=req-b,2
@Component
public class MyAddRequestHeader2GatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
//重点是可以直接 config.getName(),config.getValue()
@Override
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> {
System.out.println("MyAddRequestHeader2GatewayFilterFactory.apply is run....");
// exchange.getRequest().mutate() 目的是转化为装饰类,否则Request为只读的,不能操作
// header方法用来设置header的值
ServerHttpRequest request = exchange.getRequest().mutate().header(config.getName(), config.getValue()).build();
// 将request包裹继续向下传递
return chain.filter(exchange.mutate().request(request).build());
};
}
}
8.5 自定义全局过滤器
Gateway 跨域配置
1.配置文件
gateway:
# YML跨域配置: (注意:yml配置方式与com.mkevin.gateway.config.CorsConfig类选用一种使用)
# 加载入口:org.springframework.cloud.gateway.config.GatewayAutoConfiguration.globalCorsProperties
# 对应属性类:org.springframework.cloud.gateway.config.GlobalCorsProperties
globalcors:
corsConfigurations:
#允许跨域的请求路径
'[/**]':
#允许的来源
allowedOrigins: "*"
#允许的方法
allowedMethods:
- GET
- POST
#是否允许携带cookie
allowCredentials: true
#允许http请求携带的header
allowedHeaders:
- Content-Type
#response应答可以暴露的header
exposedHeaders:
- Content-Type
#预检命令缓存时常,单位秒
maxAge: 18000
2. 配置类(推荐,比较灵活)
/**
* 跨域配置
* 跨域定义:
* http://www.mkevin.com/123132 ajax -> http://www.mkevin.com/123132/test X
* http://abd.mkevin.com/123132 ajax -> http://def.mkevin.com/123132/test Y
* http://abd.mkevin.com:8801/123132 ajax -> http://abd.mkevin.com:9901/123132/test Y
* http://abd.mkevin.com:8801/123132 ajax -> https://abd.mkevin.com:8801/123132/test Y
*/
@Configuration
public class CorsConfig {
private static final String MAX_AGE = "18000L";
@Bean
public WebFilter corsFilter() {
return (ServerWebExchange ctx, WebFilterChain chain) -> {
System.out.println("corsFilter... run");
ServerHttpRequest request = ctx.getRequest();
if (!CorsUtils.isCorsRequest(request)) {//判断是否是跨域
return chain.filter(ctx);
}
//下面是常规配置了
HttpHeaders requestHeaders = request.getHeaders();
ServerHttpResponse response = ctx.getResponse();
HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
HttpHeaders headers = response.getHeaders();
//允许那些原域访问
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
//允许的header
headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());
if (requestMethod != null) {
//允许的方法
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
}
//是否允许携带cookie
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
//response应答可以暴露的header
headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
//预检命令缓存时常
headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
//OPTION请求处理
if (request.getMethod() == HttpMethod.OPTIONS) {
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
return chain.filter(ctx);
};
}
}
Gateway 超时时间设置
gateway:
httpclient:
# 连接超时,毫秒
connect-timeout: 1000
# 应答超时:java.time.Duration
response-timeout: 5s
Gateway 内置API
/actuator/gateway/routes/{id}, methods=[ DELETE], 删除单个路由
/actuator/gateway/routes/{id}, methods=[ POST], 新增单个路由
/actuator/gateway/routes/{id}, methods=[ GET], 查看单个路 由。
/actuator/gateway/routes, methods=[ GET], 获取路由列表
/actuator/gateway/refresh, methods=[ POST], 路由刷新
/actuator/gateway/globalfilters, methods=[ GET], 获取全局过滤器列表
/actuator/gateway/routefilters, methods=[ GET], 路由过滤器工厂列表
…未完待续…