生产问题(十三)谷歌Protobuf误修改系统全局时区

news2024/9/27 9:30:11

一、引言

        最近其他组出了个线上问题,导致用户的时间出现问题,影响用户出行,后来才发现是谷歌的Protobuf会更改系统全局时区。不过有一说一,感觉jdk的问题更大。

        Protobuf(Protocol Buffers)是一种轻量级的数据序列化格式,由Google开发。它可以用于将结构化数据序列化为二进制格式,以便在不同的系统之间进行数据交换和存储。Protobuf具有高效、紧凑和可扩展的特点,可以在多种编程语言中使用。它通常用于网络通信、数据存储和配置文件等方面。

二、时区结构

        一开始这个问题看的作者非常迷惑,因为报告写的是修改了机器时区,什么意思?调用了操作系统对应时区的api?不可能吧,这种代码在哪里都不会给他用的,review要被喷死,再看看。

        哦,原来是jvm时区的TimeZone里面的全局变量被改了。这个不能叫做机器时区,就是个全局的时区变量。

        在看问题之前先要看看时区的结构,不然不了解全局的时区变量在哪里。

1. TimeZone(时区):TimeZone 类用于表示不同地区的时区信息。它提供了获取和设置时区偏移量、获取时区名称等方法。通过 TimeZone 类,你可以将日期和时间转换为特定时区的表示形式。

2. Calendar(日历):Calendar 类是一个抽象类,用于处理日期和时间的计算和操作。它提供了获取和设置年、月、日、时、分、秒等日期和时间字段的方法。Calendar 类还提供了一些方便的方法,如计算两个日期之间的差异等。通过 Calendar 类,你可以执行日期和时间的各种操作。

3. GregorianCalendar(公历):GregorianCalendar 类是 Calendar 类的一个具体实现,用于处理公历日期和时间。它继承了 Calendar 类的功能,并提供了更多的方法和功能。GregorianCalendar 类支持闰年、月份天数等公历特性。

        一旦使用 TimeZone 类初始化了一个时区对象,该时区对象的偏移量和其他属性是固定的,不会随着时间的推移而变化。时区对象的属性在初始化时被确定,并且不会自动更新。
        不过时区对象的属性可以通过调用 TimeZone 类的方法来进行更改。例如setRawOffset() 方法来更改时区的原始偏移量,或者通过 setDSTSavings() 方法来更改夏令时的偏移量。

三、分析-构造函数

        这其实是错误的方向,想直接看Protobuf问题的可以直接跳到第四章,不过作者认为错误的方向也是有必要的,因为不管是一时的还是长期的错误方向,既然它能使经验丰富的开发人员产生错误的想法,他就一定是有价值的。

        就像很多学校研究,其实不一定能研究出什么对的,但是可以排除很多的错误的方向,并且在很多方面为别人提供避雷指引。

        出问题的代码

public sType() {
    this.sDate = new GregorianCalendar(1,0,1,0,0,0);
    this.sDate.setTimeZone(ZoneInfo.getTimeZone("UTC"));
}

        这是框架生成的一个构造方法,相对于在不为空的情况下给你时间一个默认值,默认值是0时区。

        按道理说这样只是一个局部变量,为什么会对TimeZone的全局变量造成影响呢?只能说明在这段代码里面,把一个局部变量和全局变量做了关联,使用同一个引用,然后局部变量被修改,映射到了全局变量上面。

        这里拿到timezone的全局变量

        全局变量传递

         这时候Calendar的局部变量与TimeZone的全局变量同一个引用

        这里传入了TimeZone的全局变量,然后给到了Calendar那个局部变量

        第二行setTimeZone把Calendar的局部变量改为0时区,但是这时候TimeZone的全局变量和Calendar局部变量引用相同,全局变量也被修改

        这里会把局部变量的引用直接换成入参

        TimeZone的这个全局变量和局部变量绑定了,所以改局部变量就会改全局变量

        有的同学看着这个应该就会有想法了,因为引用关联是会有影响,但是需要修改引用的值。直接替换引用对于第一个全局变量是不产生影响的

