Web 服务 SocketTimeOutException 导致数据库不一致
Posted
技术标签:
【中文标题】Web 服务 SocketTimeOutException 导致数据库不一致【英文标题】:Web service SocketTimeOutException causes database inconsistency 【发布时间】:2013-01-30 12:07:37 【问题描述】:我想在 Apache HTTP 客户端库的帮助下从 android 设备调用 Rails Web 服务时克服以下情况
场景:
客户端(Android):发起 HTTP POST 请求(使用 Apache HTTP 客户端,JSON 作为交换格式)将少量记录插入服务器数据库(mysql)。
服务器(Rails 中的 Web 服务):处理请求并将记录成功插入数据库,但同时超时 (SocketTimeOutException)
发生在客户端。
客户端(Android):超时时,重试执行相同的 HTTP POST 请求,并再次将相同的记录插入数据库并发生数据库不一致。
谁能帮我克服这种情况。
提前致谢
编辑:
我目前的情况:
1) 用户尝试通过填写和提交数据来使用 Android 应用进行注册。所以基本上 POST 服务包含发送到服务器(Rails 和 MySql)的所有必需用户
2) POST 请求由服务器处理,该过程主要负责将记录插入数据库,并以插入数据的 USER_ID 向客户端提供响应。
3) 轰!!数据已插入 MySql 数据库,但由于某种原因,Android 端存在SocketTimeOutException
,因此 Android 端没有 USER_ID,用户尝试再次注册............数据库不一致和我很伤心:(
注意:步骤 3) 的发生非常罕见。但是当服务器太忙或有一些处理负载时,这种情况很常见。..
【问题讨论】:
我认为 基于会话的 Web 服务 是您所需要的。所以只有当您的服务器成功完成时会话才会过期.. 您能否提供更多有关创建和维护基于会话的 Web 服务的信息......以及它将如何帮助我克服这个问题? 您可以尝试增加超时时间,但这不是最终的解决方案,因为包括超时在内的任何原因仍可能导致连接失败。 @MisterSmith 目前超时设置为 10 秒,我认为这很好。但如果超时时间增加,客户端(Android)将不得不等待更长的时间......所以又是这样一个问题。 不,超时是您必须在连接调用中等待服务器响应的最长时间。如果将其设置为 60 秒,并不意味着每次连接都需要 60 秒。但你是对的,这只是在踢罐子。 【参考方案1】:对于您上面描述的特定情况,我建议您在程序中添加一个会话 cookie。当用户发布数据时,您检查该会话是否已经存在 USER_ID,如果是,则返回它,如果没有,则执行数据库插入。因此,即使初始插入发生,然后在用户获得响应之前超时,您也可以在用户重试注册时提供响应。不会让您的数据库被破坏。
您还可以在数据库中查找相同(或其他唯一)记录(最近创建)并在第二次请求时返回该 USER_ID。这将取决于用户发送的数据。但如果是注册,并且您在短时间内连续收到两篇具有相同电子邮件的帖子,则可以肯定它来自同一用户...
编辑前:
由于您使用的是 Rails,您可以简单地利用 create_or_update 方法来创建记录。基本上你检查记录是否已经存在,然后更新它们,而不是盲目地插入新的。
这是一个您可能无论如何都必须处理的问题,因为即使您没有转发问题,两个客户也可能同时发布相同的信息。
如果您想更详细地说明其工作原理,则必须提供有关帖子内容的更多信息。在这种情况下,键和唯一字段以及有关记录是否绑定到特定用户等的任何信息都很有趣...
【讨论】:
嘿,我已经更新了我的问题,请您检查一下并告诉我......谢谢【参考方案2】:请尝试使用以下代码
HttpParams httpParameters = new BasicHttpParams();
int timeoutConnection = 30000;
HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
int timeoutSocket = 30000;
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);
HttpClient httpclient = new DefaultHttpClient(httpParameters);
HttpPost httppost = new HttpPost(url);
StringEntity se = new StringEntity((JSONRequest));
httppost.setEntity(se);
httppost.setHeader("Accept", "application/json");
httppost.setHeader("Content-type", "application/json; charset=utf-8");
ResponseHandler<String> responseHandler = new BasicResponseHandler();
strResponse = httpclient.execute(httppost, responseHandler);
根据您的要求设置超时。
【讨论】:
【参考方案3】:#rule 始终允许 php 和 mysql 脚本在您的客户端(Android 或任何其他)之前超时。您可以通过在系统的所有端点上适当地设置超时来轻松确保这一点。
专门针对您的方案,您可以向您的用户表添加一个布尔值并将其命名为 verified
。
-
用户注册时,默认未设置已验证 (
verified=false
)。
当用户第一次登录时,设置了验证(verified=true
)。
当用户注册并且指定的username
已经存在时,允许注册if verified==false else不允许注册。
(cost) 当用户注册时,他应该登录。最好删除注册页面并向用户显示一个视觉指示器,表明注册已完成,现在我们正在登录。我通常通过在注册时显示一个进度对话框来显示“注册...”然后进入登录页面并向用户显示“正在登录...”的进度对话框。
这应该确保问题不再发生。如果登录屏幕上发生错误(超时),用户应留在该屏幕上以便他可以重试。这确实是android上的正常流程(Intent)。
您当然可以在注册页面上强制登录静默,然后继续重试,直到成功为止。 您也可以删除额外的登录步骤,只在第一次交易时验证用户,但这会花费很多(您必须检查每个请求)
【讨论】:
以上是关于Web 服务 SocketTimeOutException 导致数据库不一致的主要内容,如果未能解决你的问题,请参考以下文章