GithubHelp home page GithubHelp logo

blog's People

Contributors

quan7u avatar

Watchers

 avatar

blog's Issues

Prometheus函数irate的误会(速率

同事查看历史监控信息,每天固定七点Nginx监控Processed connections的nginx_connections_handled指标会骤减,担心组件是否有潜在的问题,让我帮忙排查下组件这边是否异常
image

上Nginx的机器上检查了连接数情况,可以看到连接数集中在5001这个端口,属于某个业务组件的流量
image

于是检查该业务组件的请求情况,发现也是在每天7点的时候,请求的量会突增50到100
image

到这里我怀疑是监控指标的问题,再回去看Nginx的监控,顺便问了下GPT
image

由此看来,irate代表的是一段时间内连接变化的速率,而不是连接处理的量,监控是符合预期的

  • 夜晚到凌晨只有一丁点业务量的时候(也就是说连接的建立和断开比较小,波动也小)irate的指标是平缓下降、平缓上升的;
  • 白天业务高峰时(频繁的建立和断开连接,监控指标维持在一个值附近
    image

官方对改函数的解释
https://prometheus.io/docs/prometheus/latest/querying/functions/#irate

😂最近故障频出,大家都比较敏感。
愈发觉得。。。搞懂技术原理、指标含义很重要,懂了才能给内外部、专业/非专业的人阐述清楚问题和现象。

Navicat导入小坑

前因

前几天需求上线前我用Navicat导入了3w号码白名单,正常操作csv>选表>字段匹配
导入任务执行完,看到结果:
已处理:3****
错误:1****
已添加:2****
看日志,基本是唯一索引重复的error。我第一时间想法是提供的csv有重复数据就没理会了。

后果

今天用户投诉说收不到消息了,一查号码在csv中,但是表中没有。
于是我在测试环境又导了一遍,发现导入之后,确实没有这个用户的号码。
image
要注意这里选中扩展即为【批量插入】

这回仔细看了日志,Navicat默认使用的是批量插入数据,如果这个批次中有一个号码重复了,整个批次都不会插入。应该是这个问题导致的。
还好这次看日志比较仔细,还有数据too long的error,增加了varchar的长度。

再次导入后,和csv去重后的数据量对应上了,下次要小心注意!

MongoDB的_id包含文档的插入时间

前言

两个月前临时做了个数据埋点接口,提供给多个业务打点,就是简单的应用id、事件、渠道、时间
上线之后发现偶现时间戳解析出错,原因是某个业务传过来的时间戳少了一位。于是增加了时间戳的判断,给特殊的入参补0,修复发版,这时候埋下一个坑:解析完没有赋值time字段,导致两个月的数据没有上报时间!等到业务方有提数需求找过来,才发现。。。后背有点发凉
image

_id解析插入时间

两个月有点长,从日志、监控都不能拿到日志时间了,就算拿到也需要额外写程序。能恢复是最好的,可惜日志也没有保留那么长时间。
之前做短链的时候研究过雪花算法,生成的id有一部分是时间,按理说通过id也能反向解析出时间。于是把希望寄托在MongoDB的
_id上。
果然。。MongoDB的_id也是类似雪花算法,

_id 是mongoDB中唯一的主键,MongoDB中存储的文档必须有一个"_id"键, 无论用户是否设置都将自动生成,其类型为ObjectId. 在一个集合里面,每个文档都有唯一的"_id"值,来确保集合里面每个文档都能被唯一标识。
ObjectId 是一个12字节 BSON 类型数据,有以下格式:
0|1|2|3 | 4|5|6 | 7|8 | 9|10|11
时间戳 | 机器 | PID | 计数器

用以下命令,将_id解析出时间赋值给time字段插入到文档,其它字段1表示保留原值
输出到临时集合,这里要注意先创建临时集合,创建好索引!

db.collection.aggregate([
  {
    $project: {
      _id: 1,
      time: { $toDate: "$_id" },
      otherField1: 1,
      otherField2: 1,
      // 添加其他需要保留的字段
    }
  },
  {
    $out: "tempCollection" // 将结果输出到临时集合
  }
]);

执行完上面的命令,时间算是恢复了。。。松了口气,提数需求是没问题了,先把数据导出给到业务

后续修复代码升级后,在原集合修正历史数据即可。

手动复制隐藏字符U-202A、U-202C的坑

背景

在做一个权益领取的需求时,临时替换了一个产品编码
注意:是人工手动复制替换的,我从excel复制到navicat中,还逐字比对,确认无误
第二天就收到用户投诉说:其中有个权益领取不了......

排查

起初看数据库配置、api调用日志,都没发现问题。而且下游返回【未找到相关记录】,我一开始还以为是订购关系退订导致的问题,他们说退订是月底同步的,不会存在此类问题,我才继续排查。

一开始我使用grep,仍然没有发现异样
图片

没办法,为了看得更详细,我使用 less -R 重新逐行看请求日志,然后发现不对劲的地方
图片
编码前后多了两个Unicode字符?

再附上一张此编码在各个工具的显示情况:navicat、studio 3T、记事本、vscode......
图片

解决

直接在库里面替换正确的产品编码,让用户再试试。后续该产品都正常领取,至此问题解决。

溯源

这两个是什么字符?

BiDi 的英文全称是bi-directional language,即双向字符集语言。 这种语言主要包括希伯来语、阿拉伯语和乌尔都语等。 它们的最大特点就是允许双向文本—也就是说,他们的本土语言书写顺序是从右往左,而其中的英文单词或商标符号从左向右显示

Unicode引入了控制方向的字符来适应上述场景,具体可参考下面引用。

为什么会多这两个字符呢?

网上博客大部分说的是在Windows上从右往左复制,会增加U-202A字符。
但是我很记得自己直接在excel上复制单元格(而且之前也没有遇到这样的情况)
所以可能从一开始,这次变动的excel里面就是不正确的编码串了......

参考资料

Unicode 中的 BIDI 双向性算法
Unicode Character (U+202A)
\u202a错误
\u202a 神奇的控制字符

服务器异常关闭导致nacos无法启动

背景

上周护网,所有测试环境关停,今天放开后,nacos集群其中一台机器一直启动失败

排查

Caused by: java.lang.IllegalStateException: Fail to init node, please see the logs to find the reason.
        at com.alipay.sofa.jraft.RaftServiceFactory.createAndInitRaftNode(RaftServiceFactory.java:48)
        at com.alipay.sofa.jraft.RaftGroupService.start(RaftGroupService.java:129)
        at com.alibaba.nacos.core.distributed.raft.JRaftServer.createMultiRaftGroup(JRaftServer.java:260)
        at com.alibaba.nacos.core.distributed.raft.JRaftProtocol.addRequestProcessors(JRaftProtocol.java:163)
        at com.alibaba.nacos.naming.consistency.persistent.impl.PersistentServiceProcessor.afterConstruct(PersistentServiceProcessor.java:79)
        at com.alibaba.nacos.naming.consistency.persistent.PersistentConsistencyServiceDelegateImpl.createNewPersistentServiceProcessor(PersistentConsistencyServiceDelegateImpl.java:112)
        at com.alibaba.nacos.naming.consistency.persistent.PersistentConsistencyServiceDelegateImpl.<init>(PersistentConsistencyServiceDelegateImpl.java:54)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:211)
        ... 157 common frames omitted

nacos.log信息如上,排查范围收缩到raft相关组件

2023-08-25 15:30:26,574 INFO SPI service [com.alipay.sofa.jraft.JRaftServiceFactory - com.alipay.sofa.jraft.core.DefaultJRaftServiceFactory] loading.

2023-08-25 15:30:26,646 INFO SPI service [com.alipay.sofa.jraft.rpc.RaftRpcFactory - com.alipay.sofa.jraft.rpc.impl.GrpcRaftRpcFactory] loading.

2023-08-25 15:30:27,056 INFO SPI service [com.alipay.sofa.jraft.util.JRaftSignalHandler - com.alipay.sofa.jraft.NodeDescribeSignalHandler] loading.

2023-08-25 15:30:27,057 INFO SPI service [com.alipay.sofa.jraft.util.JRaftSignalHandler - com.alipay.sofa.jraft.NodeMetricsSignalHandler] loading.

2023-08-25 15:30:27,058 INFO SPI service [com.alipay.sofa.jraft.util.JRaftSignalHandler - com.alipay.sofa.jraft.ThreadPoolMetricsSignalHandler] loading.

2023-08-25 15:30:27,060 INFO SPI service [com.alipay.sofa.jraft.util.timer.RaftTimerFactory - com.alipay.sofa.jraft.util.timer.DefaultRaftTimerFactory] loading.

2023-08-25 15:30:27,064 INFO The number of active nodes increment to 1.

2023-08-25 15:30:27,302 INFO Starts FSMCaller successfully.

2023-08-25 15:30:27,311 INFO Deleting snapshot /usr/local/nacos/data/protocol/raft/naming_persistent_service/snapshot/snapshot_9.

2023-08-25 15:30:27,315 ERROR Fail to destroy snapshot /usr/local/nacos/data/protocol/raft/naming_persistent_service/snapshot/snapshot_9.

2023-08-25 15:30:27,315 ERROR Fail to init snapshot storage.

2023-08-25 15:30:27,315 ERROR Node <naming_persistent_service/打码:7848> initSnapshotStorage failed.

查看alipay-jraft.log,其中最关键最明显的ERROR日志说明了问题:销毁快照失败

解决

考虑到当初可能是暴力关停服务器,导致相关文件写入中断,没有正常关闭(这里还需要深究。。。)
于是手动将snapshot备份到别的目录,再重试启动
这次nacos启动成功,整个集群正常运行

随想

最近也在看raft相关的知识,才发现nacos也是使用的支付宝的jraft

Lettuce超时问题Command timed out after xxx

前言

接上文 [Lettuce偶现Connection reset by peer](#2)
问题并没有解决,衍生出io.lettuce.core.RedisCommandTimeoutException: Command timed out after 1 second(s)的问题
挖个坑,后续再写。。

异常中after x second(s),这个x源于组件的连接配置,默认应该是10s

复现

此问题与 redis/lettuce#2082 是同一个问题,参照原po主的复现步骤即可复现

1、iptables屏蔽redis端口流入、流出的数据包

iptables -A INPUT -p tcp --dport 6377 -j DROP
iptables -A OUTPUT -p tcp --sport 6377 -j DROP

2、等待15分钟(926.4s)「注意这个时间」

3、客户端操作redis,异常信息以及Wireshark抓包记录如下

io.lettuce.core.RedisCommandTimeoutException: Command timed out after 10 second(s)
    at io.lettuce.core.internal.ExceptionFactory.createTimeoutException(ExceptionFactory.java:59)
    at io.lettuce.core.internal.Futures.awaitOrCancel(Futures.java:246)
    at io.lettuce.core.LettuceFutures.awaitOrCancel(LettuceFutures.java:74)
    at org.springframework.data.redis.connection.lettuce.LettuceConnection.await(LettuceConnection.java:1061)
    at org.springframework.data.redis.connection.lettuce.LettuceConnection.lambda$doInvoke$4(LettuceConnection.java:920)
    at org.springframework.data.redis.connection.lettuce.LettuceInvoker$Synchronizer.invoke(LettuceInvoker.java:665)
    at org.springframework.data.redis.connection.lettuce.LettuceInvoker.just(LettuceInvoker.java:94)
    at org.springframework.data.redis.connection.lettuce.LettuceStringCommands.get(LettuceStringCommands.java:55)
    at org.springframework.data.redis.connection.DefaultedRedisConnection.get(DefaultedRedisConnection.java:278)
    at org.springframework.data.redis.connection.DefaultStringRedisConnection.get(DefaultStringRedisConnection.java:419)
    at org.springframework.data.redis.core.DefaultValueOperations$1.inRedis(DefaultValueOperations.java:58)
    at org.springframework.data.redis.core.AbstractOperations$ValueDeserializingRedisCallback.doInRedis(AbstractOperations.java:61)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:223)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:190)
    at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:97)
    at org.springframework.data.redis.core.DefaultValueOperations.get(DefaultValueOperations.java:54)
