2023年开发踩坑记录

2023-08-23 一次看似简单的跨域异常处理

1、问题场景

场景很简单,在已有的服务中新加几个接口,已有的服务全部的基础配置都是稳定可用的(已在线上跑了很久了);前端项目为单独的一个新项目,联调开发阶段都没问题后,部署测试环境时,出现了一个很小的问题,接口跨域了。这种小问题比较简单,定位也比较明确,少配置的加配置就完事,但是,配置都是全的,这就很尴尬了。

2、问题排查与解决

  1. 第一步,当然是看配置了,稳定运行,配置不一定全,又很尴尬,没少配置,其他的前端项目也有调用这个后端服务的接口;

  2. 第二步,检查前端请求参数,请求头信息,发现前端没有传递 Origin 这个请求头,那么这个请求头有什么用呢?后端项目使用了 com.thetransactioncompany.cors 包下面的com.thetransactioncompany.cors.CORSFilter过滤器,

    具体的执行过滤器是:**com.thetransactioncompany.cors.CORSRequestHandler#CORSRequestHandler**下面的这个方法,当没有**Origin** 这个请求头的时候,是会出现跨域问题
    ![image.png](https://img-blog.csdnimg.cn/img_convert/d2fbab211fca77b4bfb237712fb9e766.png#averageHue=#2f2c2c&clientId=ue070923b-c2c2-4&from=paste&height=606&id=u89d2e82e&originHeight=606&originWidth=1016&originalType=binary&ratio=1&rotation=0&showTitle=false&size=86969&status=done&style=none&taskId=u46050a5f-5f92-4b9b-8e37-467b6705d70&title=&width=1016)
    
  3. 与前端沟通后,前端打开了下面的配置,加上了Origin 这个请求头

    image.png

  4. 加上去后,浏览器看不到跨域CORS异常了,出现了以下异常,这就和跨域不沾边了,触发了俩次请求一次预请求,一次正式请求,预请求通过后,才会正式请求接口:

image.png2023年开发踩坑记录_第1张图片

  1. 一看,没见过,是时候面向网络编程了,一查,是谷歌浏览器做了限制,并且一些以谷歌浏览器为内核的浏览器也会出现该问题:具体问题就是这个情况:Chrome安全策略更新之私有网络访问(Private Network Access, PNA)

原始链接
2023年开发踩坑记录_第2张图片

  1. 问题找到了,修改浏览器配置后项目可以正常访问了,我的浏览器版本是114.0.5735.110(正式版本) (64 位),也有方案说后端响应时添加响应头,上面的文章中有,我实践后没有生效,以下方案我均有测试,但均未生效:

    1:增加响应头–无效;
    2:增加响应头+用自定义过滤器进行跨域处理并处理options请求—无效;
    3:还有使用一个spring boot的预请求配置项— 无效
    4:还有一些前端手段,但是需要前端开发调试测试。。。

  2. 根本原因就是网络的问题,因为没有服务器权限,没有办法使用nginx代理的方式去做实验,或者都改为https请求,理论上也可以解决该问题。

2023-04-27 InputStream流再次读取为空异常

1、问题场景:

在进行oss文件上传时,要先读取一次InputStream流,进行文件内容校验,然后再读取一次InputStream流;
使用oss的skd上传到oss,但是在上传至oss时,抛出了空指针异常,但是在校验文件时没有抛出空指针异常,
也就是说InputStream是有值的,那么为什么第二次读取会异常

2、问题排查与解决:

这一次问题比较明确,就是第一次读取正常,第二次读取InputStream流的时候抛出空指针异常;
问题肯定存在读取的地方,我使用了 IOUtils.copy(inputStream, outputStream)的方式读取的流;
进入看代码就一目了然了,InputStream里有个偏移量的标志,读取流时需要根据偏移量读取
2023年开发踩坑记录_第3张图片
2023年开发踩坑记录_第4张图片

第一次读取的时候偏移量会从0递增,且不会重置,第二次读取的时候是从该流的最大偏移量读取的,
自然什么都读不到了,就抛出了空指针异常,可以通过流的reset()方法重置偏移量,就可以正常读取了;
但是InputStream不支持reset(),只能使用其子类实现,可以参考如下方式:

//可以重复读取流
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
IOUtils.copy(inputStream, byteArrayOutputStream);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
byteArrayOutputStream.reset();
byteArrayInputStream.reset();

20230424 分页查询mysql,分页数据重复问题

1、问题场景:

在一次的分页数据查询时,出现了以下情况:第二页的前几条数据为第一页的最后几条数据,且数据总数正常

2、问题排查与解决:

1、怀疑为自定义分页sql逻辑有问题,取出日志中的分页sql运行后发现,sql执行的分页结果就会重复
那么应该就是mysql的一些默认机制导致的该现象,搜索阅读文档后,发现mysql5.6及以后的版本,没加排序时
是会出现该现象,加排序后解决。

3、问题总结

在MySQL 5.6的版本上,优化器在遇到order by limit语句的时候,做了一个优化,即 使用了priority queue。使用 priority queue 的目的,
就是在不能使用索引有序性的时候,如果要排序,并且使用了limit n,那么只需要在排序的过程中,保留n条记录即可,
这样虽然不能解决所有记录都需要排序的开销,但是只需要 sort buffer 少量的内存就可以完成排序。
之所以MySQL 5.6出现了第二页数据重复的问题,是因为 priority queue 使用了堆排序的排序方法,
而堆排序是一个不稳定的排序方法,也就是相同的值可能排序出来的结果和读出来的数据顺序不一致。
MySQL 5.5 没有这个优化,所以也就不会出现这个问题。
也就是说,MySQL 5.5是不存在本文提到的问题的,5.6版本之后才出现了这种情况。再看下MySQL解释sql语言时的执行顺序:

   (1)     SELECT 
   (2)     DISTINCT <select_list>
   (3)     FROM <left_table>
   (4)     <join_type> JOIN <right_table>
   (5)     ON <join_condition>
   (6)     WHERE <where_condition>
   (7)     GROUP BY <group_by_list>
   (8)     HAVING <having_condition>
   (9)     ORDER BY <order_by_condition>
   (10)    LIMIT <limit_number>

执行顺序依次为 form… where… select… order by… limit…,由于上述priority queue的原因,在完成select之后,
所有记录是以堆排序的方法排列的,在进行order by时,仅把view_count值大的往前移动。
但由于limit的因素,排序过程中只需要保留到5条记录即可,view_count并不具备索引有序性,所以当第二页数据要展示时,
mysql见到哪一条就拿哪一条,因此,当排序值相同的时候,
第一次排序是随意排的,第二次再执行该sql的时候,其结果应该和第一次结果一样。

4、参考资料

https://developer.aliyun.com/article/869044
https://zhuanlan.zhihu.com/p/102980603

20230424 Feign rpc调用long类型参数自动转换为String

1、问题场景:

场景如下,目前有两个微服务A、B,两个微服务依赖公共的请求体包;
A需要通过Feign调用B微服务的的接口,请求体如下

   {
     "title": "test",
     "dataList": [
       12345678910,
       123456788910
     ]
   }

请求体中的dataList,是一个long类型的数组,但是在B服务接收时,接收到的参数是下面这样的:

    {
     "title": "test",
     "dataList": [
       "12345678910",
       "123456788910"
     ]
   }

2、问题排查与解决:

1、先补充A、B服务的接口请求日志,打印发送出去的请求参数与接收到的请求参数具体是什么,
发现A发送的时候是正常的,但是B接收到的就已经变成了字符串数组;那么应该是序列化和反序列化上出了问题;
2、要知道服务A发送请求时,需要将参数序列化,服务B接收到请求时,需要将参数反序列化;
但是反序列化只是把二进制内容变成了java对象,应该不会对数据类型做什么处理,
那就是服务A在发送数据时序列化的问题
3、然后就一步步的debug往里调试,最终发现了在序列化时,将long类型转换成了String类型

3、问题总结

那为什么会要把long类型字段转成String?开始检查项目中的序列化配置,发现的确配置了这个序列化规则,
会把Long类型转换为String;原因就是过长的long类型返回给前端的时候,前端会出现数据精度丢失;
所以转成了String,就不会丢失精度了,而此处的Feign调用没有配置序列化规则,使用了Spring默认的配置;
就出现了上述的问题,修改之后恢复正常;

20230407 记一次接口速度调优过程

1、问题场景:

有一个历史综合信息查询接口,响应速度很慢,已经到了快10s才会响应,导致前端界面加载过慢,必须得优化。

2、问题排查与解决:

   列表查询的业务比较明确,即查询一个主体的不同的附属信息,需要查询mongo、mysql以及rpc远程接口调用,才能获取到全部信息;
   分析业务代码后,发现在查询到基础数据后,会进行for循环调用查询mongo、mysql、以及调用远程rpc接口,这都有可能降低响应速度;
   排查后大概以下业务可能会导致响应慢:
     1、循环调用rpc接口查询了主体日志信息,且该rpc接口中查询了mysql,然后累加获取到mysql数据;
     2、循环查询mongo,且使用了类似sql查询,limit1的操作
   针对以上问题进行优化:
     1、rpc接口改为一次查询多条数据,并在获取结果时使用sql对数据进行聚合统计;此处修改后,响应速度平均在4s左右
      降了一大半;
     2、循环查询mongo,且使用了类似sql查询,limit1的操作,此处改为使用mongo聚合查询,类似sql中的group by+sum
      此处修改后,速度优化了300ms左右,接口响应速度平均大概在3.7s左右
  目前根据业务分析不出来哪里还可能需要优化了,开始上科技:arthas
      ```trace -E class_path method|method2|.... ```
     1、先直接用arthas的trace命令追了接口,发现追踪到的一些方法耗时都很短,基本就是几毫秒,不会影响速度;
	    那么就是有方法没有追到;
	 2、仔细排查业务代码,发现如果类的set方法中的入参是一个方法,那么这个方法就不会被追踪到,这些比较隐蔽;
	    花了点时间一个个都找了出来,然后再用trace进行追踪,慢的方法一目了然。
	 3、也是在for循环的时候进行查询了mongo,查出每一个主体信息在该mongo中的全部数据,然后根据查出来的数据进行分类统计;
	    这样如果某一个主体在mongo中有大量数据时,就会非常耗时,数据量小的主体,响应速度就很快;

3、问题总结

 开发业务时,不要先急着动手,要想清楚大概业务逻辑,可以适当写一些todo list,将业务列出来    
 这样可以明确哪些数据可能需要批量查询,哪些业务的数据需要聚合查询,哪些业务的统计数据可以提前做,而不是实时查实时统计等
 要尽量避免以下操作:
   1、循环调用rpc接口;
   2、循环查询任何数据库;
   3、对大量历史数据进行查询并实时统计。

20230329 异步注解导致RequestContextHolder.getRequestAttributes()获取请求信息空指针异常

1、问题场景:

正常业务中使用RequestContextHolder.getRequestAttributes()时可以获取到请求头中的一些用户信息,
在一次接口优化时,需要优化响应时间,目前网关设置响应时间默认为2分钟,但涉及要处理的数据量大时,
2分钟不够了,且直接返回了超时异常(实际业务正在处理中),导致用户体验很差,
于是将部分耗时业务使用异步的方式去处理(该业务为实际处理数据),先返回结果,提示处理中;
但是异步线程中使用了RequestContextHolder.getRequestAttributes()获取时无法获取用户信息,就抛出了空指针异常

2、问题排查:

1、空指针异常较好排查,直接把报空的对象打印一下,发现通过RequestContextHolder.getRequestAttributes()获取的对象就为空;
但是不是用异步注解时,程序无异常

3、问题原因

1、问题原因比较明显,使用异步注解时,会开启一个新线程去执行业务,新线程中没有传递Request相关的信息;
那么在新线程中肯定无法获取到相应的值使用如下方法可以解决:
1:将要获取的对象作为值传递到异步方法与业务中,但不会从根本上解决问题;
2:在使用异步方法前的业务加上如下代码:

  RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true);
     通过该方法可以将主线程的上下文数据共享给子线程
	 RequestContextHolder内部也是使用了俩个ThreadLocal去存储数据
	 	private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
		new NamedThreadLocal<>("Request attributes");

        private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
		new NamedInheritableThreadLocal<>("Request context");
		NamedInheritableThreadLocal为可继承线程,子线程会继承改线程中的内容,当setRequestAttributes设置为true时
		子线程就可以使用父线程中的RequestAttribute信息了。

