【Android 日常学习】不同时区获取时间戳不一致咋办?——时钟对齐原理及实现方案

文章目录

背景

  • 私信中一些时延相关的统计需要用时间戳来做差值但是用客户端本地的时间戳可能会由于时区的问题产生误差,因此想需要服务端的时间戳做个校准。
  • 例如:在统计接收消息时延时,需要用接收方收到消息的时间戳减去发送方发送消息的时间戳。但是如果两个人的时区不同的话,得到的结果是错误的。因此需要统一按照server的时间来做运算

原理

由于可能受到物理因素[1]等干预。会导致本地时钟与服务端时钟存在误差。因此需要进行时间校准;
目前最简单且精准的时间校准方案可参考NTP协议实现方式:

[1] 物理因素:钟表和计算机内部有一个叫做晶体振荡器的东西,给它加上电压,它就会以固定的频率振动。但这个振动频率的稳定性,取决于它的制造工艺以及外界环境的影响。
出于成本考虑,钟表的制作工艺没那么高,所以它更容易有误差。而电脑制作工艺虽然比较高,但它内部的晶体振荡器会受到温度变化带来的影响,实际工作过程中也会产生误差。

向server请求服务端时钟时间,并在网络请求上添加时间戳,配合计算网络延迟。从而修改本地时间;
在这里插入图片描述
根据图示可以计算出网络传输延迟,以及本地时钟与服务端时钟的时延:

  • 网络传输延迟 = (t4 - t1) - (t3 - t2)
  • 本地与服务端时钟时延 = t2 - t1 - 网络传输延迟 / 2 = ((t2 - t1) + (t3 - t4)) / 2

计算过程假设网络上下回路对称,传输延迟一致

获取时钟时延后,便可以对本地时钟进行修改,保证与服务端时间的一致性;

方案

  • server在所有请求的response中增加两个通用字段,request_arrived_time(请求到达中台的时间),server_execution_end_time(中台返回response的时间),单位都是毫秒
  • 客户端在收到response时,计算与server时间的delta(误差),计算公式如下:
request_duration = client_request_end - client_request_start;
server_execution_duration = server_execution_end_time - request_arrived_time;
estimate_request_arrived_time = (request_duration - server_execution_duration) / 2 + client_request_start;
delta = request_arrived_time - estimate_request_arrived_time;

客户端在每次计算出的delta做累加得到total_delta,并记录计算次数times,avg_delta = total_delta / time,用avg_delta来作为当前客户端与server的时间误差,后续在做统计时统一用客户端的时间戳加上avg_delta

代码实现

请求服务端获取请求发送相关时间戳,并发送时钟对齐的消息

            mainThreadHandler.sendMessageDelayed(Message.obtain().apply {
                what = MESSAGE_TOKEN
                obj = timestampModel
            }, DELAY_TIME)

收到消息后进行计算

        val mainThreadHandler = object : Handler(Looper.getMainLooper()) {
            override fun handleMessage(msg: Message?) {
                super.handleMessage(msg)
                msg ?: return
                when (msg.what) {
                    MESSAGE_TOKEN -> {
                        val timestampModel = msg.obj as? RequestTimestampModel ?: return

                        Task.delay(0)
                                .continueWith {
                                    // 可能存在并发问题,所以需要考虑线程安全问题
                                    timestampModel.apply {
                                        val requestDuration = clientEndTime - clientStartTime
                                        val serverExecuteDuration = serverExecutionEndTime - serverArrivedTime
                                        val estimateRequestArrivedTime = (requestDuration - serverExecuteDuration) / 2 + clientStartTime
                                        lastDeltaFromServerTime = serverArrivedTime - estimateRequestArrivedTime
                                        serverSyncTimes++
                                        totalDeltaFromServerTime += lastDeltaFromServerTime
                                        avgDeltaFromServerTime = totalDeltaFromServerTime / serverSyncTimes
                                        Depend.Log.i(TAG,
                                                "sync Client timestamp $serverSyncTimes cmd:$cmd,($requestDuration,$serverExecuteDuration),singleDelta:$lastDeltaFromServerTime,avg:$avgDeltaFromServerTime")
                                    }
                                }

                    }
                }
            }
        }

存在问题

PTP的时间戳是在硬件上产生的,不受软件影响,效率更高。同步精度可以实现100ns。
网络同步时延,受网络流量影响也很大。网络波动越大,测得的路径时延就越不准确。可以通过重复对齐解决问题。

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集

在这里插入图片描述
二、源码解析合集
在这里插入图片描述

三、开源框架合集
在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值