你好,我是Qiuner. 为帮助别人少走弯路和记录自己编程学习过程而写博客
这是我的 github https://github.com/Qiuner ⭐️
gitee https://gitee.com/Qiuner 🌹
如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 😄 (^ ~ ^)
想看更多 那就点个关注吧 我会尽力带来有趣的内容 😎
- 这篇有不少bug记录与方便您复制的代码,相信一定能节省学习时间同时达到更好的效果
- 本博客要与原文档搭配使用 day04-微服务02 - 飞书云文档 (feishu.cn)
- 这个是重置版,因为我发现有很多东西都没有讲明白,于是我又写了重置版。原版也不会删除,因为重制版不是全部照搬
- 我这一版写的可以说是付出很多心血了,里面网关、项目查错思路,保证您一看就会😎
黑马程序员的官方文档和他配套视频讲的不是同一个东西,不要直接复制文档照着抄
2024最新SpringCloud微服务开发与实战,java黑马商城项目微服务实战开发(涵盖MybatisPlus、Docker、MQ、ES、Redis高级等)Day 4 重制版
文章目录
- 2024最新SpringCloud微服务开发与实战,java黑马商城项目微服务实战开发(涵盖MybatisPlus、Docker、MQ、ES、Redis高级等)Day 4 重制版
- 项目查错思路分析
- 网关
- 使用OpenFeign 传递用户
- 配置管理
项目查错思路分析
一:发现/复现问题
- 点击加入购物车,显示添加购物车失败,多点几个不同的,发现都失败
- 打开控制台,发现这个500服务器内部报错
- 项目的api是这个添加的前缀,访问什么都会有
二:查看报错
- 这个问题比较简单,看报错就出来了,但很多时候开发中很多问题是没有报错的,其实我刚开始写这个项目差错分析的时候就是觉得能这个会是没有报错,,,想拿真实例子来展示下没有报错要怎么办
三:查看是否是操作不当导致问题出现
因为是显示没有用户id,因此我们很容易就能想到是因为没有进行用户登录,因此导致没有传递用户id,所以检查是否是在登录的情况下进行的操作
发现是登录下,依然还是添加失败。(这里可能存在误解,因为我们还没确认是否是前端的问题,所以不要看前端,而是要看请求是否成功。我这里是确认前端已经没有问题了)
四:定位问题代码
- 因为现代开发将数据处理分为了三个层,因此我们需要在能打印的时候打印出来,看看是那一步开始没有了userId
- 但在这里,添加购物车方法,很显然没有接收userId参数
- 因此去服务层看看
- 这个时候就可以下出判断了,这是一个微服务项目,各个模块是彼此分散的,而这里是将用户信息去上下文中去取出来,因此这是一个其他模块没有将用户存储在上下文导致的错误
- 一层错误(表面错误): 数据库没有userId字段,无法进行查询
- 二层错误(truth错误):网关没有将用户id在登录的时候存储好
五:仔细查看代码
- 这一段网关保存用户登录信息的逻辑其实挺长的,线程存储和请求头添加用户绕来绕去的
- 官方文档的代码和官方视频代码不是完全相同的,不要照着抄
网关
什么是网关
- 网关的意思 和 nat协议差不多的,不知道nat协议的可以看看我这篇博客: (写了 没发)
https://blog.csdn.net/qq_61654952/article/details/139453099?spm=1001.2014.3001.5501 (这篇解释的比较官方,看目录,在最后那边)
网关是如何实现路由转发、身份校验的?
- 说到这里,可能对网关会认识不全面,不明白大概是个什么样。
- 网关其实也是一个微服务模块,这个模块实现了路由转发 身份校验等等功能,和单体项目中的拦截器是一样的
- 使用了网关后,对于访问者来说,所有的东西不能自由地被请求,因为有网关拦截着
快速搭建一个网关
- id: item # 路由规则id,自定义,唯一 uri: lb://item-service # 路由的目标服务,lb代表负载均衡,会从注册中心拉取服务列表 predicates: # 路由断言,判断当前请求是否符合当前规则,符合则路由到目标服务 - Path=/items/**,/search/** # 这里是以请求路径作为判断规则
- 当请求路径是
/items/**
或/search/**
时, 满足Path
条件。符合条件的请求将会被路由从注册中心拉取item-service
的服务列表到目标服务。 - 路由是什么?路由在的位置比较底层,你在配置的路由地址就是路由的一部分,你在告诉路由你要做什么。而这个路由又只是整个网关模块的一部分
一些网关问题的推断方式 感谢出镜的小伙伴
- 看到原本文章里的一个小伙伴说这个,博主虽然没有遇到这个问题,但我知道是什么引起的
推断
- 1、首先 500 表示是服务器的问题,但服务器报出500的问题情况是多样的,仅凭一个500只能得出是服务器的问题
- 2、在这里,是配置网关后,与使用网关的行为后报错 500,而原模块没有产生问题 因此,可以判断 问题出在网关上
- 3、网关目前只进行了基础配置,配置了路由、端口号、nacos,显然,端口号不会出现这个问题。而nacos,出问题应该也是自己出问题,不是影响整个网关,因此定位到路由。
- 4、根据以上得出粗略推断:路由写的有问题
网关路由配置
路由断言
路由过滤器
打印为空
- 响应头和请求头单词长得有的像了,因此很容易分不清
网关登录校验
- 需求描述:对请求进行登录校验,分别设在每个模块是不太合理的,因此将其设在网关层。因此,我们需要了解网关的运行过程,从而判断在哪一步编写相关登录校验
网关中自定义过滤器 全局和局部过滤器
GlobalFilter全局
package com.hmall.gateway.filters; import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Http; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @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(); return chain.filter(exchange) ; } @Override public int getOrder() { return 0; } }
GatewayFilter局部过滤器
- 这里是在创建抽象工厂,关于抽象工厂不知道的可以看我的另一篇博客
https://blog.csdn.net/qq_61654952/article/details/138126798?spm=1001.2014.3001.5501
- 使用这个过滤器工厂,需要有FilterFactory后缀
- 多个参数的情况
过滤器在idea中没有输出
- 像视频一样刷新页面,但是没有任何提示
- 这是因为你没有登录
- 你需要登录后网关才能生效
网关中传递用户(在请求中添加用户数据方式)
@ConditionalOnClass(DispatcherServlet.class
是Spring Boot中的一个条件注解,它在特定条件下有选择地加载某些配置或Bean- 在这个例子中注解会检查类路径中是否存在指定的类
DispatcherServlet
。只有当DispatcherServlet
类存在于类路径中时,Spring才会实例化和加载标注了这个注解的配置类或Bean。
网关传递用户的核心
- 首先在网关层中,会有一个东西负责将信息存储到请求头,存储到请求头后,这时候就分为不同的形式了,比如
形式一:每个请求手动添加
- 如图,在每个方法上手动添加,捕获,然后得到userId属性
形式二:给每个模块添加一个拦截器
在拦截器中将用户的id取出来
这样又几个模块就需要加几个代码,也是比较麻烦
形式三:定义一个通用的拦截器(本例使用)
- 通用拦截器可能会遇到没有sprigboot依赖的模块无法中使用会报错的问题,需要使用 @ConditionalOnClass(DispatcherServlet.class) 来检测项目是否存在springboot依赖,不存在则不生效,由此来规避报错
AuthGlobalFilter拦截器无法打印数据而又没有任何报错
这是因为你的前端挂掉了,将前端nginx再启动然后重启一遍服务即可
即我写了打印1 但是不打印1
使用OpenFeign 传递用户
之前传递的方式是用户网关登录,而没登录访问页面会跳转到登录页面,这时候自然将用户信息写进去了
可是这次是交易完,需要删除购物车的商品,这时候,使用网关来进行传递商品信息就有些不妥了,当然你也可以实现用网关来传递
因此,使用OpenFeign来进行用户传递
无报错 复合bug排查解决方法思路 :在api中添加完DefaultFeignConfig等依然无法删除购物车中的商品
一:确认基础配置没有问题
二:确认有无报错
- 其他也是如此 没有任何报错
- 但购物车没有删除
三:查看自己留下来的排错信息
- 居然没有打印,显然,没有调用,于是我们得去找是谁会来调用这个方法。
- 因为我们使用OpenFeign来进行调用的,所以显然得去这里
- 同时,能发现拦截器 并没有生效
- 这时候就能发现关于DefaulFeignConfig的问题,多了个@Configuration的注释
- 去掉后,发现拦截器在运行了,但是点开购物车,发现购物车商品还是没有删除
- 因此判断,这是一个复合bug
四:查看这时有没有报错信息
- 查看了一番,还是没有任何报错
- 不过注意到
- 于是进行全局搜索
- 修改下代码,果然是这个。那这时候我心里有了个猜测了,于是我回去听下业务逻辑
微服务02-09.网关登录校验-OpenFeign传递用户信息_哔哩哔哩_bilibili
- 业务逻辑大概是:
- 网关将用户信息写在请求上
- hm-common中定义UserInfoInterceptor来获取请求中的id
- hm-api中定义DefaultFeignConfig来从上下文中获取id
- 在各个模块中,使用pom将其联系起来(这样模块耦合度太大了)
- 因此,我们可以判断出这些执行的先后顺序。将所有微服务模块重启一遍,然后看具体的日志
先进行登录操作,查看有那些发出了信息
- 可以看到,到这一步都没什么问题
二:点击结算
- 这时候user露出鸡脚了,它没有查询到user_id为2的数据
- 去数据库一看,原来叫id不叫userid,那问题就好办了
但仔细阅读后,发现这是查地址的东西,好像是设计成找不到,然后前端来展示数据
线索又一次断掉了
但能发现,并没有打印删除的提示词,因此查看前端,也没有发现删除的请求,因此发现,应该是OpenFeign的问题
- 一番寻找,发现cart的客户端居然使用get
package com.hmall.api.client; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.RequestParam; import java.util.Collection; @FeignClient("cart-service") public interface CartClient { @DeleteMapping("/carts") void deleteCartItemByIds(@RequestParam("ids") Collection<Long> ids); }
修改后,发现还是没有删除商品
百思不得其解
- 服务注册也没问题
- 接口测试也通过
五:最终问题排查
- 这个时候,就要开始梳理逻辑了,逻辑一梳理,数据库中的数据是原本就有的,而代码使用item_id来进行删除增加购物车中商品,因此我们去查看校对两个值
- 发现值也是一样的,那就先将数据库中的数据全部删除,再次添加结算
- 如此竟可以了
开始清理购物车商品 itemIds的值是[40305713537] 15:59:54:118 DEBUG 39592 --- [nio-8085-exec-1] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] ---> DELETE http://cart-service/carts?ids=40305713537 HTTP/1.1 15:59:54:118 DEBUG 39592 --- [nio-8085-exec-1] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] user-info: 1 15:59:54:118 DEBUG 39592 --- [nio-8085-exec-1] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] ---> END HTTP (0-byte body) 15:59:54:144 DEBUG 39592 --- [nio-8085-exec-1] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] <--- HTTP/1.1 200 (25ms) 15:59:54:144 DEBUG 39592 --- [nio-8085-exec-1] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] connection: keep-alive 15:59:54:144 DEBUG 39592 --- [nio-8085-exec-1] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] content-length: 0 15:59:54:144 DEBUG 39592 --- [nio-8085-exec-1] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] date: Tue, 23 Jul 2024 07:59:54 GMT 15:59:54:144 DEBUG 39592 --- [nio-8085-exec-1] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] keep-alive: timeout=60 15:59:54:144 DEBUG 39592 --- [nio-8085-exec-1] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] 15:59:54:144 DEBUG 39592 --- [nio-8085-exec-1] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] <--- END HTTP (0-byte body) 我是UserInfoInterceptor 我将要把数据存储到线程中 userInfo = 1 15:59:54:235 DEBUG 39592 --- [nio-8085-exec-2] c.h.trade.mapper.OrderMapper.selectById : ==> Preparing: SELECT id,total_fee,payment_type,user_id,status,create_time,pay_time,consign_time,end_time,close_time,comment_time,update_time FROM `order` WHERE id=? 15:59:54:235 DEBUG 39592 --- [nio-8085-exec-2] c.h.trade.mapper.OrderMapper.selectById : ==> Parameters: 1815658079372996609(Long) 15:59:54:243 DEBUG 39592 --- [nio-8085-exec-2] c.h.trade.mapper.OrderMapper.selectById : <== Total: 1 15:59:54:539 INFO 39592 --- [ent-executor-16] com.alibaba.nacos.common.remote.client : [f9caff51-eccd-4556-bcfd-9974893d2976] Receive server push request, request = NotifySubscriberRequest, requestId = 267 15:59:54:539 INFO 39592 --- [ent-executor-16] com.alibaba.nacos.common.remote.client : [f9caff51-eccd-4556-bcfd-9974893d2976] Ack server push request, request = NotifySubscriberRequest, requestId = 267 15:59:54:638 INFO 39592 --- [ent-executor-17] com.alibaba.nacos.common.remote.client : [f9caff51-eccd-4556-bcfd-9974893d2976] Receive server push request, request = NotifySubscriberRequest, requestId = 268 15:59:54:638 INFO 39592 --- [ent-executor-17] com.alibaba.nacos.common.remote.client : [f9caff51-eccd-4556-bcfd-9974893d2976] Ack server push request, request = NotifySubscriberRequest, requestId = 268 我是UserInfoInterceptor 我将要把数据存储到线程中 userInfo = 1 16:02:00:026 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] ---> GET http://item-service/items?ids=8533120 HTTP/1.1 16:02:00:027 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] user-info: 1 16:02:00:027 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] ---> END HTTP (0-byte body) 16:02:00:033 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] <--- HTTP/1.1 200 (7ms) 16:02:00:033 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] connection: keep-alive 16:02:00:033 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] content-type: application/json 16:02:00:033 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] date: Tue, 23 Jul 2024 08:02:00 GMT 16:02:00:033 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] keep-alive: timeout=60 16:02:00:033 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] transfer-encoding: chunked 16:02:00:033 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] 16:02:00:033 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] [{"id":"8533120","name":"海尔 (Haier )LQ65H31 65英寸 4K曲面人工智能全面屏超高清LED液晶电视","price":379500,"stock":10000,"image":"https://m.360buyimg.com/mobilecms/s720x720_jfs/t1/2851/14/13998/101463/5bd9723cE81b2c299/1bfdf2d1628648e4.jpg!q70.jpg.webp","category":"曲面电视","brand":"海尔","spec":"{}","sold":0,"commentCount":0,"isAD":false,"status":1}] 16:02:00:035 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] <--- END HTTP (384-byte body) 16:02:00:036 DEBUG 39592 --- [nio-8085-exec-4] c.hmall.trade.mapper.OrderMapper.insert : ==> Preparing: INSERT INTO `order` ( id, total_fee, payment_type, user_id, status ) VALUES ( ?, ?, ?, ?, ? ) 16:02:00:036 DEBUG 39592 --- [nio-8085-exec-4] c.hmall.trade.mapper.OrderMapper.insert : ==> Parameters: 1815658607628808194(Long), 379500(Integer), 3(Integer), 1(Long), 1(Integer) 16:02:00:037 DEBUG 39592 --- [nio-8085-exec-4] c.hmall.trade.mapper.OrderMapper.insert : <== Updates: 1 16:02:00:038 DEBUG 39592 --- [nio-8085-exec-4] c.h.t.mapper.OrderDetailMapper.insert : ==> Preparing: INSERT INTO order_detail ( order_id, item_id, num, name, spec, price, image ) VALUES ( ?, ?, ?, ?, ?, ?, ? ) 16:02:00:038 DEBUG 39592 --- [nio-8085-exec-4] c.h.t.mapper.OrderDetailMapper.insert : ==> Parameters: 1815658607628808194(Long), 8533120(Long), 1(Integer), 海尔 (Haier )LQ65H31 65英寸 4K曲面人工智能全面屏超高清LED液晶电视(String), {}(String), 379500(Integer), https://m.360buyimg.com/mobilecms/s720x720_jfs/t1/2851/14/13998/101463/5bd9723cE81b2c299/1bfdf2d1628648e4.jpg!q70.jpg.webp(String) 开始清理购物车商品 itemIds的值是[8533120] 16:02:00:040 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] ---> DELETE http://cart-service/carts?ids=8533120 HTTP/1.1 16:02:00:040 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] user-info: 1 16:02:00:040 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] ---> END HTTP (0-byte body) 16:02:00:050 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] <--- HTTP/1.1 200 (9ms) 16:02:00:051 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] connection: keep-alive 16:02:00:051 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] content-length: 0 16:02:00:051 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] date: Tue, 23 Jul 2024 08:02:00 GMT 16:02:00:051 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] keep-alive: timeout=60 16:02:00:051 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] 16:02:00:051 DEBUG 39592 --- [nio-8085-exec-4] com.hmall.api.client.CartClient : [CartClient#deleteCartItemByIds] <--- END HTTP (0-byte body)
- 有理由怀疑,这个数字是个上限值之类的。
- 再添加一份商品,然后将item_id修改为这个数字 40305713537,点击结算后居然也行了。。。。
- 那看来是别的字段导致无法删除某个商品,一番查看,果然发现num字段为0,该商品不可删除
配置管理
- 剩下的添加配置直接看我原本的博客吧 原本博客还有个总结三种拦截器
- https://blog.csdn.net/qq_61654952/article/details/140390388?spm=1001.2014.3001.5501