xts.so - 2015年9月
type: channel
link: https://movil.xts.so/2015/09/
description: 分享技术相关的文章
items: 用Canvas技术压缩要上传的图片
items: SPDY代理省流技术的架构
items: 用iptables的DNAT技术架设PPTP中继服务器
items: 阿里云9月1日事故的记录

用Canvas技术压缩要上传的图片
type: item
date: 2015-09-29T16:05:00+08:00
description: 背景 现在摄像头已经是手机的标配了，移动网站也做得越来越像APP。然而拍照上传这件事情的体验似乎仍然不如APP，主要原因是现在手机拍摄的照片太大，上传非常消耗流量也非常耗时。APP都会在上传前缩小要上传的照片尺寸，以期更节省流量和时间。在HTML5时代，利用文件API和Canvas技术，Web上也可以做到图片压缩上传。 过滤文件类型 首先我们希望用户能直接选择手机照片，而不是在各种类型的文件中选择。只需要在input标签中加入accept属性就可以实现这一点： 在Android4以上，iOS7以上设备实测，当用户点击这个文件选择器的时候，手机会自动调出图片库，并带有拍照选择。 读文件生成预览 用户选择了图片之后，需要读取文件内容，读出的内容可供生成预览图，也可以供后面压缩使用。使用HTML5的FileReader API可以达成这一目的。 var file = document.querySelector("[type=file]"); file.addEventListener("change", function(e) { for (var i = 0, f; f = e.target.files[i]; i++) { if (f.type.indexOf("image") !== 0) continue; var reader = new FileReader(); reader.onload = function(e) { var img = document.createElement("img"); img.src = e.target.result; document.getElementById("preview").appendChild(img); } reader.readAsDataURL(f); } }, false); 如果不需要预览图，可以不把img对象添加到DOM上。 压缩 利用Canvas渲染上下文的drawImage接口，可以把一张图片绘制到Canvas上，在这个过程中可以重新定义图片尺寸，然后再用Canvas的toDataURL接口可以生成出压缩后的图片。 var images = document.querySelectorAll("#preview img"); var dstWidth = 400, dstHeight = 300; var compressedImages = []; [].forEach.call(images, function (image) { var canvas = document.createElement("canvas"); canvas.width = dstWidth; canvas.height = dstHeight; canvas.getContent("2d").drawImage(image); // 这里传入img元素对象 var compressed = canvas.toDataURL("image/jpeg", 0.7); compressedImages.push(compressed); }); 上传 前面一步骤生成的压缩后图片是Data URL形式的，上传前需要把开头部分的data:image/jpeg;base64,截掉才是图片的Base64编码形式。 可以直接把Base64的字符串上传到服务器，然后由服务端解码为JPG图片，也可以在前端解码上传。如果要在前端解码并以文件方式上传，先要用atob函数把Base64解开，然后转换为ArrayBuffer，再用它创建一个Blob对象。文件方式上传需要用multipart/form-data格式，可以利用FormData对象组装生成好的Blob对象来实现。 function b64toBlob(b64Data, contentType, sliceSize) { contentType = contentType || ''; sliceSize = sliceSize || 512; var byteCharacters = atob(b64Data); var byteArrays = []; for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) { var slice = byteCharacters.slice(offset, offset + sliceSize); var byteNumbers = new Array(slice.length); for (var i = 0; i < slice.length; i++) { byteNumbers[i] = sli…

SPDY代理省流技术的架构
type: item
date: 2015-09-07T15:06:00+08:00
description: 基本架构 Opera Turbo技术让浏览器可以压缩浏览过程中非加密的图片内容，从而减少网络流量和提升加载速度（需要Turbo服务器的网络多线高度优化）。不过由于这一技术局限在浏览器中，并不方便使用。其实可以考虑基于OpenWRT实现一个类似的东西。 网络架构如下图所示： 路由器上可以有一个程序嗅探并劫持明文HTTP流量，利用端口号和请求内容侦测把HTTP请求识别出来，通过SPDY通道发给Proxy Server处理。Proxy Server负责HTTP请求原始资源，利用其优质的网络连接和强大的服务器计算能力把请求回来的图片压缩缓存下来，并对客户端提供响应。 一些细节 当用户发送TCP SYN到80端口的时候就可以开始劫持流量，可能需要在内核模块上挂钩子完成任务，可以看看libcap和redsocks库是否可用。 和代理服务器间使用SPDY协议通信，保持长连接主要是为了得到它的多路复用的好处，少维护一点TCP连接，毕竟一般运行OpenWRT的设备内存都不大，服务器也可以从减少连接数中受益。 Proxy Server的前端使用SPDY，后端可以直接使用开源的HTTP代理程序加入压缩模块。如果不考虑复杂的认证（认证可以利用SPDY环节的TLS客户端证书功能完成），可以优先考虑基于polipo等轻量级的HTTP代理实现。 图片压缩环节可以考虑支持GPU加速的库，使用CUDA或者OpenCL实现，当然前提是压缩服务器上有显卡。可以将SPDY代理模块和HTTP压缩模块划分到不同的服务器硬件上组建集群。 以上只是基本架构的考虑，尚没有具体实现，之前计划基于Socks服务器实现一个原型，时间关系并没有完成。

用iptables的DNAT技术架设PPTP中继服务器
type: item
date: 2015-09-05T18:03:00+08:00
description: 微软的PPTP协议是非常流行的VPN技术，它的指令控制部分使用1723端口上的TCP连接传输，而实际的VPN数据则使用GRE协议封装。如果你在一个防火墙的后面使用PPTP协议的VPN技术，那么你的防火墙必须支持PPTP穿隧才能正常使用。这是因为一般的防火墙都使用UDP或者TCP端口号的方式来生成和记录NAT对应关系，而GRE协议没有端口号。不过好在微软在GRE协议之上加了个东西，那就是PPP数据的Channel ID，利用这个东西，防火墙就可以根据它来记录NAT对应关系，把GRE报文转交给正确的内网主机。 现代的路由器一般都支持PPTP穿隧技术，所以基本无需自己设置就可以在防火墙后访问公网上的PPTP服务器建立VPN连接。另一方面，现在的商用路由器几乎都能识别1723端口，如果你在公司的路由上对1723端口做了DNAT映射，那么来自外网的PPTP请求也能正确地和防火墙后的VPN服务器建立连接。 如果使用Linux的iptables做软件防火墙，而没有使用路由器，我们依然可以建立起一个DNAT通到内网的PPTP服务器。这首先需要Linux内核加载ip_nat_pptp和ip_conntrack_pptp两个模块。可以用lsmod命令检查是否已经载入。 # lsmod | grep pptp 如果没有载入，可以使用modprobe加载内核模块 # modprobe ip_nat_pptp # modprobe ip_conntrack_pptp 当然，为了做NAT，把IPv4 Forward打开也是非常必要的 # sysctl -w net.ipv4.ip_forward=1 接下来可以设置允许PPTP的TCP协议和GRE协议连入 # iptables -A INPUT -d $FIREWALL_IP -p tcp --dport 1723 -j ACCEPT # iptables -A INPUT -d $FIREWALL_IP -p gre -j ACCEPT # iptables -A FORWARD -d $PPTPD_IP -p tcp --dport 1723 -j ACCEPT # iptables -A FORWARD -d $PPTPD_IP -p gre -j ACCEPT 最后配置DNAT规则完成转发，当然为了识别DNAT后的地址对应关系，转换地址后还需要把它加入MASQUERADE里。 # iptables -t nat -A PREROUTING -d $FIREWALL_IP -p tcp --dport 1723 -j DNAT --to-destination $PPTPD_IP # iptables -t -A POSTROUTING -d $PPTPD_IP -p tcp --dport 1723 -j MASQUERADE 这样，当PPTP客户端拨号到$FIREWALL_IP上的时候，实际是后面的$PPTPD_IP服务器在提供服务。运行iptables的机器也可以视为PPTP中继服务器，向后面的服务转发数据。

阿里云9月1日事故的记录
type: item
date: 2015-09-02T16:06:00+08:00
description: 事件经过 事情发生 9.1 11:40 9月1日上午11点多钟，我正登录服务器进行维护工作，因为前一天在服务器部署了新的seafile，还有搜索等功能需要手动开启。大约到了11:40的时候，朋友让我帮他测试一个IP的路由路径，我就在服务器上运行了mtr测试。然而mtr刚发送了几个数据包，就自己退出了。我很奇怪地再运行一次，系统却提示我无法运行mtr，没有那个程序。这还真是奇怪的事情，前几秒还在运行的程序，现在就没了。我以为是不是PATH环境变量出什么问题了，运行which mtr也找不到。于是决定重新安装一个，直接apt-get安装。结果问题又来了，apt-get提示我程序配置出错，要运行dpkg --configure修复。我再按提示运行，却看到提示说dpkg命令也不存在了。 我意识到服务器出现问题了，本着“重试、重启、重装”的三重原则，我选择了重启服务器。结果悲剧地无法启动了，连接管理终端发现机器卡在Booting from Hard Disk...就不动了。我的第一反应是保护自己的数据，马上到阿里云后台的磁盘管理中创建了一个磁盘快照。然后提交工单寻求技术支持。 提交工单 9.1 12:16 12:16我提交了工单，等着十几分钟没有回应，不过因为是个人网站，我就没有打电话催促。此时我还不知道阿里云发生了线上事故，只以为我的云服务器硬盘坏掉了，不过考虑到阿里云宣传的多重备份的硬盘管理策略，加上我又做了一个快照，我并不太担心自己的数据丢失。于是我提交完工单就和同事吃饭去了。 客服回复 9.1 12:50 回到办公室的时候，已经是下午一点多钟，阿里云工单有了进展，客服在12:49接手工单，在12:52给出事故原因：因云盾升级触发bug，导致少量文件被系统误删除。我们已经第一时间启动系统回滚。被误删除的文件正在陆续恢复，您无需进行手动恢复操作，请耐心等待。对您带来的不便我们深表歉意。 好吧，这时候我觉得阿里云的服务还不错，事故原因合情合理，客服也代表公司表达了歉意。甭管多大的软件公司，谁能不出点差错呢，这次也就和以前杀毒软件病毒库升级误杀系统文件导致开不了机一样。既然让我不要操作，我就不管了。 论坛炸锅 9.1 15:40 我本以为恢复一个误删的文件并不难，对于那些没重启的机器，既然云盾能把文件删了，自然可以统一往下分发文件；对于已经重启启动不能的云服务器，阿里完全可以用程序启一个等同系统的实例然后把用户的文件系统挂上去，再参照前面的方法恢复。 阿里云总共能用的系统镜像也没几个，统一恢复工作应该不会超过一个小时就能完成。结果事实证明我毕竟还是too young，时间过去了三四个小时，还是没有完成恢复。v2ex和阿里云官网论坛上哀鸿遍野，无数人在诉说自己被老板被客户骂到快死掉的过程。 等待了三个多小时后，阿里云工单上终于回复：您好，问题目前还在恢复中，有进一步进展，我们会随时和您反馈，对您带来的不便我们深表歉意，还请您耐心等待。 阿里云的官网则挂出了公告 尊敬的客户： 因云盾安骑士server组件的恶意文件查杀功能升级触发了bug，导致部分服务器的少量可执行文件被误隔离。系统在第一时间启动了回滚，目前被误隔离的文件已基本恢复。我们正在回访个别尚未恢复的客户，协助尽快恢复。对于受影响的客户，我们将立即启动百倍时间赔偿，并避免类似失误再次发生。我们深知这一失误对您业务带来的影响和损失，再次致以最深刻的歉意。 统一恢复完成 9.1 18:37 18:15阿里云工单回复Windows系统已经恢复，Linux系统还需要时间。18:37再次更新，说全部恢复完毕，并让我如果有问题随时反馈。然而我的服务器还是挂着的，我在18:43反馈了服务器的实际情况，表示并没有修复，不同意关闭工单，要求进一步处理。 收到阿里云通知邮件 21:09 时间又过去两个多小时，收到了阿里云的通知邮件： 如果您的ECS机器云盾进程已经退出，我们无法下发文件进行修复，请通过以下方式开启： Windows机器请启动 Alibaba Security Aegis Detect Service 服务 步骤： 1）打开计算机管理 2）选择 “服务” 3）找到 Alibaba Security Aegis Detect Service 服务并启动 4）如果找不到该服务，请尝试查找并启动 Alibaba Security Aegis Update Service 服务 Linux 机器 方案1： service aegis start 方案2： 进入/usr/local/aegis/aegis_client目录，选择高版本子目录，例如（aegis_00_79) 进入该子目录，运行 AliYunDun 进程 工单上并没有任何解释或者回复，我留言表示服务器挂了无法启动阿里云盾的进程。 最终…