abbshr.github.io
abbshr.github.io copied to clipboard
人们往往接受流行,不是因为想要与众不同,而是因为害怕与众不同
今年年初做毕设那段时间, 曾涉及一些关于数据存储方面的技术, 着手实现时也发现了不少乐趣, 这里就梳理一下思路, 做个总结, 也尽量让人读完之后有个全面的了解, 感谢工业界和学术界产出的详实资料文献和笔录经验, 给我学习过程提供了莫大便利! 其中过于底层的技术的原理我可能没法做到了然于胸, 当然我会持续深入学习那些不甚了解/拿不准的东西, 不断完善知识技能树. ## 几个术语 来区分这两个专有名词: - 数据库 - 存储引擎 数据库往往是一个比较丰富完整的系统, 目的是大而全, 提供了丰富的查询和一系列复杂的数据操作逻辑, 或者是在网络层面,水平扩展等支持. 然而一个存储引擎目标则是小而精, 因为它是纯粹专注于**读/写/存储**, 一般来说, 直接使用存储引擎的是像数据这样的上层存储系统. ## 那么来思考几个问题 如果是你, 你要如何:...
这是机智的面试官问的第三个问题~ 我当时回答的比较含糊,因为毕竟一个网络协议的内容如此之复杂,之前也并没有过于关注掩码这东西究竟起什么作用。出于被自己熟悉领域难住的羞愧,面试后立马去翻看RFC,找到好长一大段的描述。 由于目前网络上还没有关于WebSocet各方面介绍与考量十分详尽的文章(当然英文版的RFC除外),这里我说一下那个神奇的Masking Key是做什么的吧。 首先不要把它与IP网络中的子网掩码弄混,绝壁不是一个概念,后者是用来划分子网的, 而前者是考虑到网络安全问题而设计的。 WebSocket协议规范里讲:“为了避免迷惑网络中介(如代理服务器),以及涉及到安全问题,客户端必须mask所有送给服务器的frame。” 不明白怎么回事?没关系,在此之前先了解下网络上**针对基础设施的攻击**,然后才能明白掩码的设计道理。 ### 针对基础设施的攻击 通过WebSocket协议成为被攻击对象的,除了终端设备之外还有其他部分的web基础设施,比如代理服务器就可能成为攻击的对象。 随着websocket协议被开发出来,一项针对代理服务器的攻击(污染那些广泛部署的缓存代理服务器)实验也开始进行。 一般形式的攻击是跟被攻击者控制的服务器建立连接,并构造一个类似WebSocket握手一样的UPGRADE请求,随后通过UPGRADE建立的连接发送看起来就像GET请求的frame去获取一个已知资源(在攻击场景中可能是一个点击跟踪脚本或广告服务网络中的资源)。 之后远程服务器会返回某些东西,就像对于这个伪造GET请求的响应,并且这个响应会被很多广泛部署的网络中间设备缓存,从而达到了污染缓存服务器的目的。对于这个攻击的产生的效应,可能一个用户被诱导访问受攻击者操控的服务器,攻击者就有可能污染这个用户以及其他共享相同缓存服务用户的缓存服务器,并跨域执行恶意脚本,破坏web安全模型。 ### 应对措施——掩码 为了避免面这种针对中间设备的攻击,以非HTTP标准的frame作为用户数据的前缀是没有说服力的,因为不太可能彻底发现并检测每个非标准的frame是否能够被非HTTP标准的中间设施识别并略过,也不清楚这些frame数据是否对中间设施的行为产生错误的影响。 对此,WebSocket的防御措施是mask所有从客户端发往服务器的数据,这样恶意脚本(攻击者)就没法获知网络链路上传输的数据是以何种形式呈现的,所以他没法构造可以被中间设施误解为HTTP请求的frame。 **这就是掩码存在的原因**。 ### 继续安全性探究——如何选择掩码? 本来到这里就该结束了, 但是协议很负责的深入说明了掩码选择上的要求~ 客户端**必须**为发送的每一个frame选择新的掩码,要求是这个掩码无法被提供数据的终端应用(即客户端)预测。 算法的选择上,为了保证随机性,可以借助密码学中的随机数生成器生成每个掩码。 倘若使用相同的掩码会有什么后果呢? 假设每次发送frame使用了相同的掩码或下一个掩码如何选择被猜出的话,攻击者就可以发送经过mask后类似HTTP请求的frame(做法很简单:攻击者以希望在网络链路上显示的形式构造数据,然后用下一个掩码mask再发出去)。 至于如何用掩码mask原始数据,在前面的 [学习WebSocket协议—从顶层到底层的实现原理(修订版) ](https://github.com/abbshr/abbshr.github.io/issues/22)...
### 从RealTime说起 自从即时Web的概念提出后,RealTime便成为了web开发者们津津乐道的话题。实时化的web应用,凭借其响应迅速、无需刷新、节省网络流量的特性,不仅让开发者们眼前一亮,更是为用户带来绝佳的网络体验。 近年来关于RealTime的实现,主要还是基于Ajax的拉取和Comet的推送。大家都知道Ajax,这是一种借助浏览器端JavaScript实现的异步无刷新请求功能:要客户端按需向服务器发出请求,并异步获取来自服务器的响应,然后按照逻辑更新当前页面的相应内容。但是这仅仅是**拉取**啊,这并不是真正的RealTime:缺少服务器端的自动推送!因此,我们不得不使用另一种略复杂的技术Comet,只有当这两者配合起来,这个web应用才勉强算是个RealTime应用! ### Hello WebSocket! ![WebSocket in Client Chrome](https://raw.githubusercontent.com/abbshr/abbshr.github.io/master/source/postimg/wsc.png) 不过随着HTML5草案的不断完善,越来越多的现代浏览器开始全面支持WebSocket技术了。至于WebSocket,我想大家或多或少都听说过。 这个WebSocket是一种全新的协议。它将TCP的Socket(套接字)应用在了web page上,从而使通信双方建立起一个保持在活动状态连接通道,并且属于**全双工**(双方同时进行双向通信)。 其实是这样的,WebSocket协议是借用HTTP协议的`101 switch protocol`来达到协议转换的,从HTTP协议切换成WebSocket通信协议。 再简单点来说,它就好像将Ajax和Comet技术的特点结合到了一起,只不过性能要高并且使用起来要方便的多(当然是之指在客户端方面。。) ### 设计哲学 RFC草案中已经说明,WebSocket的目的就是为了在基础上保证传输的数据量最少。 这个协议是基于Frame而非Stream的,也就是说,数据的传输不是像传统的流式读写一样按字节发送,而是采用一帧一帧的Frame,并且每个Frame都定义了严格的数据结构,因此所有的信息就在这个Frame载体中。(后面会详细介绍这个Frame) #### 特点 - 基于TCP协议 - 具有命名空间 - 可以和HTTP...
![gitscout-image-from-clipboard-2](http://static.gitscout.com/e5ffc1a36972052e7cfc8aa90099e867384c7ccd7eaa1f5ef67754a720e0ad8a.png) 缺点: + 缺少 markdown 自动补全机制 + 刚写的内容容易丢失 (缺少自动保存机制), 需要手动点击一下 draft, 不然一个`ESC`全没了. 毕竟不是专为写作设计的工具, 也是可以忍受的.
> 好一段时间没整理笔记了, 一是整个10月份基本上都在陪爸妈在上海和苏杭玩, 二是离开阿里之后有不少事要忙, 除了搞毕业设计就是参与新团队新项目, 也就没写什么东西. 好在有机会在又拍云做一次技术分享, 让我介绍一下以前做过的流控方案啥的, 就算做一个阶段性总结了. ### Chapter 1: 为毛要做流控? 如果世界上每台服务器都可以瞬间轻松应付海量TCP连接(HTTP请求), 或者路由器效率和网络带宽高的离谱, 谁还费那心思做流控? 所以答案很简单, 资源有限. 为了让有限的资源尽量为多数人服务提供更稳定的服务, 流控技术出现了. 我们可以把流控的重要性细分为如下三点: - **Protection**: 保护上游服务器, 减轻(D)DoS, TCP SYN flood等攻击带来的负面效果. - **QoS**:...
### Frontend HTML5为我们带来不少新奇的东西,除了那几个闪亮的明星“WebSocket”、“Worker”、“Canvas”等等之外,还有几个非著名演员(包括但不限于):“Blob”、“ArrayBuffer”、“URL”、“FormData”。这些小角色是用来支持二进制字节数据操作的。但`Blob`和`ArrayBuffer`的分界线似乎很模糊,我们写程序时往往会纠结应该使用哪种方式来在和Server进行数据交互,所以今天我们就把他们弄明白。 #### ArrayBuffer 我在前几篇里简单的翻译了Node中`Buffer`函数和`buffer`对象的解释及用法。没错,_buffer_就是缓冲区的意思。“缓冲”的目之一就是为了解决设备之间I/O速度差异的问题:当一个Socket A向Socket B发送数据时,到达的比特流首先会在B的缓冲区里停留,然后再由B进行读取,如果没有这个缓冲区,B就有可能无法读取完整的数据。 当然,这里谈到的ArrayBuffer就类似刚刚所说的_缓冲区_。这些缓冲区都是按byte(字节)进行划分的。这个ArrayBuffer和Node环境下的Buffer很像,并且都是一个特殊的数组(将缓冲区作为数组来使用)。虽说Node里也有ArrayBuffer,不过和前端的ArrayBuffer稍有些区别,这里我们讨论的是浏览器端的ArrayBuffer。 如果缓冲区这个解释略抽象,那么就把他当成是一个用来装byte数据的容器吧。可以算是浏览器端最基础的数据格式了,请记住关键字:**字节**。这是用以区分后面提到的Blob的最好描述。 ``` // 申请一个10byte的缓冲区 var af = new ArrayBuffer(10); ``` ##### view 对于ArrayBuffer,HTML5还提供了几个高级的封装,用以快捷操作字节数据,我们称之为view: - Uint8Array - Uint16Array - Uint32Array - Int8Array...
昨晚做了个技术分享, 今天整理一下发上来. ## Be unknown, hard to survive 对于我们应用开发团队来说, 上层应用开发每天要面对最多的就是业务逻辑. 而我们都知道, 业务逻辑的最大特点就是 **量大,复杂,多变**. 三天两头变动的需求以及突发的任务都会很大程度的影响业务逻辑, 导致它们状态很难控制. 也正因如此, 特别是创业团队, 在开发之初往往着重于功能的实现上, 很可能就降低了对代码质量, 算法性能以及架构设计的要求. 这种情况下对某些环节的监控基本上就被遗漏了, 于是导致潜在的问题不易发现, 突发问题不易排查. 所以最开始产出的代码质量很难保证, 即便是上线了后能否稳定运行心里也不敢打保票. ### 当线上服务出现问题时... 当有人告诉你服务挂了时, 怎么办呢, 你可能会很淡定的先去服务器上查查日志来看看他究竟怎么了:...
单线程世界里如何处理大量并发任务而不阻塞主线程的执行在做JavaScript开发时显得尤为重要。不过这不是今天的主题。既然浏览器中常常因为滥用JavaScript的事件而导致主线程阻塞,那我们就先来看看客户端JavaScript中的线程。 ## 线程的阻塞 `setTimeout` 和 `setInterval`是JavaScript中的两个定时器,指定一定时间过后触发某某动作,常用于JavaScript制作的动画效果中。或许你尝试过这样用: ``` setTimeout(function () {}, 0); ``` 假如你想通过这种方式来实现在0ms之后执行函数,那么你会发现往往事与愿违:函数并没有立即执行。 为什么?这种方式和: ``` (function () {})(); ``` 有什么区别吗? 乍看似乎是一样:直接执行那个函数。不过第一段代码中的函数若想和第二个效果一样,要基于**两个前提**:时钟周期、代码上下文。 **时钟周期**:你无法改变,这是有你的机器硬件所决定的,你的函数执行的最小延时时间取决于系统时钟的最小周期,所以即便是你把setTimeOut的延时参数设为0毫秒,触发也不会是即刻的,因为他永远大于系统的时钟周期。一般计算机的最小时钟周期在4ms~15ms左右,所以就算是3ms的延时,函数也不会再3ms之后被触发,而是等到最小时钟周期到了之后。 **代码上下文**:这个是可以改变的,因为setTimeout的执行环境由你而定,你想让他在那里执行都可以。如果整个代码的末尾执行了setTimeout,handle函数会在最小时钟周期一过便立即执行了,但是如果setTimeOut还有后文(下面还有其他要执行的代码),则首先会将handle函数依次压入事件队列,然后继续向下执行其他代码,等到所有代码都执行完毕,再将事件队列中的事件依次出队列进入事件轮询并执行。因此你会发现: ``` var arr = []; setTimeout(function ()...
爬虫常被看作是一个边缘hack技术。通过别人的前端页面获取数据似乎为人所不耻,但是不管人们怎么评价它,爬虫的作用就摆在那里,有用没用全取决于你的目的。 为什么在互联网这片浩瀚的海洋里,我仍然旧事重提?起因与我最近做的事有关。 如果说让你爬取一个网页,你会怎么做? OK,你很可能会说这很简单:“拿一个HTTPClient请求一下那个URL不就行了”。 对于普通网页,可能很顺利的就拿下了,那么对于如下几种情况,该怎么办呢: - 抓下来的是一个HTML页面,但statuscode为302。 - 仍然抓下来了HTML页,并且状态码也为200,但就是内容不对。 - 直接拒绝连接 - 抓下来的内容应该是正确的但乱码 - 得到了HTML页,但不仅状态码为302且,而且重定位的location还不变。 我把以上情景大致分为两类: - 拒绝访问 - 允许访问 拒绝访问的一般就是在Web server上做了爬虫防范措施。当有客户端请求数据时,对其进行检测,如果判断为浏览器,则允许与自己通信,否则就可以用各种方法为难客户端,比如:close这个socket(切断连接),返回非浏览器客户端不懂的redirect 302或非法访问等等。 至于如何断定该客户端就是浏览器或一定不是浏览器,有好多方法,打开network流量监视器,可以看到每个请求的详细内容,服务器可以检查: `Referer`,`Host`,`User-Agent`,因为这些是浏览器最具代表性的字段。 一般我们把这几个字段也一同提交过去大多数服务器就能被搞定了,但随着技术的进步,防范措施也在不断改进。 但对于最后一种情况,你可能会很头痛并且百思不得其解,这也是我在实际操作中遇到的问题。 我在爬取一个看似再正常不过的页面时,爬取工具竟然crash了!排除网络问题,我又详细的看了几遍浏览器中的请求头,以及是否有明显的redirect痕迹。结果很遗憾:network控制台仅显示了一个请求,响应码为200!: ![2014-10-27 12...
# 重构缘由 在去年的一个 PO 里曾简要介绍了使用 Ansible 做应用部署的做法: https://github.com/abbshr/abbshr.github.io/issues/57, 但是随着对 ansible 的了解, 一些灵活而简单的管理思路才逐渐被揭示, 比如说我们要谈的版本控制机制. 最初使用 ansible 时, 由于缺乏了解和使用经验, 完全凭借自己的推测和 shell 脚本以一种丑陋的方式去实现我们需要的功能, 显然我们达到了目的, 但是其流程之复杂, 代码结构之混乱, 导致我们每次更新或者应用到新的项目时很难做到一键化完成, 并且每次手动修改往往会导致出现莫名其妙的诡异问题. 因此这套系统常常被组内成员(包括自己在内..)所诟病. 借此重构机会, 我们将发挥 ansible 的优势:...