四、分析-Protobuf

        需要先写个demo,结合目前的情况,是因为类里面有个GregorianCalendar类型的字段,然后这个类被Protobuf序列化存到redis,又在其他时候被反序列化拿出来用。那么demo就很清晰了:

private static final DefaultIdStrategy ID_STRATEGY = ((DefaultIdStrategy)RuntimeEnv.ID_STRATEGY);

private static <T> byte[] serialPojoByClass(T value) {
byte[] result = null;

 Class<T> clazz = (Class<T>)value.getClass();
 Schema<T> schema = RuntimeSchema.getSchema(clazz, ID_STRATEGY);
 result = ProtobufIOUtil.toByteArray(value, schema, LinkedBuffer.allocate());

 return result;
}

public static class DateTest {
private GregorianCalendar testDate;

 public void setT(GregorianCalendar testDate) {
this.testDate = testDate;
 }
}

public static <T> T deserializePojo(byte[] bytes, Class<T> clazz)
throws IllegalAccessException, InstantiationException {

if (bytes == null || bytes.length == 0) {
return null;
 }

return deserializePojo(bytes, 0, bytes.length, clazz);
}

private static <T> T deserializePojoByClass(byte[] bytes, int offset, int length, Class<T> clazz)
throws IllegalAccessException, InstantiationException {
if (clazz.isArray()) {
return null;
 }

Schema<T> schema = RuntimeSchema.getSchema(clazz, ID_STRATEGY);
 T result = clazz.newInstance();
 ProtobufIOUtil.mergeFrom(bytes, offset, length, result, schema);
 return result;
}

public static <T> T deserializePojo(byte[] bytes, int offset, int length, Class<T> clazz)
throws InstantiationException, IllegalAccessException {

if (bytes == null || bytes.length == 0) {
return null;
 }

return deserializePojoByClass(bytes, offset, length, clazz);
}

public static void main(String[] args) throws IOException, InstantiationException, IllegalAccessException {
DateTest dateTest = new DateTest();
 GregorianCalendar t = new GregorianCalendar(1, 0, 1, 0, 0, 0);
 t.setTimeZone(ZoneInfo.getTimeZone("UTC"));
 dateTest.setT(t);
 byte[] s = serialPojoByClass(dateTest);
 System.out.println("序列化前:" + TimeZone.getDefault().getID());
 deserializePojo(s, DateTest.class);
 System.out.println("序列化后:" + TimeZone.getDefault().getID());

}

        先看效果再分析,很奇怪吧,我只是序列化再反序列化,怎么全局时区被改了。

        

        这时候把局部变量取出来了,局部变量引用着全局变量

        pb这里new了GregorianCalendar,和全局变量绑定

        这时候开始设置全局变量里面的每一个字段,字段是从序列化字符串拿出来

        取出了时区的id

        unsafe替换字段,时区彻底被改变

五、总结

        这个过程简单点说就是Protobuf会把对象里面的每一个字段都new出来,如果对象是字段继续new里面的对象,并且设置里面对象的每一个字段,所以最底层的时区id都会被改变,替换成序列化字符串里面的值。

        本来这是没什么问题的,糟糕的是GregorianCalendar里面的字段是直接和TimeZone的全局时区关联的,改他就是在改全局时区,所以后面就会导致系统时区是错的。

        这个锅其实应该是jdk和Protobuf一起背,Protobuf责任还要小一点,Protobuf会说我只是做序列化反序列化,你jdk怎么搞一个不安全的GregorianCalendar出来呢?jdk会说我有问题,但是这个你们提供工具就要考虑各种情况做兼容,如果用户自己定义了类相似的类呢?

        这个其实就非常像工作中到扯皮状态的两个组了,作为使用者都不好提issue。各位同学用的时候注意Protobuf的问题,也要注意GregorianCalendar这个类。

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

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

相关文章

基于ssm大学生校园招聘网的设计与实现论文

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统大学生校园招聘信息管理难度大&#xff0c;容错率低&…