......

图片

分析

看抓包结果,可以发现经过n次重传(Retransmission)之后,客户端发出RST断开连接

这里和常见的15次(tcp_retries2)、924.6s对应不上,因为我是在Windows上复现的,随便搜了一下没看到Windows的相关配置,先暂时忽略吧。只要知道「网络出现分区后,用已存在的连接第一次请求操作redis,lettuce客户端会抛出异常并断开连接」即可!

「网络出现分区后,用已存在的连接第一次请求操作redis,lettuce客户端会抛出异常并断开连接」

这是一个简单又很困扰的问题。在原po与lettuce作者的讨论中,

  • 原po(业务角度,没错,我也是业务)觉得既然客户端发现该连接tcp已经失效了,应该重连,重连成功后,继续请求返回正确的数据。
  • 作者(应该属于中间件角度)认为网络分区就该解决分区的问题;连接失效就应该抛出异常。。。(我觉得也挺合理的)
    后续原po大神提了个pr(通过定时去ping redis server,重连那些失效的连接),被作者拒绝了(连接应该以keepalive的机制去确保活跃,也是不破坏redis的相关协议)

其实看到这里,可能很多人认为lettuce没有重连机制?(以及网上大部分博客,在这个问题上拿jedis和lettuce做对比,都说lettuce没有重连机制)

