从零搭建微服务项目(第7章——微服务网关模块底子实现) ...

打印 上一主题 下一主题

主题 1766|帖子 1766|积分 5298

媒介:

在前面6章的学习中已经完成了服务间的调用实现,即各微服务通过nacos或eureka服务器完成服务的注册,并从nacos中拉取配置实现热更新。当某个服务接口需要调用其他服务时,通过feign定义接口,并通过注解配置服务名称,在nacos或eureka服务器中找到对应服务端口完成调用。
但实际应用中,用户不可能直接访问这些服务端口,由于每个服务对应一个端口,当服务拆分很多时,会有大量端口,前端开辟人员不可能针对每次调用看文档找对应端口,因此引入网关模块,用户只需要访问网关模块端口,网关模块自动转发,且网关模块也能实现服务聚合、负载均衡、用户鉴权等功能。为此,本章实现底子的网关模块,包括网关服务模块创建、路由断言、过滤器配置

本章代码基于第6章项目,前置源码可在第6章博客下载,博客链接如下:
从零搭建微服务项目(第6章——Feign性能优化以及模块抽取)-CSDN博客
https://blog.csdn.net/wlf2030/article/details/145649565扼要介绍前置项目流程:order-service以及user-service两服务分别连接order-db以及user-db两数据库,order-db中仅有user-id,user-info存在user-db中,为提供完备order-info,order-service通过nacos发现user-service服务地址并使用Feign调用服务端口拿取user-info联合从order-db中拿取的信息返回给前端。同时项目自定义日记输出。
本项目源码链接如下:
wlf728050719/SpringCloudBase7
https://github.com/wlf728050719/SpringCloudBase7以及本专栏会连续更新微服务项目,每一章的项目都会基于前一章项目举行功能的完善,欢迎小同伴们关注!同时假如只是对单章感兴趣也不消从头看,只需下载前一章项目即可,每一章都会有前置项目准备部门,跟着操作就能实现上一章的最终结果,当然假如是一直跟着做可以直接跳过这一部门。


一、前置项目准备

1.从github下载前一章的项目解压,重命名为Base7打开。

2.重命名模块为Base7.

3.父工程pom.xml中<name>改成Base7。

4.选择环境为dev,并重新加载maven

5.启动nacos(安装和启动见第三章)

6.进入nacos网页 配置管理->配置列表确认有这些yaml文件。
(假如不是一直跟着专栏做自然是没有的,需要看第四章的环境隔离和配置拉取,记得把父工程pom文件中namespace的值与nacos中命名空间生成的保持同等)


7.配置数据源,更换两服务的resources下yml文件的数据库配置,数据库sql见第一章数据库准备部门。

.测试数据库连接 属性->点击数据源->测试连接->输入用户名密码



8.添加运行配置 服务->加号->运行配置范例->spring boot。

启动服务,测试接口。

能够在日记文件中看到最新的日记记录。



二、网关服务模块创建以及配置

1.新建SpringBoot模块,配置如下。

2.不添加任何依赖

3.删除不须要文件和目录,最终结构如下。

4.将gateway模块的pom文件替换为下面内容。
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3.          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4.     <modelVersion>4.0.0</modelVersion>
  5.     <parent>
  6.         <groupId>cn.bit</groupId>
  7.         <artifactId>Base7</artifactId>
  8.         <version>1.0-SNAPSHOT</version>
  9.         <relativePath>../pom.xml</relativePath>
  10.     </parent>
  11.     <artifactId>gateway</artifactId>
  12.     <version>0.0.1-SNAPSHOT</version>
  13.     <name>gateway</name>
  14.     <description>gateway</description>
  15.     <dependencies>
  16.         <dependency>
  17.             <groupId>org.springframework.cloud</groupId>
  18.             <artifactId>spring-cloud-starter-gateway</artifactId>
  19.             <version>2.2.5.RELEASE</version>
  20.         </dependency>
  21.         <!-- nacos客户端依赖包 -->
  22.         <dependency>
  23.             <groupId>com.alibaba.cloud</groupId>
  24.             <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  25.         </dependency>
  26.     </dependencies>
  27.     <build>
  28.         <plugins>
  29.             <plugin>
  30.                 <groupId>org.springframework.boot</groupId>
  31.                 <artifactId>spring-boot-maven-plugin</artifactId>
  32.             </plugin>
  33.         </plugins>
  34.     </build>
  35. </project>
