通常情况下,做移动开发时,如果要向后台请求数据,都会直接使用TCP通信。但实际上一来HTTP比TCP简单易用多了,二来有很多现有CGI如果要进行改造得花很大功夫。还是会有使用HTTP请求来拉取数据。

在做Android QQ二维码时,扫描到二维码字符串,就是把该字符串用HTTP传给后台,后台解析后返回给客户端,客户端再进行下一步处理。在提交测试后,测试同事发现,在移动网络上,DNS解析会经常失败,导致二维码扫描功能不可用。功能测试不通过,导致无法发布。同时测试喜欢在清空DNS缓存和屏蔽了DNS解析的情况下,二维码解析功能仍然可用。因此这里增加了如下处理:

  1. 进入“扫一扫”的界面时,客户端就开始对CGI所在域名进行解析,并把解析结果缓存到本地文件;
  2. 发起CGI请求时,还是使用原域名进行请求,如果DNS解析失败,则用第一步缓存的IP替换掉CGI中的域名再发起一次请求。

由于国内有好几个网络运营商,公司在不同的网络环境都有不一样的出口IP,因此选择最近的IP才能快速访问CGI。所以缓存域名结果时,需要区分网络分别保存。
more
这样,当用户的手机上至少成功使用了一次二维码扫描时,CGI对应的IP就保存下来了(至少有一个出口IP)。下次访问(同一次登录或者关闭手机QQ后再打开)时,即使DNS解析失败,还是能使用上一次保存的IP进行访问,可能会相应比较慢(用户从A网络切换到了B网络),但起码保证了功能的可用性。实现起来也不难,Java都有现成的接口了。

主动发起DNS解析只要调用InetAddress的接口即可,把获取到的结果保存到SharedPreferences供下次使用:

public static void lookupIP(Context context, String host){
     SharedPreferences preferences = context.getSharedPreferences("host", 0);
     String ip;
     try {
          InetAddress address = InetAddress.getByName(host);
          ip = address.getHostAddress();
          SharedPreferences.Editor editor = preferences.edit();
          editor.putString(host, ip);
          editor.commit();
          QLog.d(TAG, "lookup address: " + host + ", ip: " + ip);
     } catch (UnknownHostException e) {
     }
}

然后在HTTP请求出现DNS解析错误的时候,用IP替换掉URL中的域名即可:

HttpResponse response = null;
try{
     response = openRequest(context, url, host, method, params, header);
}catch (IOException e){
     //DNS错误改用ip重新发一次
     String ip = getIP(context, host);
     if(ip != null){
          url = url.replace(host, ip);
     }
     response = openRequest(context, url, host, method, params, header);
}

这里之所以不直接用上次保存的IP来替换域名进行访问,是因为在正常情况下,进入“扫一扫”的界面时,已经预先对所用域名进行了预解析,如果解析成功,调用openRequest时就不用DNS查询了;而如果预解析失败了,openRequest的时候还能再尝试一次,失败时才使用IP访问。同时,很多用户都会在移动网络和WIFI网络中切换,如果直接用IP替换域名,会出现用户当前是移动网络,而是用的域名IP是在教育网IP(上一次使用的是教育网WIFI)的情况,导致CGI响应变得更慢。

当然,更好的解决方法是:先检测当前用户的网络环境,如果本地保存了对应网络的IP,直接使用IP请求;如果没有,再改用域名来请求。

还有更进阶的方案:第一次登陆时,从服务器拉取一份host列表(每个域名包括各个网络环境的出口IP),把所有HTTP请求的域名都替换成相应IP,这样就能完美解决DNS不可用、DNS解析失败的情况了。之后维护host文件的更新即可。

Comments
Write a Comment

Tags

css3   魅力CSS   nodejs   loading   CSS   疯狂的菊花   html5   animation   compiler   编译脚本   png   WordPress   智能   旅行   优化   模板   历史记录   跨域   manifest   frame   canvas   动画   js   离线应用   codelet   transform   抽取   java   兼容问题   发布脚本   富文本   那一年在他乡   htaccess   iframe   帧动画   加载速度   intelligent   跨浏览器   DNS解析   插件   checkbox   单边   step-start   vary   复选框   自动更新   转换   文本溢出   盒子阴影   menu   blob   西安   滑动背景   box-shadow   内存占用   键盘事件   python   auto   text overflow   background   所见所得   android   rotate   字节数   合并   文本框   slide   字符串连接符   协议   伪类   兄弟选择符   网格   节点位置比较   空白   斜线拼接   自定义命令   溢出   clock   素描   无法更新   分隔符   字符编码   body   下载文件   精灵图   step   nodej   ubuntu   apache   css3选择器   创建文件   多级菜单   编辑状态   ajax   阴影   垂直   chrome   管道   时钟   firefox   背景   文件上传   createobjecturl   游记   下载   放射渐变   版本号   宽高   照片   localStorage   渐变背景   图片   图片拼接   属性值检测   自动生成   计算   返回键   oauth   合图   reset   调用   cavnas   漏洞   按钮   margin   线性渐变   xsrf   被黑   tab   checked   修复   border   消失   step-end   sprite   common-upload   菜单   兄弟选择器   字符串   svn   九寨沟   缩进   css遮罩   svg   添加系统服务   gzip   插入代码   动态   加速   模拟