客户端秒级时间同步方案

在客户端开发中,往往会有一些功能对时间要求比较严格,客户端需要获取到当前最准确的时间。但由于客户端环境多种多样,我们无法保证直接在客户端设备上获取到的时间是最准确的时间。

对于某些问题设备来说,设备时间与比当前实际的时间差了几个小时,甚至几天的情况都存在。倘若某功能依赖于当前时间,而客户端所提供的时间不准,就往往会给客户造成一些困扰。

那么,客户端如何能够获取到当前最准确的时间呢?

从服务器同步时间

我们首先想到的是,服务器可以提供一个获取当前时间戳的接口。客户端每次获取当前时间时,都直接从服务器拉数据就可以了。

这个方案简单粗暴,但是问题也可以一眼看出:

每次都从服务器拉时间,一方面会对服务器造成一些压力;另一方面网络也存在时延损耗和不稳定的可能,将会减低客户端的体验。

只拉取一次时间

那么,能不能只从服务器拉取一次时间,不用每次都访问服务器呢?

我们可以在客户端初始化的时候,拉取一次时间接口。

记此时的服务器时间为server_init_time,同时获取到当前客户端的时间, 记为local_init_time

当客户端需要获取当前的准确时间的时候,首先得到客户端的当前时间 记为local_now_time

那么,当前最准确的时间就可以通过一个简单的差值计算得到。

server_now_time = server_init_time + (local_now_time - local_init_time)

通过计算两次本地时间的差值,就可以推出当前服务器的时间了。

网络时延的损耗

上述方案实际上已经能够准确的获取到当前服务器的时间了。

但是仍然有个不严谨的地方:

在该方案中,我们假设server_init_time和local_init_time是同一时刻。

但实际上并不是这样的。server_init_time只是http请求到达服务器的时间。

server_init_time和local_init_time还差一个请求返回时间。

网络时延

我们都知道网络是不可靠的,严重情况下,一次网络时延可以达到数秒。这对于时间校准的也会造成一些小小的干扰。

基于这个问题,我们可以假设客户端发出请求到服务器的时间服务器回复请求到客户端的时间基本是一致的。虽然在实际情况下,有可能存在偏差。

此时

server_init_time = server_init_time - delta / 2;

其中delta是指一次请求的总时延。

防止客户端运行期间时间改变

基于以上考虑,我们的时间校准方案已经基本上可以满足大多数客户端的需求了。

但是,你永远也不会知道客户端会出现什么情况。

假如,在软件运行期间,无论是出于被动还是用户有意主动的修改,客户端的时间发生了变化。那么,以上通过计算两次本地时间差值来获取准确时间的方案将会失效。

因此,我们需要使用一个不随本地时间变化的维度作为校对的标准。我们首先想到了开机时长,开机时长是指当前时刻距离设备开机时刻的毫秒数,而这个东西是不随设备的时钟变化的。

因此我们的公式可以修改为:

server_now_time = server_init_time + (local_now_tickcount - local_init_tickout)

local_now_tickcount和local_init_tickout分别指的是设备当前的开机时长和初始化阶段用户的开机时长。

时间溢出

使用开机时长作为校对的标准的方案,看似完美无缺,实际上仍然存在着一些意想不到的问题....

以Windows为例,C#用来返回开机时长的方法Environment.TickCount是int32类型的,单位为ms。

我们可以简单计算下,一天大概有 24 * 60 * 60 * 1000 = 86400000 毫秒,而int32的最大值是2^31 - 1 = 2147483647

这也就意味着,当开机时间超过2147483647 / 86400000 = 24.85 天的时候int32就溢出了。。

也就意味着,如果我们的客户端软件运行在一个25天未关机的设备上,那么软件的时间校准将会出现严重的问题。。。

在真实的情况下,客户端设备25天不关机的情况太常见了。

那么,如果解决此问题呢?

我们发现C#有一个StopWatch函数,常常用来统计函数运行时长。而它的时间表示stopWatch.ElapsedMilliseconds是long型的。同时,StopWatch是基于Timer实现的时间统计,也不与本地时钟相关。

那么,与利用开机时长的方案类似,我们在软件初始化时,开启一个StopWatch。每次获取准确时间的时候,将stopWatch中记录的当前耗时时间与服务器初始时间相加,即可得到当前的准确时间。

最终的时间校准方案如下:

server_init_time = server_init_time - delta / 2;
server_now_time = server_init_time + stopWatch.ElapsedMilliseconds / 1000

基于该方案,我们就实现了一个秒级的时间同步方案

本文首发于腾讯云+社区,稍后同步于博客www.cyhone.com

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏PPV课数据科学社区

【重磅】33款可用来抓数据的开源爬虫软件工具

要玩大数据,没有数据怎么玩?这里推荐一些33款开源爬虫软件给大家。 爬虫,即网络爬虫,是一种自动获取网页内容的程序。是搜索引擎的重要组成部分,因此搜索引擎优化很...

6274
来自专栏后端技术探索

CAP原理和最终一致性

在足球比赛里,一个球员在一场比赛中进三个球,称之为帽子戏法(Hat-trick).在分布式数据系统中,也有一个帽子原理(CAP Theorem),不过此帽子非彼...

1482
来自专栏智能算法

一文看懂npm、yarn、pnpm之间的区别

原文:Understanding differences between npm, yarn and pnpm 作者:Alex Kras 翻译:雁惊寒 本文作者...

34010
来自专栏钱塘大数据

【推荐收藏】33款可用来抓数据的开源爬虫软件工具

要玩大数据,没有数据怎么玩?这里推荐一些33款开源爬虫软件给大家。 爬虫,即网络爬虫,是一种自动获取网页内容的程序。是搜索引擎的重要组成部分,因此搜索引擎优化很...

5125
来自专栏Python数据科学

33款你可能不知道的开源爬虫软件工具

爬虫,即网络爬虫,是一种自动获取网页内容的程序。是搜索引擎的重要组成部分,因此搜索引擎优化很大程度上就是针对爬虫而做出的优化。

4002
来自专栏三丰SanFeng

负载均衡 - 综述

1 什么是负载均衡 网络的各个核心部件随着业务量的提高、访问量和数据流量的快速增长,其处理能力和计算强度也相应增大,使得单一设备根本无法承担。在此情况下,如果扔...

2588
来自专栏IT技术精选文摘

系统负载能力浅析

一. 衡量指标 用什么来衡量一个系统的负载能力呢?有一个概念叫做每秒请求数(Requests per second),指的是每秒能够成功处理请求的数目。比如说...

2075
来自专栏企鹅号快讯

浅谈MySQL集群高可用架构

前言 高可用架构对于互联网服务基本是标配,无论是应用服务还是数据库服务都需要做到高可用。对于一个系统而言,可能包含很多模块,比如前端应用,缓存,数据库,搜索,消...

5049
来自专栏Albert陈凯

Hadoop离线数据分析平台实战——290活跃用户分析Hadoop离线数据分析平台实战——290活跃用户分析

Hadoop离线数据分析平台实战——290活跃用户分析 项目进度 模块名称 完成情况 用户基本信息分析(MR)� 未完成 浏览器信息分析(MR...

35514
来自专栏用户2442861的专栏

高并发服务端分布式系统设计概要(上)

http://www.cnblogs.com/ccdev/p/3338412.html

1033

扫码关注云+社区