20230309 记录点:mongoDB默认区分大小写

20230214 @Aspect日志拦截器无法拦截api请求异常

1、问题场景:

在排查一次线上问题时,突然发现异常的接口(a)没有输出请求与响应日志,但是其他接口(b)请求响应日志正常输出,
且这俩个接口在同一个controller中

2、问题排查

1、a、b两接口在同一个controller中,那么先检查@Aspect日志拦截器,看是否做了特殊处理,
排查后发现没有特殊处理;
2、对比两个接口,使用的注解、路径、请求参数等信息,均无异常;
在反复对比的过程中,发现a接口没有修饰符,而b接口有public修饰符;
a接口加上public修饰符后,日志拦截器正常工作;

3、问题原因

1、没有加修饰符时,api提供类中的方法的作用域为default,其他包无法访问;
也就导致了日志拦截类没有访问到该api,定义为public时,就可以拦截到了。

20230210 spring cloud gateway集成skywalking抛出空指针异常,且网关无法访问所有服务并产生跨域

1、问题场景

原有服务模块需要加入skywalking链路追踪相关插件,但是在网关中加入skywalking-agent时,网关启动后无法访问其他服务;
且接口无法调用,不是超时就是跨域,去掉agent后,网关恢复正常;这种现象只有部署在容器中时会出现;
本地启动网关,加入agent进行链路追踪时不会出现该现象。

