微服务组件----网关

news2024/12/23 8:05:10

       小编目前大一,刚开始着手学习微服务的相关知识,小编会把它们整理成知识点发布出来。我认为同为初学者,我把我对知识点的理解以这种代码加观点的方式分享出来不仅加深了我的理解,或许在某个时候对你也有所帮助,同时也欢迎大家在评论区分享你们的观点。

       知不足而奋进,望远山而前行。  

目录

前言

快速入门

路由属性

网关登录校验


前言

        在我们把一个单体架构拆分成微服务时会遇到,服务端口过多,并且将来有可能发生变化,前端不知道该请求谁,又或者是每个服务都可能需要登录用户信息,各自去做不仅麻烦,还有密钥泄露的风险诸如此类的等等问题。为了解决上述问题,我们就需要了解网关相关的知识。

        网关就是网络的关口,负责请求的路由、转发、身份校验。微服务就相当于小区的住户,网关就是小区的保安,记录着每位业主的住址信息,几楼几号,对于陌生人要进小区会查验身份,身份合格后才会放你进来,并告诉你要找的人的住址。在我们项目中,就是前端直接发送请求到网关,网关身份校验后,再通过路由转发去调用指定的微服务。        

        另外我们通过注册中心来进行服务拉取获取服务列表,实现了网关对微服务状态信息的获取,如果服务挂掉了,我们也就不会路由转发给它。

        在SpringCloud中网关的实现包括两种:Spring Cloud Gateway Netfilx Zuul,具体二者的区别见下图:

快速入门

        接下来我们就来尝试使用一下网关,网关的快速入门主要分为两步:第一步搭建好网关,第二步就是配置路由规则,剩下的事情网关就可以自动去完成了。但是虽说是两步,但是引入依赖,编写启动类这些基础的其实也在里面。其实搭建网关服务,依赖,启动类就是重新创建个模块,还是不难的。重点就是配置路由规则,具体规则如下:

        id这一块我们直接以服务名称做id,方便还不容易出错,uri属性也是比较简单的。第三个predicates属性,就是判断请求是否符合规则,这里我们通常使用模糊匹配,匹配整个controller所有的方法。

        首先创建一个新的模块,我就把它叫做hm-gateway吧,接着第二步引入相关依赖。

    <dependencies>
        <!--common-->
        <dependency>
            <groupId>com.heima</groupId>
            <artifactId>hm-common</artifactId>
            <version>1.0.0</version>
        </dependency>
        <!--网关-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--nacos discovery-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--负载均衡-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

        第三步编写网关启动类

@SpringBootApplication
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}

        第四步就是最重要的配置路由规则了,按照上面定义的规则,我们不难写出下面的代码。