其实不然,在第一次请求多次tcp重传发现tcp连接失效,抛出异常给调用方之后,会进行重连!
图片

这也是当初我们遇到这个问题,将它归结为“玄学”的原因。
背景是一个客服系统,长时间不使用后redis连接会失效(我严重怀疑是虚拟主机或者waf之类的机制会定期清除没有数据传输的长连接)
晚上没有人使用,redis连接逐渐都失效了,第二天客服上班登录该系统,会出现上面的Command timed out after 10 second(s)异常。
又由于是连接池,客服第一次请求用的连接失败重连后具备使用后,再点击按钮可能又用了池子里别的失效连接。
等客服投诉到我们开发这边的时候,池子里的连接基本重连恢复了,而且白天工作时间,操作较为频繁,线上又无法复现问题了。

解决

一、定时ping(❌

同事在网上找了个定时ping redis的定时任务,发版后还是没有解决问题。(我怀疑一个是连接池的问题,定时任务没有将所有的连接都保活;还有就是定时ping的时间>重传的时间,连接还是不定期失效,没有根本解决问题)

二、获取连接时验证连接(❌

我还发现lettuce连接工厂有一个验证连接的方法

org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory#setValidateConnection

这也是个坑,每次获取连接都要加锁检验,同步改完发版后,导致高并发的组件线程数飙升,全阻塞在这获取连接了。。

三、tcp_user_timeout(✅

https://github.com/lettuce-io/lettuce-core/issues/2082的讨论中,有个大佬提出
配置 keep-alive 和 tcp_user_timeout 可以解决问题,原po也验证过在Linux下确实能够解决问题。
(个人更加看好这个方案)

四、替换Jedis(✅

替换为Jedis,这也是我们选择的方案。作为经过业务校验的客户端库,jedis无非是最好的选择,直接替换依赖即可。
没有使用方案三解决,更多是因为这个问题长时间没有定位并得到解决,偶现的分区(或者可能是waf机制长连接清理)无法溯源,权限有限,依赖于客户那边的云主机(不太配合排查。。

小结

完全理想的情况下,建议使用Lettuce

当存在网络不理想、主从切换?等等情况,建议还是使用Jedis,对于错误连接的检测和重连更加积极(有各种策略主动去探测连接的情况,剔除无效的连接,避免业务报错)

做过复现步骤的朋友别忘记恢复iptables......

iptables -F INPUT
iptables -F OUTPUT

参考资料

https://www.cnblogs.com/wingcode/p/14527107.html
redis/lettuce#2082 关键一篇issue
redis/lettuce#2253
https://www.cnblogs.com/songjiyang/p/16809694.html
https://blog.cloudflare.com/when-tcp-sockets-refuse-to-die/
https://pracucci.com/linux-tcp-rto-min-max-and-tcp-retries2.html

https://help.aliyun.com/document_detail/43848.html

https://www.cnkirito.moe/redis-connect/ 质量很高

arthas热更新修复线上NPE

操作步骤

  1. 修改有问题的代码
  2. 编译
  3. 上传编译后的class文件到服务器
  4. 启动arthas
  5. 执行 retransform /usr/local/app/马赛克.class

注意注意注意!retransform 的限制

下面来源于官方文档(retransform不是万能的)

  • 不允许新增加 field/method
  • 正在跑的函数,没有退出不能生效,比如下面新增加的System.out.println,只有run()函数里的会生效
public class MathGame {
    public static void main(String[] args) throws InterruptedException {
        MathGame game = new MathGame();
        while (true) {
            game.run();
            TimeUnit.SECONDS.sleep(1);
            // 这个不生效,因为代码一直跑在 while里
            System.out.println("in loop");
        }
    }

    public void run() throws InterruptedException {
        // 这个生效,因为run()函数每次都可以完整结束
        System.out.println("call run()");
        try {
            int number = random.nextInt();
            List<Integer> primeFactors = primeFactors(number);
            print(number, primeFactors);

        } catch (Exception e) {
            System.out.println(String.format("illegalArgumentCount:%3d, ", illegalArgumentCount) + e.getMessage());
        }
    }
}

参考资料

retransform|Arthas

Lettuce偶现Connection reset by peer

最近上线一个后台管理系统的服务组件,老是收到投诉说验证码、登录报错了,一直请求没反应,于是查看该组件的监控情况
image
可以看到这个组件的访问频率很低,每次超时情况的接口耗时在20s左右

监控也显示接口异常,先定位错误日志的堆栈信息

org.springframework.data.redis.RedisSystemException: Redis exception; nested exception is io.lettuce.core.RedisException: io.netty.channel.unix.Errors$NativeIoExc
eption: readAddress(..) failed: Connection reset by peer
        at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:74)
        at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:41)
        at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44)
        at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:42)
        at org.springframework.data.redis.connection.lettuce.LettuceConnection.convertLettuceAccessException(LettuceConnection.java:272)
        at org.springframework.data.redis.connection.lettuce.LettuceConnection.await(LettuceConnection.java:1063)

出现了最有用的信息,也是标题,这个错误指的是TCP连接中,对端发送了RST信号,中断连接。
https://stackoverflow.com/questions/1434451/what-does-connection-reset-by-peer-mean
这篇最高赞的回答很有意思
"Connection reset by peer" is the TCP/IP equivalent of slamming the phone back on the hook. It's more polite than merely not replying, leaving one hanging. But it's not the FIN-ACK expected of the truly polite TCP/IP converseur.

由于是TCP连接被中断了,可以猜到要么是连接池配置的有问题,要么是redis服务配置的连接超时时间太短了
先看redis客户端连接池配置,我们使用的是lettuce

spring:
  redis:
    lettuce:
      pool:
        # 连接池最大连接数默认值为8
        max-active: 100
        # 连接池最大阻塞时间(使用负值表示没有限制)默认值为-1
        max-wait: -1
        # 连接池中最大空闲连接数默认值为8
        max-idle: 10
        # 连接池中的最小空闲连接数,默认值为0
        min-idle: 10

其实到这里,我也没怀疑是连接池配置的问题,因为中间件相关的配置我们是公用的,如果连接池有问题,那么其它的服务组件也会存在类似的问题(目前只有这个管理后台的接口组件有问题

检查下redis的配置
image
timeout配置是360秒,不敢乱改。。(这个不知道是不是运维同事配置的,默认应该是0)

既然连接池最小空闲也没问题,难道重新连接失败了?这里我去看了下组件的日志,lettuce超时reconnected都成功的,间隔时间也是比较正常的。
这就奇怪了,一直在网上搜相关问题,终于看到一篇提到lettuce连接池的影响 https://www.dazhuanlan.com/panyupei/topics/1215150
org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory的文档注释上提到
This factory creates a new LettuceConnection on each call to getConnection(). Multiple LettuceConnections share a single thread-safe native connection by default.
The shared native connection is never closed by LettuceConnection, therefore it is not validated by default on getConnection(). Use setValidateConnection(boolean) to change this behavior if necessary. Inject a Pool to pool dedicated connections. If shareNativeConnection is true, the pool will be used to select a connection for blocking and tx operations only, which should not share a connection. If native connection sharing is disabled, the selected connection will be used for all operations.

关键:默认情况下,获取连接不会去检查连接的状态

问题应该就是出在这里了,连接池配置是没有问题的,redis server的超时时间触发RST给到长时间空闲的连接,导致连接池的连接已经被关闭了,组件再次收到请求时,在连接池获取连接执行命令才暴露错误。

照着文档的说明,只需要配置工厂为setValidateConnection(boolean)即可,这样每次获取连接会去校验,如果校验连接失效会创建连接。

至此问题定位解决,一直听说lettuce有很多坑,这个应该也属于是比较常见的问题,顺手记录下。

许久不见的乱码问题

前言

重构项目上线几个月,核心模块业务割接之后已经趋于稳定,现在陆陆续续把其它之前遗漏的/调用量较小的接口也指向新平台。

前几天夜晚突然说要割接一个短信模块,我这边还负责给对方加ip白名单(文档留存的重要性。。。老项目没有对接过的记录)

对方说已经把地址切过来了,我这边看日志准备把未通过的内网ip给加到白名单,结果发现短信中文内容变成一串乱码(如下现象......

有过类似经验的同学应该知道,这样的问号已经丢失了部分字节,无法反向编码进行恢复了

现象

23:04:05[INFO ]c-22 c.u.a.c.l.WebLogFilter         <<< POST /马赛克/receive马赛克, payload={"mobile":"f1f4aa32099f4de6马赛克84d963cce70","msgtype":4,"content":"����ͨ����������֤����:578864���������Ч","username":"yhzx"}
23:04:05[INFO ]c-22 c.u.a.e.util.IOUtil            Content-Type:application/json;charset=GBK, payload:{"mobile":"f1f4aa32099f4de马赛克c84d963cce70","msgtype":4,"content":"����ͨ����������֤����:578864���������Ч","username":"yhzx"}
23:04:05[ERROR]c-22 a.c.e.h.GlobalExceptionHandler 业务异常:105-鉴权失败,未知服务器ip111.202.马赛克
23:04:05[INFO ]c-22 c.u.a.c.l.WebLogFilter         >>> 1ms {"code":105,"msg":"鉴权失败,未知服务器ip:111.202.马赛克","successful":false,"data":null}

排查

思路:什么时候被编码成不可恢复的乱码呢?(因为多字节>单字节还可以反向编码恢复,虽然不推荐)

抓包(进入Nginx前)

{"mobile":"181cbe0be4f马赛克2f29e00cc32","msgtype":4,"content":"¡¾ÁªÍ¨Öú马赛克ÓÓÐЧ","username":"马赛克"}

图片

抓包(进入Java组件前)

图片

分析

思路:分析编码链路,找到被编码的问题处,进行修复

复现

本地环境,印证编码过程:GBK>>>ISO-8859-1>>>utf-8

图片

测试环境,复现成功
图片

修复

问题是哪里将ISO-8859-1编码成utf-8?

  1. nginx?(抓包排查,排除

  2. spring?(没有相关配置、机制,排除

    Spring默认提供了CharacterEncodingFilter,就是解决乱码问题,根据请求头响应头自适应进行编码

  3. 自己写的请求装饰器。。。

    自己挖了个坑,重构项目对入参统一缓存格式化,缓存前把流转换为utf-8编码的字符串了。。。

图片

在组件过滤器修复后,拦截器拿到的内容正常了

图片

全局公共请求装饰器修复,日志输出均正常

图片

总结

GBK 使用双字节编码,UTF-8 使用可变长度编码,ISO 8859-1 使用单字节编码。

这次还好是gbk转换为iso8859-1,还能反向编码恢复回去。

以下情况没有办法恢复

  • iso8859-1编码中文字符串。本身不支持中文字符,不可逆
  • gbk编码——iso8859-1解码——gbk编码——gbk解码。双字节拆成单字节,单字节再次编码,不可逆

网上找到类似情况的一张图!

图片

参考资料

https://cloud.tencent.com/developer/article/1058756

https://www.cnblogs.com/amunamuna/p/8922125.html

我们常说的网站乱码其实是指http请求的编码有问题。原因很简单,默认的编码是ISO-8859-1,而这种编码是一种80年代的遗留标准,它只能表示256个字符,即便对于西方英语用户,这种编码也有很多符号不能正确解决,当然中文更不用说了。

UTF-8是UTF家族的,相对要新,而解决的问题更多,对于绝大部分文字都有很好的支持,属于事实上的标准。

因为ISO-8859-1是历史遗留的标准,所以绝大部分容器仍然都是将其作为默认编码,包括Apache,Tomcat,WebSphere等,而Spring Boot是内嵌Tomcat运行,所以默认编码也是ISO-8859-1,虽然复写了编码为UTF-8,但是并没有设置为force encoding,所以一些版本依然会乱码。

因为默认编码问题在很多地方都存在,所以标准也是相互影响的。

简单来看历史进程是这样的:

  1. 最开始是ASCII,今天也有大量遗留系统使用着
  2. 在WIndow流行时,默认编码是ANSI,也叫Windows-1252
  3. 后来就是ISO-8859-1,这是是随着HTML 2.0标准固定下来的
  4. 再到后来的UTF-8,HTML5的默认编码已经是UTF-8了

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.