2、问题排查

   1、由于只在容器中出现该问题,无法本地调试,只能根据异常日志定位问题;
      但是抛异常的代码块为skywalking的gateway插件,追踪相应代码发现响应对象为空导致的;
	  那么就是因为跨域无法获取到响应,然后追踪的时候网关插件抛出了空指针异常。
   2、根据上述排查,可能为agent插件问题,插件更新替换后还有该问题;
   3、根据日志在网上查询,有类似空指针的问题,但均不符合;
   4、于是通过github上搜索skywalking的issue,看是否有类似的问题,问题是有,
      但被归结为插件问题,需要自行调试测试(由于网络问题和依赖问题,无法进行本地调试)
   5、这里开始回归代码,检查原有网关的配置和依赖等问题,但由于是老代码,依赖复杂,没有注释说明;
      排查无法进行;
   6、多次试验无果后,决定重新写一下网关模块,单独起项目,实现原有网关的所有业务功能,但是没有
      增加限流插件和限流配置(原有同时网关使用了redis限流和sentinel限流)
   7、重新部署新网关模块后,通过新网关访问接口时,问题消失了。。。
   8、尝试在github请教skywalking的共享者,对方表示从未见过这样的问题。。。
      附上链接(https://github.com/apache/skywalking/discussions/10357)

3、问题原因

   1、猜测可能为限流模块和其限流配置导致的该问题,但为什么会产生这样的问题,无从定位,
      只能通过一点点移除原有网关的限流模块,发布并验证了。

** 已彻底解决 ** :skyagent的插件包中,有gatew2.x和3.x的插件,启动时使用了2.x的插件,发送了上述问题,但项目中使用的gateway版本对应的应该是3.x的插件,为什么使用到了2.x的插件还有待排查,且依赖树中spring cloud gatew的版本均一致,没发现低版本gateway依赖

20230207 skywalking agent引入后,项目启动时Controller初始化异常

1、问题描述

  • 项目引入skywalking agent后,部署服务时启动失败,抛如下异常
  • 本地引入skywalking agent后,项目可正常启动
  • skywalking agent版本为8.9.0
    2023年开发踩坑记录_第5张图片

1.1 异常日志核心内容为实例化bean失败,缺少需要空参构造,但在本地引入agent后没有出现该异常

	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1270)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1164)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:846)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:863)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248)