复制代码
4.修改父文件pom,即将gateway模块声明为父模块的子模块,重新加载maven模块。

5.重新加载maven文件后,检察maven结构是否如下,假如不是见本专栏第0章第三节部门有对应解决方法。

6.在父文件pom中为gateway在各配置环境下设置端口。

7.在gateway的application中排除默认数据源,否则需要在application中配置数据源,后续动态路由时需要使用数据库时再规复。

8.为gateway在resources目录下创建application.yml配置文件,内容如下:
  1. server:
  2.   port: @gateway.port@
  3. spring:
  4.   application:
  5.     name: gateway
  6.   cloud:
  7.     nacos:
  8.       server-addr: localhost:8848
  9.       discovery:
  10.         namespace: @namespace@
  11.     gateway:
  12.       routes:
  13.         - id: user-service
  14.           uri:
  15.             lb://user-service
  16.           predicates:
  17.             - Path=/user/**
  18.         - id: order-service
  19.           uri:
  20.             lb://order-service
  21.           predicates:
  22.             - Path=/order/**
复制代码
9.启动服务

通过在网关端口输入路径即可通过断言调取对应服务的接口。


三、路由断言


现在是通过application.yml配置路由工厂。但使用动态路由需要重写实现路由工厂类,以及使用同一的格式便于规范数据库中路由信息,纵然用args+name,为方便后续章节理解,使用args+name替换gateway的application.yml,内容如下:
  1. server:
  2.   port: @gateway.port@
  3. spring:
  4.   application:
  5.     name: gateway
  6.   cloud:
  7.     nacos:
  8.       server-addr: localhost:8848
  9.       discovery:
  10.         namespace: @namespace@
  11.     gateway:
  12.       routes:
  13.         - id: user-service
  14.           uri:
  15.             lb://user-service
  16.           predicates:
  17.             - name: Path
  18.               args:
  19.                 _genkey_0: /user/**
  20.         - id: order-service
  21.           uri:
  22.             lb://order-service
  23.           predicates:
  24.             - name: Path
  25.               args:
  26.                 _genkey_0: /order/**
复制代码
测试通过


四、路由过滤器


后续鉴权模块需要配合网关的过滤器一起搭配使用才能实现不同脚色不同权限访问对应服务/端口,需要手写代码实现 AbstractGatewayFilterFactory,现在先通过yml配置文件为网关添加过滤器方便后续理解。
修改网关模块的yml文件如下:(现在的yml其实就已经很长了,后续必然需要使用代码联合数据库代替配置文件)
  1. server:
  2.   port: @gateway.port@
  3. spring:
  4.   application:
  5.     name: gateway
  6.   cloud:
  7.     nacos:
  8.       server-addr: localhost:8848
  9.       discovery:
  10.         namespace: @namespace@
  11.     gateway:
  12.       routes:
  13.         - id: user-service
  14.           uri:
  15.             lb://user-service
  16.           predicates:
  17.             - name: Path
  18.               args:
  19.                 _genkey_0: /user/**
  20.           filters:
  21.             - name: AddRequestHeader
  22.               args:
  23.                 name: source
  24.                 value: request user from gateway
  25.         - id: order-service
  26.           uri:
  27.             lb://order-service
  28.           predicates:
  29.             - name: Path
  30.               args:
  31.                 _genkey_0: /order/**
  32.           filters:
  33.             - name: AddRequestHeader
  34.               args:
  35.                 name: source
  36.                 value: request order from gateway
复制代码
为了获取哀求头内容,对OrderController和UserController举行修改。直接替换成下面内容即可。
  1. package cn.bit.orderservice.controller;
  2. import cn.bit.common.pojo.vo.OrderInfoVO;
  3. import cn.bit.common.pojo.vo.R;
  4. import cn.bit.orderservice.service.OrderService;
  5. import lombok.extern.slf4j.Slf4j;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.web.bind.annotation.*;
  8. @Slf4j
  9. @RestController
  10. @RequestMapping("/order")
  11. public class OrderController {
  12.     @Autowired
  13.     private OrderService orderService;
  14.     @GetMapping("/test/{id}")
  15.     public String test(@PathVariable Integer id) {
  16.         System.out.println(id);
  17.         return id.toString();
  18.     }
  19.     @GetMapping("/info/{id}")
  20.     public R getOrderInfoById(@PathVariable Integer id, @RequestHeader(value = "source",required = false) String source) {
  21.         log.debug("debug");
  22.         log.info("info");
  23.         log.warn("warning");
  24.         System.out.println(source);
  25.         OrderInfoVO orderInfoVO = orderService.getOrderInfoById(id);
  26.         if (orderInfoVO == null) {
  27.             return R.failed("订单不存在");
  28.         }
  29.         else
  30.             return R.ok(orderInfoVO);
  31.     }
  32. }
复制代码
  1. package cn.bit.userservice.controller;
  2. import cn.bit.common.pojo.dto.UserBaseInfoDTO;
  3. import cn.bit.common.pojo.vo.R;
  4. import cn.bit.common.pojo.vo.UserFavorVO;
  5. import cn.bit.userservice.config.PatternProperties;
  6. import cn.bit.userservice.service.UserService;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.web.bind.annotation.*;
  9. import java.time.LocalDateTime;
  10. import java.time.format.DateTimeFormatter;
  11. @RestController
  12. @RequestMapping("/user")
  13. public class UserController {
  14.     @Autowired
  15.     private UserService userService;
  16.     @Autowired
  17.     private PatternProperties patternProperties;
  18.     @GetMapping("/test/{id}")
  19.     public String test(@PathVariable Integer id) {
  20.         System.out.println(id);
  21.         return id.toString()+" "+LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat()));
  22.     }
  23.     @GetMapping("/favor/{id}")
  24.     public R getUserFavorById(@PathVariable Integer id) {
  25.         UserFavorVO vo = userService.getUserFavorById(id);
  26.         if(vo != null) {
  27.             return R.ok(vo);
  28.         }
  29.         else
  30.             return R.failed("用户不存在");
  31.     }
  32.     @GetMapping("/baseInfo/{id}")
  33.     public R getUserBaseInfoById(@PathVariable Integer id, @RequestHeader(value = "source",required = false) String source) {
  34.         System.out.println("get request");
  35.         System.out.println(source);
  36.         UserBaseInfoDTO dto = userService.getUserBaseInfoById(id);
  37.         if(dto != null) {
  38.             return R.ok(dto);
  39.         }
  40.         else
  41.             return R.failed("用户不存在");
  42.     }
  43. }
复制代码
启动服务

先访问user接口即localhost:1233/user/baseInfo/1

能够验证确实添加了哀求头

再访问order接口即localhost:1233/order/info/1

发现order-service获取到哀求头,user-service为null,由于网关只对访问order-service的request添加了哀求头,order-service之后使用feign访问的user-service,自然没有哀求头。


为了实现调用朔源可以修改UserClient的接口方法

以及其调用

再次启动服务调用


五、全局过滤器

之前的过滤器为每个路由的过滤器,而全局过滤器无论使用哪条路由均需要使用。配置如下:
在网关模块创建filter包以及AuthorizeFilter类,内容如下:
  1. package cn.bit.gateway.filter;
  2. import org.springframework.cloud.gateway.filter.GatewayFilterChain;
  3. import org.springframework.cloud.gateway.filter.GlobalFilter;
  4. import org.springframework.core.annotation.Order;
  5. import org.springframework.http.HttpStatus;
  6. import org.springframework.http.server.reactive.ServerHttpRequest;
  7. import org.springframework.stereotype.Component;
  8. import org.springframework.util.MultiValueMap;
  9. import org.springframework.web.server.ServerWebExchange;
  10. import reactor.core.publisher.Mono;
  11. @Order(-1)
  12. @Component
  13. public class AuthorizeFilter implements GlobalFilter {
  14.     @Override
  15.     public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  16.         ServerHttpRequest request = exchange.getRequest();
  17.         MultiValueMap<String, String> queryParams = request.getQueryParams();
  18.         String token = queryParams.getFirst("token");
  19.         if("admin".equals(token)) {
  20.             return chain.filter(exchange);
  21.         }
  22.         exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
  23.         return exchange.getResponse().setComplete();
  24.     }
  25. }
复制代码
重启后发现只有哀求参数包含token字段且值为token才能访问对应服务。



六、过滤器执行顺序




最后:

黑马课程关于网关模块讲解还是比力浅显,和企业实际应用有较大出入,后续会研究动态路由实现以及鉴权模块,这两模块比力复杂以是后续更新会慢些。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

熊熊出没

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表