官网:
中文文档:
介绍:
crm开发定制网关就是当前的统一入口
crm开发定制通常在微服务项目中,crm开发定制只有网关项目是暴露在网络里的,crm开发定制其他服务一般都是在内网里,
crm开发定制用户访问网关,crm开发定制网关根据访问的路径,crm开发定制来进行路由
Gateway 网关也是微服务的一部分,需要将项目注册到Nacos
因为某一个服务可能存在多台服务器,所以也需要负载均衡依赖
依赖项
<!--Gateway网关依赖--><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId></dependency><!--网关负载均衡依赖--><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency><!--Nacos依赖--><dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
纯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/**
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
编写配置类的方式实现配置(此种方式,配置文件里只配置Nacos注册中心即可)
spring: application: name: gateway cloud: nacos: discovery: # 设置nacos服务器的地址 server-addr: localhost:8848
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
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;@Configurationpublic 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(); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
动态路由(默认关闭)
开启方式:在配置文件中设置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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
开启后,无需编写配置类或yaml内的配置
访问路由时,需要带上Nacos里注册的名称
如,假设
Gateway网关的端口为9000
北京服务器端口为9001(Nacos注册名为beijing)
上海服务器端口为9002(Nacos注册名为shanghai)
2个服务器同时都暴露了一个 /xx/show 的接口
原访问地址:
http://localhost:9001/bj/showhttp://localhost:9002/sh/show
- 1
- 2
通过网关的访问地址:
http://localhost:9000/beijing/bj/show >>等价于>> http://localhost:9001/bj/showhttp://localhost:9000/shanghai/sh/show >>等价于>> http://localhost:9002/sh/show
- 1
- 2
其中,地址中的 **shanghai/beijing **即为Nacos里,注册服务器的名称,请求会被路由到相应的服务器
注:动态路由与手动设置配置文件或编写配置类可以同时存在,并且同时生效
在网关中使用swagger/knife4j来测试其他服务的接口
在pom文件中添加swagger/knife4j依赖
<dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId></dependency>
- 1
- 2
- 3
- 4
添加以下3个类…添加后,
访问 http://网关地址:网关端口/服务名称/doc.html 来访问
如下,其中 nacos-stock 是注册到Nacos的一个服务名称
http://localhost:19000/nacos-stock/doc.html
- 1
控制器类
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<ResponseEntity<SecurityConfiguration>> securityConfiguration() { return Mono.just(new ResponseEntity<>( Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK)); } @GetMapping("/configuration/ui") public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() { return Mono.just(new ResponseEntity<>( Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK)); } @GetMapping("") public Mono<ResponseEntity> swaggerResources() { return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK))); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
配置类
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;@Componentpublic 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<SwaggerResource> get() { //接口资源列表 List<SwaggerResource> resources = new ArrayList<>(); //服务名称列表 List<String> 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<String> 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; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
过滤器类
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;@Componentpublic 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); }; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
注意: 由于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
- 1
- 2
- 3
2. 添加spring-boot-starter-web依赖时,排除Tomcat
<!-- web实例 --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions></dependency>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
3.不添加spring-boot-starter-web依赖,只添加Spring Cloud Gateway依赖
<!-- web实例 --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency>
- 1
- 2
- 3
- 4
- 5
达内:lzy