赞
踩
本篇文章我们将实现下图所示的架构
我们可以接着使用前面《SpringCloud之Feign使用篇》中的项目环境,当然也也可以自己重新搭建都是可以的,这里我们使用的springcloud的版本是:Greenwich.RELEASE
新建module spring-cloud-gateway-server
需要我们在spring-cloud-gateway-server 项目pom.xml添加依赖,这里 父工程我没有使用之前的,而是选择了spring-boot,因为在springcloud gateway中,web使用的webflux 没有使用spring mvc ,而之前的父工程中引入mvc。
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> </parent> <dependencyManagement> <!--spring cloud依赖版本管理--> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-commons</artifactId> </dependency> <!--eureka 客户端依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--gateway 依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!--webflux 依赖 ,gateway 使用的是webflux,并没有使用springmvc --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <!--springboot监控--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
在网关服务 application.yml 中配置网关路由规则
server: port: 8020 spring: application: name: spring-cloud-gateway-server cloud: gateway: # 配置路由,可以配置多个 routes: - id: order-microservice-router # id 自定义路由的id uri: http://127.0.0.1:7070 # uri就是 目标服务地址 predicates: # 断言,也就是路由条件 ,这里使用了path作为路由条件 - Path=/order/** - id: user-microservice-router uri: http://127.0.0.1:8081 predicates: - Path=/user/** #eureka 配置 eureka: client: service-url: # eureka server url defaultZone: http://EurekaServerA:9090/eureka,http://EurekaServerB:9091/eureka register-with-eureka: true fetch-registry: true instance: prefer-ip-address: true instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
其中我们配置了2个路由,path=/order 开头的转到http://127.0.0.1:7070 服务上,也就是咱们的订单服务,path=/user 开头的转到http://127.0.0.1:8081 服务上,也就是我们的用户服务。
我们首先将 Eureka Server集群 9090与9091提起来,然后再将用户服务与订单服务提起来,在起订单服务之前为了测试方便 需要给订单服务里面添加个接口:
我们在订单服务spring-cloud-order-service-provider 的controller里面添加个接口(之前的getTodayFinishOrderNum方法不用动,用户服务还要调用它)
@RestController @RequestMapping("/order/data") public class OrderStatisticServiceController { @Value("${server.port}") private Integer port; @Value("${spring.application.name}") private String appName; /** * 根据用户id获取今日完单数 * @param id 用户ID * @return 完单数 */ @GetMapping("/getTodayFinishOrderNum/{id}") public Integer getTodayFinishOrderNum(@PathVariable("id") Integer id){ System.out.println("我是"+port); if (port==7070){ try {// 睡眠10s Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } return port; } // 获取appName @GetMapping("/getAppName/{id}") public String getAppName(@PathVariable("id") Integer id){ return appName; } }
然后将服务提起来,可以看到已经全部起来了,将gateway项目也提起来。
浏览器请求:http://127.0.0.1:8020/order/data/getAppName/100 ,注意咱们这个ip与port是网关服务的
这时候可以看到网关将咱们的请求打到order服务上了。
浏览器再请求:http://127.0.0.1:8020/user/data/getTodayStatistic/100
网关就把咱们这次请求打到user服务上了。
在springcloud gateway 中内置了很多 Predicates 功能,实现了很多路由匹配规则匹配到对应的路由。
断言类型 | 描述 |
---|---|
DateTime 时间断言 | 根据请求时间在配置的时间的前面,后面,之前 |
Cookie 类断言 | 指定Cookie正则匹配指定值 |
Header 请求头 | 指定Header正则匹配指定值,请求头中是否包含某个属性 |
Host请求主机断言 | 请求Host匹配指定值 |
Method请求方式断言 | 请求Method匹配指定请求方式 |
Path 请求路径类断言 | 请求路径正则匹配指定值 |
QueryParam 请求参数 | 查询参数正则匹配指定值 |
RemoteAddr远程地址类断言 | 请求远程地址匹配指定值 |
在上面第二节的简单使用中,我们把uri 这个目标服务器写死了,固定到了某个ip:port上面,其实springcloud gateway支持从注册中心获取服务列表进行访问,能够实现动态路由。下面我们就演示下动态路由
这里我们需要把uri 配置成 lb://在注册中心注册的服务名
server: port: 8020 spring: application: name: spring-cloud-gateway-server cloud: gateway: # 配置路由,可以配置多个 routes: - id: order-microservice-router # id 自定义路由的id # uri就是目标服务地址,这里使用服务名的方式,gateway会帮我们去注册中心中获取服务列表 uri: lb://spring-cloud-order-service-provider predicates: # 断言,也就是路由条件 ,这里使用了path作为路由条件 - Path=/order/** - id: user-microservice-router uri: lb://spring-cloud-user-service-consumer predicates: - Path=/user/** #eureka 配置 eureka: client: service-url: # eureka server url defaultZone: http://EurekaServerA:9090/eureka,http://EurekaServerB:9091/eureka register-with-eureka: true fetch-registry: true instance: prefer-ip-address: true instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
在项目启动类上面添加@EnableDiscoveryClient的注解,表示开启Eureka
重启网关服务,在测试前,我们为了测试方便还是需要修改订单服务的controller方法,将端口加到返回值中。
@RestController @RequestMapping("/order/data") public class OrderStatisticServiceController { @Value("${server.port}") private Integer port; @Value("${spring.application.name}") private String appName; /** * 根据用户id获取今日完单数 * @param id 用户ID * @return 完单数 */ @GetMapping("/getTodayFinishOrderNum/{id}") public Integer getTodayFinishOrderNum(@PathVariable("id") Integer id){ System.out.println("我是"+port); if (port==7070){ try {// 睡眠10s Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } return port; } // 获取appName @GetMapping("/getAppName/{id}") public String getAppName(@PathVariable("id") Integer id){ // appName +port return appName+String.valueOf(port); } }
我们重启下订单服务,然后进行测试。
浏览器访问:http://127.0.0.1:8020/order/data/getAppName/100,多访问几次
我们可以看到有7070的也有7071的,说明我们配置的动态路由生效了。
在springchoud gateway中 过滤器可以分为Global过滤器与Gateway过滤器
过滤器类型 | 说明 |
---|---|
Global过滤器 | 这种就是作用在所有的路由上 |
Gateway过滤器 | 这种就是作用在指定的路由上面 |
然后根据过滤器的生命周期分可以分为 pre过滤与post过滤
生命周期点 | 介绍 |
---|---|
pre | 这个就是请求来的时候,可以对请求做一些动作,我们可利用这种过滤器实现身份验证,在集群中选择请求的微服务,记录调试信息等。 |
post | 这个就是响应过来的时候,可以对响应做某些过滤动作,这种过滤器可用来为响应添加标准的 HTTP Header,收集统计信息和指标,将响应从微服务发送给客户端等。 |
我们这里自定义一个全局过滤器,然后实现一个黑白名单的功能。
这里我们写一个BlackListFilter类然后实现GlobalFilter, Ordered 接口
@Component public class BlackListFilter implements GlobalFilter, Ordered { // 这里模拟下黑名单 private static final List<String> blackList=new ArrayList<>(); static { blackList.add("127.0.0.1");// 模拟本机地址 } /** * 进行过滤操作 * @param exchange * @param chain * @return */ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 获取请求 ServerHttpRequest request = exchange.getRequest(); // 获取响应 ServerHttpResponse response = exchange.getResponse(); // 获取客户端ip String host = request.getRemoteAddress().getHostString(); System.out.println("remote host:"+host); if (blackList.contains(host)){ // 这个客户端ip在黑名单里面 // 设置响应码 response.setStatusCode(HttpStatus.UNAUTHORIZED); String data = "拒绝访问"; DataBuffer wrap = response.bufferFactory().wrap(data.getBytes()); HttpHeaders headers = response.getHeaders(); // 设置中文乱码 headers.add("content-type", "text/html;charset=utf-8"); return response.writeWith(Mono.just(wrap)); } // 放行到下一个过滤器 return chain.filter(exchange); } /** * 主要是多个过滤器的时候,需要对过滤器排序, * 先经过哪个,后经过哪个,数值越小,这个优先级越高 * @return order优先级 */ @Override public int getOrder() { return 0; } }
实现这个GlobalFilter 接口主要是说明我这个是个全局过滤器并在定义一些过滤的动作,
然后实现这个Ordered 接口主要是对咱们自定义过滤器的优先级做一个设置,数值越小,filter就越早执行,优先级就越高
重启网关项目,然后测试一下:
浏览器输入http://127.0.0.1:8020/order/data/getAppName/100 ,可以看到请求被拦截了。
我们要想对网关做高可用,就得依赖网关上层的nginx来实现,接下来我们简单配置下nginx 来实现gateway的高可用。
修改网关服务的appliction.yml
spring: application: name: spring-cloud-gateway-server cloud: gateway: # 配置路由,可以配置多个 routes: - id: order-microservice-router # id 自定义路由的id # uri就是目标服务地址,这里使用服务名的方式,gateway会帮我们去注册中心中获取服务列表 uri: lb://spring-cloud-order-service-provider predicates: # 断言,也就是路由条件 ,这里使用了path作为路由条件 - Path=/order/** - id: user-microservice-router uri: lb://spring-cloud-user-service-consumer predicates: - Path=/user/** #eureka 配置 eureka: client: service-url: # eureka server url defaultZone: http://EurekaServerA:9090/eureka,http://EurekaServerB:9091/eureka register-with-eureka: true fetch-registry: true instance: prefer-ip-address: true instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@ --- spring: profiles: g1 server: port: 8020 --- spring: profiles: g2 server: port: 8021
这样启动的时候可以配置一下idea
这样我们就有了8020 与8021 两个网关实例
如果没有nginx ,可以去 http://nginx.org/en/download.html 下载一个,这个地方选择自己系统下载就好了。
然后就是nginx安装,window 解压就能用,linux系统稍微麻烦些也可以使用yum安装,mac系统可以直接使用brew 安装 brew install nginx
接着就是nginx的配置文件了,修改nginx.conf
#user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; upstream gateway { server 127.0.0.1:8020; server 127.0.0.1:8021; } server { listen 80; server_name localhost; location / { proxy_pass http://gateway; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }
这里主要是
upstream gateway {
server 127.0.0.1:8020;
server 127.0.0.1:8021;
}
location / {
proxy_pass http://gateway;
}
接着启动一下nginx
因为咱们nginx是80端口,所以直接在浏览器访问:http://127.0.0.1/order/data/getAppName/100
接着咱们模拟一个网关服务挂了(手动关闭一个8021)
然后访问,还是能访问到
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。