1.2 controller代码如下,此处采用了构造注入的方式实例化Service

2023年开发踩坑记录_第6张图片

2、问题排查

2.1 skywalking-agent版本与插件检查

  1. 本地的skywalkingagent无异常,docker镜像中使用的skywalking-agent会出现该异常,俩个agent版本均一致,唯一不同点在于镜像中的agent移除了不用的插件,添加了部分插件,与本地agent插件不同,怀疑可能是镜像中添加的插件影响导致,开始对比插件;
  2. 对比移除相应添加插件后,问题依旧无法解决,直接把本地agent中的全部插件复制到镜像中后,服务可正常启动,那么就不是添加插件导致的问题,而是缺少插件导致的问题;
  3. 由于插件过多,有八九十个,无法明确的找到缺少什么插件,于是去skywalking的github上看下是否有人遇到过相同的问题,进查询后崅实有类似的问题,虽然原因各不相同,但核心内容均为实例化类失败,猜测可能为缺少spring插件导致;
  4. 搜索本地agent的spring相关插件,根据插件名找出相关性较高的插件,猜测可能为如下几个插件
    2023年开发踩坑记录_第7张图片
  5. 一个个引入,然后启动验证,最终定位到了是缺少了apm-spring-core-patch-8.9.0这个插件

3、问题原因

  1. 在上面的controller代码中,使用了构造注入的方式实例初始化,这就导致了初始该类时需要空参构造,然而代码只提供了有参构造,实例化找不到构造函数,就抛出了异常;
  2. 此时也可以通过增加空参注解去解决,但是涉及到很多代码的改动,故抛弃;
  3. 那么为什么引入了apm-spring-core-patch-8.9.0这个插件就可以正常运行启动呢?把这个包解压后,看下代码做了什么事情,发现了如下一段代码,在这段代码中,进行了实例化增强,对所有有构造函数的类先进行了初始化,并存入缓存,这样在spring实例化该类时,先在缓存中查到了该类,然后直接取出,就不会抛出实例化失败的异常了:
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;