spring:
  cloud:
    gateway:
      routes:
        - id: item-service
          uri: lb://item-service
          predicates:
            - Path=/items/**,/search/**
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/users/**,/addresses/**

        以上步骤完成后,我们就可以去浏览器测试一下了。

        通过网关端口8080访问,也是请求到了数据,证明没有什么问题。

路由属性

        以上的路由属性配置已经能满足我们基本的需求了,但是难免有时候会出现我们有别的需求,所以我们有必要了解一下路由属性。

        我们知道在yaml文件中所有配置属性最终都会有一个对应的java类来读取,网关路由对应的java类型是RouteDefinition,其中常见的属性有:id、uri、predicates、filters。

        在Spring内部提供了12种基本的路由断言RoutePredicateFactory实现:

        网关中提供了33种路由过滤器,每种过滤器都有独特的作用。

        接下来我们就来尝试使用一下过滤器,首先我们要到yml文件种去配置过滤器,我这里就选择添加请求头。

    gateway:
      routes:
        - id: item-service
          uri: lb://item-service
          predicates:
            - Path=/items/**,/search/**
          filters:
            - AddRequestHeader=truth, anyone long-press like button will be rich

        设置好后,我们就去找个方法拿到请求头并打印到控制台。

@ApiOperation("分页查询商品")
    @GetMapping("/page")
    public PageDTO<ItemDTO> queryItemByPage(PageQuery query, @RequestHeader(value = "truth", required = false) String truth) {
        System.out.println("truth:" + truth);
        // 1.分页查询
        Page<Item> result = itemService.page(query.toMpPage("update_time", false));
        // 2.封装并返回
        return PageDTO.of(result, ItemDTO.class);
    }

        接下来我们就看看访问商品服务的分页查询功能时,能不能获取到请求头并打印到控制台。 

        很显然我们是成功了的。 另外过滤器也可以不用配置到routes中,也可以配置与routes同级,这样就叫做默认过滤器。这样就实现了给所有路由配置了过滤器,方便省事,同时也是生效的

 网关登录校验

     思路分析

        用户微服务实现了登录授权后会携带JWT令牌,但是其它服务,比如订单,购物车等也需要用户的信息的话,不能每个服务都做JWT校验,所以JWT的校验应该由网关来实现,同时该检验一定要放在路由转发之前。

        网关请求处理的流程图见下:

        既然NettyFilter是负责转发请求的,所以我们只需要自定义一个过滤器保证执行顺序在Netty路由过滤器之前并且在它的pre阶段中实现JWT的校验可以实现登录校验了。 另外网关在校验之后还需要将用户信息传递给微服务,这一块我们就可以将用户信息保存到请求头,这样微服务也可以从请求头中取出用户信息。还有一点需要注意,有时候微服务之间也会互相调用,那如何在微服务之间传递用户信息呢,微服务之间是通过OpenFeign去实现的,所以这一块和网关保存用户信息到请求头还是有所不同。

自定义过滤器

        网关过滤器有两种,分别是:GatewayFilterGlobalFilter。二者的区别如下:

        这两种过滤器的过滤方法都是filter,无论是返回值类型,还是参数都是一样的。

 

    自定义GlobalFilter

        其实这个也比较简单,我们只要让我们自定以的Filter继承GlobalFilter,接着重写filter方法就行了,另外我们还要保证我们的过滤器是在NettyFilter之前执行,所以我们还要继承Order接口,来设置一些优先级,NettyFilter是最低优先级,int的最大值,我们只要比这个数小,那么优先级就比它大。

@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // TODO 模拟实现登录逻辑
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();
        System.out.println("headers:" + headers);
        // 放行
        return chain.filter(exchange);
    }

    //  设置我们过滤器的优先级要比NettyFilter高
    //  NettyFilter是int的最大值,我们只要比这个数小,优先级就比它大
    @Override
    public int getOrder() {
        return 0;
    }
}
    自定义GatewayFilter

        GatewayFilter相比于GlobalFilter更加灵活,可以任意指定路由,但是如果我们想自定义GatewayFilter就比较麻烦了。将来我们使用GlobalFilter更多,这一块仅作了解就好了。

        自定以GatewayFilter不是直接实现GatewayFilter,而是实现AbstractGatewayFilterfactory

         下面就是使用工厂模式定义一个无参的GatewayFilter,我们将它的优先级指定为1,这样它就比我们刚刚定义的GlobalFilter级别高了。

@Component
public class PrintAnyGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
    @Override
    public GatewayFilter apply(Object config) {
        return new OrderedGatewayFilter(new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                System.out.println("print any filter running");
                return chain.filter(exchange);
            }
        }, 1);
    }
}

        这一种过滤器仅作了解就好了,大部分情况我们使用的都是GlobalFilter

实现登录校验

        接着我们就来手写一个GlobalFilter来实现登录校验,这一块其实和我们之前写的JWT校验都差不多,只不过我们要设置一下排除一些路径,因为有些路径的访问时不需要做登录拦截的。

@Component
@RequiredArgsConstructor
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    private final AuthProperties authProperties;

    private final JwtTool jwtTool;

    private final AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1. 获取request对象
        ServerHttpRequest request = exchange.getRequest();
        // 2. 判断是否需要做登录拦截
        if (isExclude(request.getPath().toString())) {
            // 放行
            return chain.filter(exchange);
        }
        // 3. 获取token
        String token = null;
        List<String> headers = request.getHeaders().get("authorization");
        if (headers != null && !headers.isEmpty()) {
            token = headers.get(0);
        }
        // 4. 校验并解析token
        Long userId = null;
        try {
            userId = jwtTool.parseToken(token);
        } catch (UnauthorizedException e) {
            // 拦截,设置响应状态码为401
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        // TODO 5.传递用户信息
        System.out.println("userId = " + userId);
        // 6. 放行
        return chain.filter(exchange);
    }

    private boolean isExclude(String path) {
        for (String pathPattern : authProperties.getExcludePaths()) {
            if (antPathMatcher.match(pathPattern, path)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

        这一块的代码也没有什么好说的,都是和之前一样的逻辑。但是我们还留下了一个问题,那就是我们还要将用户信息传递下去,这个应该怎么实现呢? 

网关传递用户

        对于微服务之间的服务调用或者网关到服务的路由发送,我们都可以通过SpringMVC提供的拦截器,获取用户信息并保存到ThreadLocal中。

        这里其实分为两步,第一步我们要在网关的登录校验过滤器中,把获取到的用户写入请求头,这一块其实已经提供好了现成的API,我们直接调用就好了。

        // 5.传递用户信息
        String userInfo = userId.toString();
        ServerWebExchange swe = exchange.mutate()
                .request(builder -> builder.header("user-info", userInfo))
                .build();

        第二步就是在common模块中编写SpringMVC拦截器,获取登录用户。具体为什么定义在common模块时因为定义在这里就只需要写一遍了。

        拦截器定义这一块没什么好说的,都是和之前差不多的

public class UserInfoInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1. 获取登录用户信息
        String userInfo = request.getHeader("user-info");
        // 2. 判断是否获取了用户,如果有,存入ThreadLocal
        if (StrUtil.isNotBlank(userInfo)) {
            UserContext.setUser(Long.valueOf(userInfo));
        }
        // 3. 放行
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 清理用户
        UserContext.removeUser();
    }
}

        接着把拦截器加载到SpringMvc的配置类当中让它生效。

@Configuration
@ConditionalOnClass(DispatcherServlet.class)
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new UserInfoInterceptor());
    }
}

        好了,以上我们就学会了从网关到微服务的用户信息传递。

OpenFeign传递用户

        接着我们来看看微服务之间的调用如何进行用户信息的传递。例如当我们去支付订单后,支付服务需要去调用购物车服务删除购物车的数据。

        现在我们在服务之间的调用时也应该让它去带上请求头,但是这个服务之间的请求时OpenFeign帮我们发送的,所以应该怎么告诉OpenFeign发送千秋之前带上请求头呢?

        OpenFeign中提供了一个拦截器接口,所有由OpenFeign发起的请求都会先调用拦截器处理请求。

         这一块我们直接使用匿名内部类就好了。

@Bean
    public RequestInterceptor userInfoRequestInterceptor() {
        return new RequestInterceptor() {
            @Override
            public void apply(RequestTemplate requestTemplate) {
                Long userId = UserContext.getUser();
                if (userId != null) {
                    requestTemplate.header("user-info", userId.toString());
                }
            }
        };
    }

        到这里我们就实现好了微服务之间调用获取用户信息的需求。

        可能到这里已经那么多过滤器拦截器什么的,已经绕晕了,对照上面这张图,相信你可以理清这之间的关系。

        以上就是网关的相关知识。

        带着决心起床,带着满意入睡。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2099542.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

KTV包房开台必点套餐--SAAS本地化及未来之窗行业应用跨平台架构

一、源码 var 未来之窗app_通用ID"";CyberWin_Dialog.layer(url,{type:"url",title:title,move:false,width:"700px",height:"400px",id:未来之窗app_通用ID,mask:true,align:59,hideclose:false}); 二、解释 以下是用修仙手法为您改…

深度学习系列69:tts技术原理

tts为text-to-speech&#xff0c;asr为Automatic Speech Recognition&#xff0c;即speech-to-text。 1. 常用基础模型 下面介绍的deep voice是端到端生成语音的模型&#xff0c;后面两个是生成Mel谱&#xff0c;然后再使用vocoder生成语音的模型。 1.1 Deep voice 目前端到…

Ubuntu安装boost,protobuf、moduo

一、Ubuntu安装muduo muduo库是基于boost开发的&#xff0c;确保先安装了boost&#xff08;对boost版本有要求&#xff09;&#xff0c;还需要先安装protobuf&#xff0c;curl库 库版本boost1.70.0protobuf3.14.0curl7.74.0muduo2.0.2 1、Ubuntu安装boost # 更新资源列表 s…

【2024-2025源码+文档+调试讲解】微信小程序的城市公交查询系统

摘 要 当今社会已经步入了科学技术进步和经济社会快速发展的新时期&#xff0c;国际信息和学术交流也不断加强&#xff0c;计算机技术对经济社会发展和人民生活改善的影响也日益突出&#xff0c;人类的生存和思考方式也产生了变化。传统城市公交查询管理采取了人工的管理方法…

【第0005页 · 贪心】非递减数列

【前言】本文以及之后的一些题解都会陆续整理到目录中&#xff0c;若想了解全部题解整理&#xff0c;请看这里&#xff1a; 第0005页 非递减数列 今天我们来看一道 LeetCode 上“广泛好评”的一道 Easy 题&#xff01;&#xff01;&#xff01;&#xff08;蓝色是 OJ 平台&…

CentOS 7 升级 OpenSSH 9.8p1

背景 OpenSSH 官方发布安全通告&#xff0c;披露CVE-2024-6387 OpenSSH Server远程代码执行漏洞。 环境 操作系统内核openssh 版本openssl 版本7.8.20033.10.0-11277.4p11.0.2k-fips 安装编译软件和下载 OpenSSH9.8p1 软件包 mkdir -p /data/software cd /data/software/…

安卓13 鼠标右键作返回键,鼠标事件修改

总纲 android13 rom 开发总纲说明 前言 目录 1.前言2.问题分析3.代码分析4.代码修改5.编译1.前言 像有些设备是使用鼠标操作的,但是默认的android 鼠标右键并不是像电脑一样,能够做到,左键是确定,右键是返回

足球比分预测分析理论

走进一个充满欢呼声的体育场&#xff0c;空气中充满了对即将到来的两支强大足球队之间对决的期待。一个引人入胜的问题浮现出来&#xff1a;我们可以在比赛开始之前预测比赛的结果吗&#xff1f; 本文提出了一个基于概率和统计的模型&#xff0c;通过考虑每支队伍的进攻和防守能…

9.2-模型量化学习内容

量化简介 量化是将模型浮点数变为定点数运行的过程。 基本概念 &#xff1a;模型量化可以减少模型尺寸&#xff0c;进而减少在推理时的内存消耗&#xff0c;并且在一些低精度运算较快的处理器上可以增加推理速度。 常见方法&#xff1a;工业界目前最常用的量化位数是8比特&a…

期权权利金要多少?期权保证金和权利金有什么区别?

今天带你了解期权权利金要多少&#xff1f;期权保证金和权利金有什么区别&#xff1f;期权交易中&#xff0c;保证金和权利金是两个核心概念&#xff0c;它们在交易过程中扮演着不同的角色。 期权权利金一般是多少&#xff1f; 期权的买方叫权利金、期权的卖方叫保证金&#…

【Altium Designer脚本开发】——PCB平面绕组线圈 V1.4

PCB平面绕组线圈工具用于生成平面电机线圈&#xff0c;应用场景可参考平面电机的书籍、CNKI论文或平面电机的视频。此工具运行环境在Altium Designer中&#xff0c;可用于Altium Designer全系列的版本中。 以下工具可以定制和试用 原理图文档处理工具 ➡️物料编码自动查找工具…

Fluent 超音速射流噪声仿真分析

1. 摘要 本算例使用ANSYS Fluent 19.0软件&#xff0c;对超音速射流流动进行直接气动声学仿真&#xff0c;文档内包含详细的网格导入、模型选择、材料物性、边界条件、求解参数、后处理的设置。通过仿真计算获得射流流场和射流噪声。 2. 案例描述 本算例仿真的案例为以简化…

【Unity】子物体旋转变形的解决方法

如图&#xff0c;如果父物体缩放不为&#xff08;1, 1&#xff0c;1&#xff09;&#xff0c;则子物体如果有旋转量&#xff0c;则会在对应坐标轴上产生变形。 解决方案是&#xff1a; 在子物体和父物体节点间多增加一个空节点&#xff0c;保证空节点的世界坐标下的缩放为&…

livox MID-360调试(解决ip设置问题)

整体的调试思路参考大疆Livox Mid360 使用指南_mid360中的imu内参-CSDN博客这篇博客。 但是在调试过程中出现了ip地址设置不对导致的报错&#xff1a; 1.livox viewer中看不到点云数据 2.livox SDK2 bind error。 joeyjoey:~/slam/Livox-SDK2/build/samples/livox_lidar_qu…

hugging face 利用现有模型进行预测

本篇主要记录下如何使用hugging face来快速找到我们需要的模型&#xff0c;并通过已有模型来快速实现demo或是功能模块。贴上hugging face 官网链接&#xff1a; hugging face model界面(需要魔法)需要模板代码请跳转流程总结 目录 1.选择模型 2.创建模型处理器对象 3.使用…

JAVA呵护晚年从智慧开始养老护理代办陪诊陪护小程序

呵护晚年&#xff0c;从智慧开始 —— 养老护理代办陪诊陪护小程序全解析 &#x1f475; 【开篇&#xff1a;岁月静好&#xff0c;智慧护航】 随着时光的流转&#xff0c;家中的长辈渐渐步入晚年。如何给予他们最贴心的关怀与照顾&#xff0c;成为了我们心中的头等大事。今天…

【Linux】08.Linux 下的第一个小程序——进度条

一、知识引入 在写我们的进度条之前需要先引入一下小的知识点&#xff1a;换行与回车&#xff0c;认识缓冲区&#xff0c;认识格式化输出 1.1 换行与回车 换行指的是跳转到下一行的同一个位置 回车指的是跳转到本行开头的位置 新起一行就是换行回车 但是日常生活中我们见到的…

驾驭Python与MySQL的桥梁:pymysql的神秘面纱

文章目录 **驾驭Python与MySQL的桥梁&#xff1a;pymysql的神秘面纱**背景&#xff1a;为何选择pymysql&#xff1f;库的简介安装指南简单的库函数使用方法场景应用常见问题与解决方案总结 驾驭Python与MySQL的桥梁&#xff1a;pymysql的神秘面纱 背景&#xff1a;为何选择pym…

Django Admin对自定义的计算字段进行排序

通常&#xff0c;Django会为模型属性字段&#xff0c;自动添加排序功能。当你添加计算字段时&#xff0c;Django不知道如何执行order_by&#xff0c;因此它不会在该字段上添加排序功能。 如果要在计算字段上添加排序&#xff0c;则必须告诉Django需要排序的内容。你可以通过在…

Ollama-AI大模型本地运行工具,Windows安装详细步骤

一、关于Ollama 1.1 简介 Ollama是一个专为在本地环境中运行和定制大型语言模型而设计的工具。它提供了一个简单而高效的接口&#xff0c;用于创建、运行和管理这些模型&#xff0c;同时还提供了一个丰富的预构建模型库&#xff0c;可以轻松集成到各种应用程序中。Ollama的目…