官网:Spring Cloud Gateway

中文文档:Spring Cloud Gateway 2.1.0 中文官网文档 - 腾讯云开发者社区-腾讯云

一、网关介绍:

网关就是当前微服务的统一入口 通常在微服务项目中,只有网关项目是暴露在网络里的,其他服务一般都是在内网里, 用户访问网关,网关根据访问的路径,来进行路由 Gateway 网关也是微服务的一部分,需要将项目注册到Nacos 因为某一个服务可能存在多台服务器,所以也需要负载均衡依赖

二、案例:

2.1.依赖项  

org.springframework.cloud

spring-cloud-starter-gateway

org.springframework.cloud

spring-cloud-starter-loadbalancer

com.alibaba.cloud

spring-cloud-starter-alibaba-nacos-discovery

 2.2.yml文件配置的方式实现路由

纯yml文件配置的方式实现路由(包含Nacos注册与Gateway配置)

spring:

application:

name: gateway

cloud:

nacos:

discovery:

# 设置nacos服务器的地址

server-addr: localhost:8848

# 默认为true是临时实例, 改为 false 就是永久实例

ephemeral: true

gateway:

#下面编写Gateway路由配置

routes:

#当前路由名称

- id: gateway-beijing

#当匹配路由的路径时,设置访问的服务器(Nacos里注册的服务器,在那个项目的spring.application.name设置里)

#lb是loadbalancer负载均衡的缩写

uri: lb://beijing

#断言 既满足条件时做某些事情

predicates:

#当请求路径以/bj/开头时,就会路由到上面设置的 lb://beijing 服务器

- Path=/bj/**

- id: gateway-shanghai

uri: lb://shanghai

predicates:

- Path=/sh/**

编写配置类的方式实现配置(此种方式,配置文件里只配置Nacos注册中心即可)

2.3.用配置类进行配置

用配置类进行配置,则配置文件只需要写下边部分就够了

spring:

application:

name: gateway

cloud:

nacos:

discovery:

# 设置nacos服务器的地址

server-addr: localhost:8848

配置类如下:

package cn.tedu.gateway.config;

import org.springframework.cloud.gateway.route.RouteLocator;

import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Configuration

public class GatewayConfiguration {

@Bean

public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {

RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();

routes

//名称为gateway-beijing的路由,匹配地址 /bj/** 使用 Nacos 里的 beijing 去处理请求 lb为负载均衡

.route("gateway-beijing", r -> r.path("/bj/**").uri("lb://beijing"))

.route("gateway-shanghai", r -> r.path("/sh/**").uri("lb://shanghai"))

.route("gateway-after", r ->

//匹配路径为 /show

r.path("/show")

//多个断言之间,使用and方法连接

.and()

//断言时间,只能在此时间后访问

.after(ZonedDateTime.parse("2022-08-25T10:00:00+08:00[Asia/Shanghai]"))

.and()

//断言查询参数,必须包含age,如 /show?age=1

.query("age")

//设置过滤器,在过滤器内添加请求参数,那么实际控制器收到的请求为: /show?age=1&name=tom

.filters(f -> f.addRequestParameter("name", "tom"))

//使用shanghai去处理请求

.uri("lb://shanghai")

)

//将路径为 /personal 的请求,转到石墨文档,石墨文档收到请求后,请求地址为: https://shimo.im/personal

.route("gateway-shimo", r -> r.path("/personal").uri("https://shimo.im"))

.build();

return routes.build();

}

}

2.4.配置文件中添加动态路由:

动态路由(默认关闭)

开启方式:在配置文件中设置spring.cloud.gateway.discovery.locator.enabled=true

spring:

application:

name: gateway

cloud:

nacos:

discovery:

# 设置nacos服务器的地址

server-addr: localhost:8848

# 默认为true是临时实例, 改为 false 就是永久实例

ephemeral: true

gateway:

discovery:

locator:

#开启动态路由

enabled: true

开启后,无需编写配置类或yaml内的配置 访问路由时,需要带上Nacos里注册的名称

如,假设 Gateway网关的端口为9000 北京服务器端口为9001(Nacos注册名为beijing) 上海服务器端口为9002(Nacos注册名为shanghai) 2个服务器同时都暴露了一个 /xx/show 的接口

原访问地址:

http://localhost:9001/bj/show http://localhost:9002/sh/show  

通过网关的访问地址:

http://localhost:9000/beijing/bj/show   >>等价于>>   http://localhost:9001/bj/show http://localhost:9000/shanghai/sh/show    >>等价于>>   http://localhost:9002/sh/show  

其中,地址中的 **shanghai/beijing **即为Nacos里,注册服务器的名称,请求会被路由到相应的服务器

注:动态路由与手动设置配置文件或编写配置类可以同时存在,并且同时生效

2.5.网关模块用knife4j测试:

在网关中使用swagger/knife4j来测试其他服务的接口 在pom文件中添加swagger/knife4j依赖添加依赖

com.github.xiaoymin

knife4j-spring-boot-starter

添加以下3个类…添加后, 访问 http://网关地址:网关端口/服务名称/doc.html 来访问 如下,其中 nacos-stock 是注册到Nacos的一个服务名称

http://localhost:19000/nacos-stock/doc.html  

控制器类:

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.http.HttpStatus;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import reactor.core.publisher.Mono;

import springfox.documentation.swagger.web.*;

import java.util.Optional;

@RestController

@RequestMapping("/swagger-resources")

public class SwaggerController {

@Autowired(required = false)

private SecurityConfiguration securityConfiguration;

@Autowired(required = false)

private UiConfiguration uiConfiguration;

private final SwaggerResourcesProvider swaggerResources;

@Autowired

public SwaggerController(SwaggerResourcesProvider swaggerResources) {

this.swaggerResources = swaggerResources;

}

@GetMapping("/configuration/security")

public Mono> securityConfiguration() {

return Mono.just(new ResponseEntity<>(

Optional.ofNullable(securityConfiguration).orElse(

SecurityConfigurationBuilder.builder().build()),

HttpStatus.OK));

}

@GetMapping("/configuration/ui")

public Mono> uiConfiguration() {

return Mono.just(new ResponseEntity<>(

Optional.ofNullable(uiConfiguration).orElse(

UiConfigurationBuilder.builder().build()),

HttpStatus.OK));

}

@GetMapping("")

public Mono swaggerResources() {

return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));

}

}

配置类

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.cloud.gateway.route.RouteLocator;

import org.springframework.stereotype.Component;

import springfox.documentation.swagger.web.SwaggerResource;

import springfox.documentation.swagger.web.SwaggerResourcesProvider;

import java.util.ArrayList;

import java.util.HashSet;

import java.util.List;

import java.util.Set;

@Component

public class SwaggerProvider implements SwaggerResourcesProvider {

/**

* 接口地址

*/