public class AutowiredAnnotationProcessorInterceptor implements InstanceMethodsAroundInterceptor, InstanceConstructorInterceptor {
    public AutowiredAnnotationProcessorInterceptor() {
    }

    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
    }

    public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object ret) throws Throwable {
        Class<?> beanClass = (Class)allArguments[0];
        if (!EnhancedInstance.class.isAssignableFrom(beanClass)) {
            return ret;
        } else {
            Map<Class<?>, Constructor<?>[]> candidateConstructorsCache = (Map)objInst.getSkyWalkingDynamicField();
            Constructor<?>[] candidateConstructors = (Constructor[])candidateConstructorsCache.get(beanClass);
            if (candidateConstructors == null) {
                Constructor<?>[] returnCandidateConstructors = (Constructor[])((Constructor[])ret);
                if (returnCandidateConstructors != null) {
                    candidateConstructors = returnCandidateConstructors;
                } else {
                    Constructor<?>[] rawConstructor = beanClass.getDeclaredConstructors();
                    List<Constructor<?>> candidateRawConstructors = new ArrayList();
                    Constructor[] var12 = rawConstructor;
                    int var13 = rawConstructor.length;

                    for(int var14 = 0; var14 < var13; ++var14) {
                        Constructor<?> constructor = var12[var14];
                        if (!Modifier.isPrivate(constructor.getModifiers())) {
                            candidateRawConstructors.add(constructor);
                        }
                    }

                    if (candidateRawConstructors.size() == 1 && ((Constructor)candidateRawConstructors.get(0)).getParameterTypes().length > 0) {
                        candidateConstructors = new Constructor[]{(Constructor)candidateRawConstructors.get(0)};
                    } else {
                        candidateConstructors = new Constructor[0];
                    }
                }

                candidateConstructorsCache.put(beanClass, candidateConstructors);
            }

            return candidateConstructors.length > 0 ? candidateConstructors : null;
        }
    }

    public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable t) {
    }

    public void onConstruct(EnhancedInstance objInst, Object[] allArguments) {
        Map<Class<?>, Constructor<?>[]> candidateConstructorsCache = new ConcurrentHashMap(20);
        objInst.setSkyWalkingDynamicField(candidateConstructorsCache);
    }
}

