12.1 Geteway 路由网关
说到路由,想必各位一定最先想到的就是 家里的路由器了,那么我们 家里的路由器 充当的是 一个什么角色呢?
我们知道,如果我们需要连接互联网,那么就需要将手机或者电脑连接到路由器上。而路由器则连接光猫。光猫再通过光纤连接到互联网。也就是说,互联网方向发送过来的数据,需要经过路由器才能到达我们的设备,而路由器充当的就是 数据包中转站,所有的局域网设备都无法直接与互联网连接,而是需要经过路由器 进行 中转,我们一般说 路由器下的网络是内网,而互联网那一端是外网。
而我们的微服务其实也该如此,我们 并不是所有的微服务都需要直接暴露给外部调用!这时候我们可以用到路由机制
,添加一层防护,让所有的请求完全通过 路由 来转发到 各个微服务
,并且转发给 多个 相同微服务实例 也可以实现 负载均衡
!
- **添加 两个 依赖 **
<?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>springcloud</artifactId>
<groupId>top.muquanyu</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-geteaway-9527</artifactId>
<dependencies>
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</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>
- 配置
application
无负载均衡
server:
port: 9527
spring:
application:
name: springcloud-zuul
cloud:
gateway:
# 配置路由,注意这里是个列表,每一项都包含了很多信息
routes:
- id: gateaway_routh # 路由名称,没有具体的规范,但是要求 统一。
uri: http://localhost:8001 # 路由的地址,这个地址肯定是提供服务的那个地址,lb 表示使用负载均衡到微服务,也可以使用 http 正常转发
predicates: # 路由规则,断言什么请求会被路由
- Path=/dept/** # 只要是访问的这个路径,一律都被路由到上面指定的服务
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
instance:
instance-id: zuul-9527 # 这个更改的就是 Status 的描述
perfer-ip-address: true
# info 配置
info:
app.name: MQy-springcloud
company.name: MQy
package top.muquanyu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class GateawayApplicaition_9527 {
public static void main(String[] args) {
SpringApplication.run(GateawayApplicaition_9527.class,args);
}
}
这个 时候 你就可以用 9527 来 访问 8001 的 服务了。
9527套在最外面,有一个地址 localhost:8001 能被访问到,predicates 断言判断 8001下面有一个 /dept/gett/{id} 地址匹配,如果路由上 predicates 为true 访问成功,false 访问失败。
12.2 简单的动态配置
上面访问的路由地址我们是写死的,在微服务架构中,微服务提供者不可能只有一台服务器,就需要动态路由。
之前80客户端发送请求访问8001/8002,通过ribbon负载均衡,将请求分散,现在服务提供者如果是多台,就需要将ribbon替换为gateway,只暴露gateway,客户端请求统一发到gateway,gateway将请求转发给8001/8002。
默认情况下Gateway会根据注册中心注册的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能。
我们先把 所有 的数据库的 这个 序号 都改一下。等一会儿 测试的时候,避免 查看的序号 不一致,体现不出 负载均衡。
- 修改
application
那个配置。lb://springcloud-provider-dept
server:
port: 9527
spring:
application:
name: springcloud-gateaway
cloud:
gateway:
# 配置路由,注意这里是个列表,每一项都包含了很多信息
routes:
- id: gateaway_routh # 路由名称
# SPRINGCLOUD-PROVIDER-DEPT 一定要 转为 小写
# 这次的 uri 就必须是 服务的 Application 名字了
uri: lb://springcloud-provider-dept # 路由的地址,lb 表示使用负载均衡到微服务,也可以使用 http 正常转发
predicates: # 路由规则,断言什么请求会被路由
- Path=/dept/** # 只要是访问的这个路径,一律都被路由到上面指定的服务
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
register-with-eureka: true
fetch-registry: true
instance:
instance-id: gateaway-9527 # 这个更改的就是 Status 的描述
perfer-ip-address: true
# info 配置
info:
app.name: MQy-springcloud
company.name: MQy
- 别忘了 声明 自己 是一个
Eureka
客户端
package top.muquanyu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class GateawayApplicaition_9527 {
public static void main(String[] args) {
SpringApplication.run(GateawayApplicaition_9527.class,args);
}
}
我们发现 确实 用 9527 这个网关 是可以 访问到 服务的。
12.3 路由过滤器
- 尝试 添加 头信息
server:
port: 9527
spring:
application:
name: springcloud-gateaway
cloud:
gateway:
# 配置路由,注意这里是个列表,每一项都包含了很多信息
routes:
- id: gateaway_routh # 路由名称
# SPRINGCLOUD-PROVIDER-DEPT 一定要 转为 小写
uri: lb://springcloud-provider-dept # 路由的地址,lb 表示使用负载均衡到微服务,也可以使用 http 正常转发
predicates: # 路由规则,断言什么请求会被路由
- Path=/dept/** # 只要是访问的这个路径,一律都被路由到上面指定的服务
filters: # 添加过滤器
- AddRequestHeader = Test,HelloWorld!
# 添加 请求的 头信息
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
register-with-eureka: true
fetch-registry: true
instance:
instance-id: gateaway-9527 # 这个更改的就是 Status 的描述
perfer-ip-address: true
# info 配置
info:
app.name: MQy-springcloud
company.name: MQy
然后 我们 修改 一下 Controller 那个请求方法
让它 能够拿到 头信息。
@GetMapping ("/dept/get/{id}")
public Dept get(@PathVariable("id") Long id, HttpServletRequest req){
System.out.println(req.getHeader("Test"));
return deptService.queryByID(id);
}
除了 针对某一个路由配置过滤器之外,我们也可以自定义全局过滤器,它可以作用于全局。但是 我们需要 通过 代码的方式进行编写,比如我们要实现拦截没有携带指定请求参数的请求!这样我们就可以通过 指定的一些参数信息 来说你是否可以通行!这也是 阻断我们抓包的一种方式。
- 新建一个
TestFilter
类
package top.muquanyu.springcloud.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.List;
@Component
public class TestFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 首先 获取到 ServerHttpRequest 对象,注意 不是 HttpServletRequest
ServerHttpRequest request = exchange.getRequest();
// 打印一下 所有的请求参数
System.out.println(request.getQueryParams());
// 判断 是否包含 test 参数,且参数值 为 1
List<String> value = request.getQueryParams().get("test");
if(value != null && value.contains("1")){
// 将 ServerWebExchange 向过滤链的下一级传递
return chain.filter(exchange);
// 跟 Servlet 原生态的 Filter 很相似
}else{
// 直接 在这里 不再向下传递,然后 返回响应即可
return exchange.getResponse().setComplete();
}
}
}
这个的意思是,我们的 URL 请求,必须 携带 参数 test=1
如果没有,那么 请求失败,因为不会给你放行!
你看,你没有携带 参数 test = 1
它就不会给你 放行。
你看,这样 就发行了。
12.4 过滤器优先级问题
过滤器是 可以 存在很多个的!那么我们 如何来决定 过滤器执行的顺序呢?
如果 是 全局过滤器的话,我们只需要 实现 Ordered 接口
然后 重写 getOrder
让其 返回 一个 优先级值 ,就代表 这个 全局过滤器的 优先级了。值越小,越优先!
// 优先级设为 0,也就是 目前最高的
@Override
public int getOrder() {
return 0;
}
如果是 指定某个 服务的 过滤器,那么 它的 优先级 是 顺次 从上到下的。排在上面的肯定优先级 比 排在 下面的高。
那么当 Order 值 一样的时候呢??答:全局的 优先于 局部/单独 的。
- 你比如说,我们现在 这个全局的过滤器 优先级是 0,那么 它能获取到 Header Test 那个吗?答案是 肯定不能的,因为 优先级 是要比 Test 那个高的。
- 当你把全局的 优先级 设为 2,那么 肯定就能拿到 这个 Header 了。