vue3实现打字机的效果

前言&#xff1a; vue3项目中实现打字机的效果。 实现效果&#xff1a; 实现步骤&#xff1a; 1、安装插件 npm i vue3typed 2、main.js中配置 import vuetyped from vue3typedconst app createApp(App) // 挂载打字机的全局方法 app.use(vuetyped) 3、界面使用 <vuet…

ClickHouse 入门与实战教程

目录 1. ClickHouse 简介 什么是 ClickHouse&#xff1f; ClickHouse 的优势和特点 适用场景 2. 安装 ClickHouse 3. ClickHouse 的基本概念 4. ClickHouse 的基本操作 创建数据库和表、插入和查询数据 使用 MergeTree 引擎处理时序数据 管理分区 创建带有分区的 Mer…

IP编址,IP地址介绍与子网划分方法

网络层位于数据链路层与传输层之间。网络层中包含了许多协议&#xff0c;其中最为重要的协议就是IP协议。网络层提供了IP路由功能。理解IP路由除了要熟悉IP协议的工作机制之外&#xff0c;还必须理解IP编址以及如何合理地使用IP地址来设计网络。 上层协议类型 以太网帧中的Typ…

【浏览器】同源策略和跨域

1. 什么是跨域 在说跨域之前,先说说同源策略,什么是同源策略呢?同源策略是浏览器的一种安全机制,减少跨站点脚本攻击(XSS,Cross Site Scripting)、跨站点请求伪造(CSRF,Cross Site Request Forgery)攻击等,因为非同源的请求会被浏览器拦截掉。 同源就是协议、域名(…

常用组件的一些数据指标

QPS metrics 机制&#xff0c;利用 AtomicLong 算出核心接口每分钟调用多少次&#xff08;除以 60 就是高峰期每秒访问次数&#xff09;以及每天被调用总次数 TP99 100ms , 99%的接口耗时在 100ms以内&#xff0c;1%的接口耗时在100ms以上 TP95 100ms , 95%的接口耗时在 1…

Python:最新Windows及Mac开发环境搭建(最详细版,附安装包)

windows 软件下载 从官网下载python和pycharm。大家建议使用同一个版本 下面是下载地址&#xff0c;也可直接到我网盘下载安装包&#xff08;在文章末尾哦&#xff09; ● python https://www.python.org/ ● pycharm https://www.jetbrains.com/zh-cn/pycharm/download/#sect…

计算机毕业设计----SSM实现的一个在线文具学习用品购买商城

项目介绍 本项目分为前后台&#xff0c;前台为普通用户登录&#xff0c;后台为管理员登录&#xff1b; 管理员角色包含以下功能&#xff1a; 管理员登录,管理员信息管理,查看用户信息,新闻公告信息管理,文具类型信息管理,城市信息管理,配货点信息管理,文具信息管理,订单信息…

速看!销冠高效给客户群发消息的秘诀

你是不是也有过这样的疑问&#xff1a;明明都是给客户群发消息&#xff0c;为什么别人的成交率那么高&#xff0c;自己却效果一般呢&#xff1f; 今天就给大家分享销冠常用的高效群发消息秘诀&#xff0c;让大家都能更好地与客户进行沟通&#xff0c;提高成交率&#xff01; …

一盒晶圆只有25片吗?

没有答案&#xff0c;可能是实践的标准。后来在工作过程中发现还有13片的&#xff0c;个人认为研究这个问题不如多看看foup&#xff01; 晶圆载具用于硅片生产、晶圆制造以及工厂之间晶圆的储存、传送、运输以及防护。晶圆载具种类很多&#xff0c;如FOUP用于晶圆制造工厂中晶圆…

金和OA C6 gethomeinfo sql注入漏洞

产品介绍 金和网络是专业信息化服务商,为城市监管部门提供了互联网监管解决方案,为企事业单位提供组织协同OA系统开发平台,电子政务一体化平台,智慧电商平台等服务。 漏洞概述 金和 OA C6 gethomeinfo接口处存在SQL注入漏洞&#xff0c;攻击者除了可以利用 SQL 注入漏洞获取…

cuda加速求解龙格库塔四阶五步积分

一般代码使用cuda加速的方法&#xff1a; 使用PyTorch进行加速&#xff1a; 首先&#xff0c;你需要将你的ODE系统定义为PyTorch模型&#xff0c;这样可以利用PyTorch的自动微分功能和GPU加速。然后&#xff0c;你需要将数据和参数转换为PyTorch张量&#xff0c;并将它们移动到…

基于ssm高校宿舍管理系统论文

基于vue的高校宿舍管理系统的设计与实现 摘 要 当下&#xff0c;正处于信息化的时代&#xff0c;许多行业顺应时代的变化&#xff0c;结合使用计算机技术向数字化、信息化建设迈进。以前学校对于宿舍信息的管理和控制&#xff0c;采用人工登记的方式保存相关数据&#xff0c;这…

IntelliJ IDEA Community(社区版)下载及安装自用版

IntelliJ IDEA Community&#xff08;社区版&#xff09;下载及安装自用版 估计是个开发都逃脱不了用IDEA的命运吧&#xff0c;这么好的软件&#xff0c;白嫖了好多年。感恩。 现在很多公司已经不让用商业版的破解版了&#xff0c;所以这里讲的是社区版。 区别&#xff1a; 商…

在线客服系统的优势:提升客户服务质量与效率的关键工具

一款好的客服系统能够帮助企业节约成本、提高服务效率、消除服务延迟 、加强客户忠诚&#xff0c;为企业的售后服务带来显著的提升&#xff0c;对企业来讲意义非凡。 就像Zoho Desk&#xff0c;它系统稳定性好、安全性高、用户体验效果好、性价比高&#xff0c;同时售后服务好&…

【HarmonyOS开发】ArkTs实现应用配色随系统深浅模式自动切换的三种方式

应用深浅配色模式是一种常见的系统外观选项&#xff0c;环境变暗时切换到深色模式&#xff0c;可以减轻眼睛疲劳和节省设备电量。 注意&#xff1a;DevEco Studio 4.0版本存在bug&#xff0c;无法生效。 1、实现思路 利用系统颜色资源&#xff1a;这种方法最简单&#xff0c;只…

树莓派安装mediapipe方法

MediaPipe 解决方案可跨多个平台使用。 每个解决方案都包含一个或多个模型&#xff0c;您也可以为某些解决方案自定义模型。 以下列表显示了每个受支持平台可用的解决方案以及您是否可以使用 Model Maker 来自定义模型&#xff1a; 在树莓派上安装mediapipe后, python可以支持…

顺序表的实现(头插、尾插、头删、尾删、查找、删除、插入)

目录 一. 数据结构相关概念​ 二、线性表 三、顺序表概念及结构 3.1顺序表一般可以分为&#xff1a; 3.2 接口实现&#xff1a; 四、基本操作实现 4.1顺序表初始化 4.2检查空间&#xff0c;如果满了&#xff0c;进行增容​编辑 4.3顺序表打印 4.4顺序表销毁 4.5顺…

Linux进阶系列(二)——lscpu、htop、seq、shuf、sort

1. lscpu lscpu 命令是Linux系统中用来显示关于CPU架构的信息的工具。它详细展示了CPU的相关信息&#xff0c;包括型号、核心数、架构类型、缓存大小等等。 1.1 物理CPU与逻辑CPU 物理CPU指的是实际存在于硬件系统上的中央处理单元。每个物理CPU都是一个独立的处理器芯片或处…

系列十一(实战)、发送 接收带标签的消息(Java操作RocketMQ)

一、发送 & 接收带标签的消息 1.1、概述 消息的种类纷繁复杂&#xff0c;不同的业务场景需要不同的消息&#xff0c;基于此RocketMQ提供了消息过滤功能&#xff0c;通过Tag或者Key进行区分&#xff0c;本章介绍Tag&#xff0c;我们再往一个Topic里面发送消息的时候&#x…