20230129 xxljob日志抛出中文异常导致任务无法停止,一直重试

xxljob执行的时候抛出的异常日志包含中文字段

重新拉取解析重试的时候 无法解析成json

所以就一直出错 一直重试

在这里插入图片描述

20230113 restTemplate调用接口数据返回缺少异常

1、问题场景

业务代码中,通过restTemplate.getForEntity(url,Map.class)调用接口时,出现了以下情况;
返回的json对象的list中,全部只有一条数据,但是服务提供方响应数据的时候确认了每个list中
都有多条数据。

2、问题排查

1、刚开始怀疑服务提供方数据返回异常,于是查看服务提供方的接口响应日志,发现并没有问题;
每个list中数据量均正常;
2、在服务调用方打印响应结果,发现打印出的结果中,list数据已经减少,那么问题即出现在服务调用方;
定位到问题出现位置,开始排查为什么缺少,debug时发现解析响应数据时,出现了数据丢失的问题;

3、问题原因

  查了一些资料,大概是如下的问题:
  举个个例子:是map中嵌入list,Map中Object一旦映射的是List,自动映射的返回数据只会返回List的最后一条数据,
              原因是map.put()的键相同,导致覆盖。进而导致数据缺失。
              使用java自动映射的对象类型:Map.class  List.class  Object.class等,必须保证接口返回的数据类型是单一的某一种,
			  (无论数量多少,只要种量为一即可),而不能是多种数据类型嵌套的复杂数据。一旦为复杂数据只能手动创建对应实体类。
  总的来说就是远程接口返回的数据类型是使用了泛型,或者返回数据所使用的实体类中数据类型不唯一,
  那么restTemplate接收的时候也只能使用对应实体类,而不能让java自己去映射。

4、解决方案,目前有如下俩种:

1、配置restTemplate序列化配置,具体方案可以自行查询;
2、通过restTemplate的exchange方法来解决:可以参考如下代码:

    public void queryTest(String name) {
    //CommonResponse和CustomDTO为自定义响应类
     ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() {};
     //设置Http的Header
     HttpHeaders headers = new HttpHeaders();
     headers.setContentType(MediaType.APPLICATION_JSON_UTF8);

     //设置访问参数
     HashMap params = new HashMap<>();
     params.put("name", name);

     //设置访问的Entity
     HttpEntity entity = new HttpEntity<>(params, headers);

     CommonResponse response = restTemplate.exchange("url" , HttpMethod.GET,entity,typeRef).getBody();
     log.info("queryOrderHeaderTest响应json,[{}]",JSONObject.toJSONString(response));
     return response.getData();
 }

5、参考链接:

https://www.bbsmax.com/A/1O5EY7PW57/
https://www.cnblogs.com/yzyBalance/p/13546552.html
https://blog.csdn.net/qq_37855749/article/details/117691268

20230105 通过网关调用服务接口,接口间歇性异常返回500

1、问题场景

通过网关调用某模块接口时,接口偶现500异常,connection time out ,

2、问题排查

1、网关与服务模块为容器化部署,怀疑可能容器内网络有问题,排查后网络正常;
2、查看调用服务模块日志,服务正常,且没有异常日志输出;
3、查看网关模块,网关输出日志如下:显示服务网络不可达;排查网关后发现网关nacos配置
可能有问题,修改部署后,问题依然存在;
4、nacos排查,查看相应模块服务有没有注册到nacos,注册状态也正常;
于是在发生异常时,查看nacos服务状态,nacos服务健康状态为false,即服务不健康
但是查看相应服务模块,服务模块运行正常,且在几分钟后,nacos健康状态自动恢复;
将nacos中对应服务下线后,发现又有服务注册上来,但是这个服务只启动了一个,
删除该服务,重新注册后,问题修复。

3、问题原因:

原因就是nacos中注册的服务与实际服务不符,具体原因不明,猜测可能为nacos缓存问题,
下线的服务没有全部删除,导致新注册的服务ip被覆盖,忽隐忽现可能为nacos服务发现机制与缓存
相互覆盖导致。

你可能感兴趣的:(踩坑合集,java,spring,boot)