public static final String API_URI = "/v2/api-docs";

/**

* 路由加载器

*/

@Autowired

private RouteLocator routeLocator;

/**

* 网关应用名称

*/

@Value("${spring.application.name}")

private String applicationName;

@Override

public List get() {

//接口资源列表

List resources = new ArrayList<>();

//服务名称列表

List routeHosts = new ArrayList<>();

// 获取所有可用的应用名称

routeLocator.getRoutes().filter(route -> route.getUri().getHost() != null)

.filter(route -> !applicationName.equals(route.getUri().getHost()))

.subscribe(route -> routeHosts.add(route.getUri().getHost()));

// 去重,多负载服务只添加一次

Set existsServer = new HashSet<>();

routeHosts.forEach(host -> {

// 拼接url

String url = "/" + host + API_URI;

//不存在则添加

if (!existsServer.contains(url)) {

existsServer.add(url);

SwaggerResource swaggerResource = new SwaggerResource();

swaggerResource.setUrl(url);

swaggerResource.setName(host);

resources.add(swaggerResource);

}

});

return resources;

}

}

过滤器类

import org.springframework.cloud.gateway.filter.GatewayFilter;

import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;

import org.springframework.http.server.reactive.ServerHttpRequest;

import org.springframework.stereotype.Component;

import org.springframework.util.StringUtils;

import org.springframework.web.server.ServerWebExchange;

@Component

public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {

private static final String HEADER_NAME = "X-Forwarded-Prefix";

private static final String URI = "/v2/api-docs";

@Override

public GatewayFilter apply(Object config) {

return (exchange, chain) -> {

ServerHttpRequest request = exchange.getRequest();

String path = request.getURI().getPath();

if (!StringUtils.endsWithIgnoreCase(path,URI )) {

return chain.filter(exchange);

}

String basePath = path.substring(0, path.lastIndexOf(URI));

ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();

ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();

return chain.filter(newExchange);

};

}

}

注意: 由于Spring Boot,Spring Cloud Gateway都带有spring-boot-starter-web依赖,但是前者使用Tomcat,后者使用Netty,会导致冲突,项目无法启动 解决办法有3个

1. 在配置文件中,指定spring.main.web-application-type=reactive

spring:

main:

web-application-type: reactive

2. 添加spring-boot-starter-web依赖时,排除Tomcat

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-tomcat

3.不添加spring-boot-starter-web依赖,只添加Spring Cloud Gateway依赖

org.springframework.boot

spring-boot-starter-web

相关文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: