服务器和客户端同步状态,客户端不能依赖服务器的响应

有一类系统,基本上所有操作都要求在线。在客户端产生的数据,直接提交到服务器,本地不存数据,或者仅保存少量缓存数据。这类应用有一个优势,就是客户端和服务器的数据始终是同步的,罕有两端不一致的情况。但是也有缺点,即对网络条件要求高,在网络条件不好的时候,用户操作需要等待,甚至无法正常使用

我们的一个APP,与上述系统不同,是支持离线操作的。数据在本地和服务器各有一份,然后通过同步机制来保持一致。这样做的缺点是提升了系统的复杂性,因为如果设计有缺陷,就很容易发生两端数据不一致的情况;另外就是如果客户端长期离线,存在数据丢失的风险。当然好处也很明显,即操作不依赖网络,所以非常流畅。即使在无网络条件下,也可以正常使用,只要在网络恢复以后,与服务器做数据同步就可以了

这类数据同步方案的一个常见错误,是客户端依赖服务器的响应结果。我们的老方案就踩了不少坑,本文总结几个常见的场景

比如这个场景:

1、客户端向服务器发起一个请求

2、服务器收到此请求以后,往数据库写入一条记录,返回成功响应

3、客户端收到响应以后,往本地也写入一条记录

看起来这个方案还不错,很直观,并且两端都有了同样的数据。但是这只是理想情况。有一天我们的系统访问量突然增大,导致服务器的负载升高,每个请求都处理得很慢,于是客户端超时了。所以客户端认为此次请求失败,没有往本地写数据,但是其实服务器稍后还是处理了这条请求。于是服务器就比客户端多了一条数据。更糟糕的情况是,客户端认为操作失败,继续反复发起请求,当然毫无例外地全部超时了,于是服务器里就多了N条记录……这是一个很容易模拟重现的场景,只要在服务端的代码里打一个断点,然后等客户端超时以后,再把断点走下去就行了

后来为了解决这个问题,我们试过增加防重复提交的机制,但是也不理想。因为首先,“重复提交”本身就是一个模糊的概念,什么样的提交算是重复提交呢,如果认为用户在同一个界面重复点击是重复提交,那么如果用户关掉窗口再来一次,是否也算重复提交呢?其次,数据同步机制本来就比较复杂,再加一个新的纠错机制,会让系统更加复杂,隐藏更多出BUG的可能性。最后,即使真的有了完美的防重复提交方案,也只能解决“多N条”的问题,还是解决不了“多一条”的问题

最后我们采取的办法是,先往本地插入一条记录,然后再提交到服务器去异步处理。由于是异步的,必然存在一个时间差(特别是客户端离线的时候,时间差还会比较长),所以会有短时间的两端数据不一致,但是只要最终一致,也就达到了目的

再比如这个场景:

1、客户端生成自从上次同步以后的新差量数据

2、将数据发到服务器,服务器处理,返回成功响应

3、客户端收到成功响应以后,删除这批数据

和上一个场景也有类似的问题,服务端处理得慢,导致客户端超时,差量数据没有被清除,但是这些数据实际上已经写入了服务器。于是下次同步的时候,重复的数据就又发到了服务器;此外还有另一个BUG,由于上报的时候,有2份差量数据(上次未清除的,此次新生成的),所以上报也发生了错误

后来我们修改了这2个BUG,首先是服务器收到请求以后,不处理已经处理过的数据;客户端则是每次只上报最新的那份差量数据;另外还被迫加入了错误数据的清理机制……为了修复一个BUG,代码又复杂了很多

其实更好的做法,应该是客户端每次要同步的时候,都重新生成一份新的差量,上报以后,即使没有收到响应,也应该把这份差量删除。这样就避免了客户端堆积多份差量文件的情况。然后接下来,就面临一个选择:在没有收到响应的时候,应该怎么标识哪些数据是“新”的。这个时候,服务器的状态是不可知的(而不是确定的成功或者失败),如果假设服务器操作成功,那么就应该在客户端上把这些数据都标识成旧的,下次不要再提交;相反,如果假设服务器操作失败,就应该保留这些数据的状态,下次再提交上去。前者的风险在于,如果服务器真的处理失败了,客户端又不再提交这些数据,那么这部分数据就再也没有机会同步到服务器。后者的问题在于,服务器可能会重复写入。两相权衡,我们认为前者的问题更严重,因为会直接造成数据丢失,所以最终选择了后面的方案,但是在服务器加上防止重复写入的机制

以上2种场景,本质上都是因为客户端和服务器是分离的,明明是一个操作,却不在一个事务里,并且服务器的状况对于客户端来说有时是不可知的。具体的解决办法,需要根据实际场景分别处理,但是有一点是明确的,即客户端不能依赖服务器的响应,而是需要单独的机制来保证一致性

再延伸一点考虑,为什么在线的应用没有这类问题,就是因为客户端没有数据,所以在整个系统里只有一份数据,自然不存在数据不一致。当然,如果允许多个客户端同时操作,还是需要考虑并发操作时的异常场景。这是另外一个主题,本文不展开。还有一种更复杂的情况,就是有多于2份数据,比如有2个客户端都能操作,并且都和服务器同步,这时候系统的复杂性又大大提升,因为1份数据,什么都不需要做;2份数据,仅需要处理同步;>2份数据,则还需要处理数据合并。比如某个会员,原本的余额是3000。然后在客户端A做了2个操作,余额变成2000;在客户端B做了3个操作,余额变成2500。这时候他的实际余额是多少呢?显然2000和2500都是错误的,需要合并数据,一种思路是客户端定时把数据上报到服务器,在服务器执行合并逻辑,把数据合并到一个中心节点,再下发到各个客户端。这个场景不在本文的讨论范围,以后再单独写

时间: 2024-10-11 22:56:20

服务器和客户端同步状态,客户端不能依赖服务器的响应的相关文章

CentOS7搭建NTP服务器及客户端同步时间

一.服务器配置 1.查看服务器.客户端操作系统版本 [[email protected] ~]# cat /etc/redhat-release CentOS Linux release 7.6.1810 (Core) 2.查看服务器是否安装ntp,系统默认安装ntpdate: [[email protected] ~]# rpm -qa | grep ntp fontpackages-filesystem-1.44-8.el7.noarch ntpdate-4.2.6p5-28.el7.cen

Linux下安装ntp服务器及客户端同步

服务器端: 1.装包 略.... 2.修改ntp服务配置文件 配置文件位置:/etc/ntp.conf 修改内容: #设置与服务器时间同步的IP网段,以下内容即为192.168.x.x机器可与服务器同步 restrict 192.168.0.0 mask 255.255.0.0 #设置ntp服务器自身同步的服务器IP,127.127.1.0意为与服务器自身进行同步 #如果在最后添加prefer则表示此主机为优先同步 server 127.127.1.0 #这行是时间服务器的层次.设为0则为顶级,

ntp服务端和客户端同步配置

ntp服务端配置(centos7) 一.打开UDP123端口INPUT -m state --state NEW -m udp -p udp --dport 123 -j ACCEPT.然后重启防火墙服务器  service firewalld restart 或直接关闭防火墙service firewalld stop,    chkconfig firewalld off 二.查看有没有ntp服务rpm –qa|grep ntp,如果没有则安装yum install –y ntp 三.要同步

一种客户端同步server数据的方案

场景 客户端A不定时地把本地数据同步到server上,然后另一个客户端B(app)从server把数据同步下来,汇总展示 客户端A数据结构 原始的数据(来自客户端A),每条都有create_time和modify_time,用于表示这条记录的创建时间和修改时间.同时系统里还有latest_backup_time字段,用来表示上次备份的时间.客户端每次备份,都会以这3个字段作为条件,找出需要备份的数据,上传到server 服务端数据结构 由于历史遗留问题,服务端收到备份数据之后,没有做其他的处理,

外网客户端访问校园内网的服务器——socket连接

在做客户端与服务器的socket连接并发送数据应用中,通常有以下四种情况: 1).客户端在内网,服务器在内网. 对于这种情况,只需要用服务器的内网IP即可. 2).客户端在外网,服务器在内网. 对于这种情况,服务器可采用花生壳软件来进行外网IP与内网IP的映射. 3).客户端在内网,服务器在外网. 对于这种情况,只需要用服务器的外网IP即可. 4).客户端在外网,服务器在外网. 对于这种情况,只需要用服务器的外网IP即可. 从分析中可以看出,只有情况二是特殊的,由于服务器在内网,而客户端在外网,

【c#源码】安卓客户端通过TCP通信与Windows服务器进行文件传输

APK文件  (对应的windows服务器端已经架设好,可以直接下载进行测试) 源码     数据库文件 在前面一篇文章:[源码]c#编写的安卓客户端与Windows服务器程序进行网络通信 中我们探讨了,如何通过xamarin技术,完成安卓客户端与Windows服务器的通信,这篇文章,我们探讨一下使用场景非常多的文件传输. 先谈一下为什么使用xamarin.android技术吧,之前有开发过一个公文系统,c#语言开发,服务器部署在Windows Server 2003上,客户端采用Winform

连接Linux服务器:Win免费SSH客户端工具

连接Linux服务器:Win免费SSH客户端工具 http://blog.csdn.net/jiangdou88/article/details/51585555

(转)基于即时通信和LBS技术的位置感知服务(三):搭建Openfire服务器+测试2款IM客户端

主要包含4个章节: 1. Java 领域的即时通信的解决方案 2. 搭建 Openfire 服务器 3. 使用客户端测试我们搭建的 Openfire 服务器 4. Smack 和 ASmack 一.Java领域的即时通信的解决方案 Java领域的即时通信的解决方案可以考虑openfire+spark+smack. 1. Openfire是基于Jabber协议(XMPP)实现的即时通信服务器端版本,目前最新的版本为3.6.4,网上可以找到下载的源代码. 2. 即时通信客户端可使用spark2.5.

域控客户端 登录 域控服务器速度慢的原因

现状: 域控服务器: winserve2008 R2 客户端 : SDWM win7 64位 SDWM XP *win7 64位,同一账号,登录要几分钟,XP则只需要几秒 可能原因: 1.服务器DNS设置错误(正常应指向服务器本机) =〉 不是这个问题 2.客户端DNS设置错误(应指向DNS服务器) =〉 不是这个问题 3.客户端 系统问题 =〉即刻解决登录问题 处理: 将win7 64位系统换成 正版 32位系统, 原因: 可能是64位D版过度删减造成.