• Feeds

  • Archive for the ‘Web’ Category


    是时候支持HTTPS了:免费SSL证书letsencrypt配置教程

    今天抽空将 blog 增加了 HTTPS 支持,并停止了原来的 HTTP 服务。

    由于证书仅网站域名需要,因此使用了免费的 Let’s Encrypt 证书服务。

    根据维基百科的说明,Let’s Encrypt 是一个于2015年三季度推出的数字证书认证机构,将通过旨在消除当前手动创建和安装证书的复杂过程的自动化流程,为安全网站提供免费的SSL/TLS证书。Let’s Encrypt 是由互联网安全研究小组(ISRG,一个公益组织)提供的服务。主要赞助商包括电子前哨基金会,Mozilla 基金会,Akamai 以及思科。

    2015年12月3日,该服务进入公测阶段,正式面向公众。

    2016年4月12日,该项目正式离开Beta阶段。

    到2016年9月9日,Let’s Encrypt 已经发放 1000 万张证书。因此对于大部分中小型网站来说,是一个值得考虑的选择。

    letsencrypt

    HTTPS 启用及配置的主要步骤如下,假设你已经有一个正常运行的 HTTP 网站。

    1. 打开 https://certbot.eff.org/ 选择对应操作系统与 Web 服务器,选完后出现响应的平台说明。由于我的系统运行在 nginx 及 Ubuntu,选完之后出现的说明地址是 https://certbot.eff.org/#ubuntuxenial-nginx

    2. 执行命令,并根据需要修改相应域名参数。

    $ sudo apt-get install letsencrypt
    $ letsencrypt certonly --webroot -w /var/www/timyang.net -d timyang.net -d www.timyang.net
    

    3. 修改 nginx
    将 80 端口相关配置改成 443 端口,并增加相关配置

    # listen       80;
    listen 443 ssl;
    server_name timyang.net www.timyang.net;
    
    ssl_certificate /etc/letsencrypt/live/timyang.net/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/timyang.net/privkey.pem;
    

    再增加 80 端口自动跳转

    server {
        listen 80;
        server_name timyang.net www.timyang.net;
        return 301 https://$host$request_uri;
    }
    

    4. 重启 nginx,并用 chrome 访问,如果 URL 地址之前出现锁标记,则说明设置成功。
    如果页面中还包含有嵌入的 HTTP 元素,chrome 会仍然显示 i 标记,通过点击 i 之后的元素说明逐个修复即可。

    timyang-https

    另外注意 Let’s Encrypt 每次只有 90 天有效期,但可以通过脚本进行更新

    测试运行

    $ sudo letsencrypt renew --dry-run --agree-tos
    

    如果运行成功,建议将正式更新脚本加到 cron 脚本中,一劳永逸。

    $ sudo letsencrypt renew
    

    虽然 Let’s Encrypt 是一个免费的服务,但对 letsencrypt 使用感到满意的朋友,还可以去他们网站进行赞助。

    用Twitter的cursor方式进行Web数据分页

    本文讨论Web应用中实现数据分页功能,不同的技术实现方式的性能方区别。

    上图功能的技术实现方法拿MySQL来举例就是

    select * from msgs where thread_id = ? limit page * count, count

    不过在看Twitter API的时候,我们却发现不少接口使用cursor的方法,而不用page, count这样直观的形式,如 followers ids 接口

    URL:

    http://twitter.com/followers/ids.format

    Returns an array of numeric IDs for every user following the specified user.

    Parameters:
    * cursor. Required. Breaks the results into pages. Provide a value of -1 to begin paging. Provide values as returned to in the response body’s next_cursor and previous_cursor attributes to page back and forth in the list.
    o Example: http://twitter.com/followers/ids/barackobama.xml?cursor=-1
    o Example: http://twitter.com/followers/ids/barackobama.xml?cursor=-1300794057949944903

    http://twitter.com/followers/ids.format

    从上面描述可以看到,http://twitter.com/followers/ids.xml 这个调用需要传cursor参数来进行分页,而不是传统的 url?page=n&count=n的形式。这样做有什么优点呢?是否让每个cursor保持一个当时数据集的镜像?防止由于结果集实时改变而产生查询结果有重复内容?
    在Google Groups这篇Cursor Expiration讨论中Twitter的架构师John Kalucki提到

    A cursor is an opaque deletion-tolerant index into a Btree keyed by source
    userid and modification time. It brings you to a point in time in the
    reverse chron sorted list. So, since you can’t change the past, other than
    erasing it, it’s effectively stable. (Modifications bubble to the top.) But
    you have to deal with additions at the list head and also block shrinkage
    due to deletions, so your blocks begin to overlap quite a bit as the data
    ages. (If you cache cursors and read much later, you’ll see the first few
    rows of cursor[n+1]’s block as duplicates of the last rows of cursor[n]’s
    block. The intersection cardinality is equal to the number of deletions in
    cursor[n]’s block). Still, there may be value in caching these cursors and
    then heuristically rebalancing them when the overlap proportion crosses some
    threshold.

    在另外一篇new cursor-based pagination not multithread-friendly中John又提到

    The page based approach does not scale with large sets. We can no
    longer support this kind of API without throwing a painful number of
    503s.

    Working with row-counts forces the data store to recount rows in an O
    (n^2) manner. Cursors avoid this issue by allowing practically
    constant time access to the next block. The cost becomes O(n/
    block_size) which, yes, is O(n), but a graceful one given n < 10^7 and
    a block_size of 5000. The cursor approach provides a more complete and
    consistent result set.

    Proportionally, very few users require multiple page fetches with a
    page size of 5,000.

    Also, scraping the social graph repeatedly at high speed is could
    often be considered a low-value, borderline abusive use of the social
    graph API.

    通过这两段文字我们已经很清楚了,对于大结果集的数据,使用cursor方式的目的主要是为了极大地提高性能。还是拿MySQL为例说明,比如翻页到100,000条时,不用cursor,对应的SQL为

    select * from msgs limit 100000, 100

    在一个百万记录的表上,第一次执行这条SQL需要5秒以上。
    假定我们使用表的主键的值作为cursor_id, 使用cursor分页方式对应的SQL可以优化为

    select * from msgs where id > cursor_id limit 100;

    同样的表中,通常只需要100ms以下, 效率会提高几十倍。MySQL limit性能差别也可参看我3年前写的一篇不成熟的文章 MySQL LIMIT 的性能问题

    结论

    建议Web应用中大数据集翻页可以采用这种cursor方式,不过此方法缺点是翻页时必须连续,不能跳页。

    PubSubHubbub的价值

    HTTP是大部分互联网应用接口的首选协议,但是由于HTTP协议短连接且是单向请求(request/response)的特性,决定了调用方要获得实时结果,需要不断的轮询(Polling)服务接口。从而造成大量无意义的请求及服务器相应的开销。针对此现状,许多方案应运而生。比如基于XMPP pubsub的方案、基于HTTP的web-hook的方案、适合即时通讯的comet方案等。但是由于HTTP的简洁及标准的力量,上述方案都没有得到大规模的流行HTTP Polling的现状暂时无人能够改变。

    PubSubHubbub是Google推出的一个基于Web-hook方式的解决方案,它包括PubSubHubbub协议及一个开源的参考实现(Reference Implementation)

    原理

    原理及数据流图在官网的Slide上已经有详细描述,这里以静态图补充。

    pubsubhubbub

    价值

    Publisher发布方

    许多Blog服务提供者来说,RSS对它们来说是一个鸡肋,对运营及广告等业务没什么帮助,但是却流量很大。因此他们经常非常矛盾的维护着这个接口。如果PubSubHubbub能够在业界大范围的适用,至少从访问压力层面解除了BSP对提供RSS接口之忧。

    特例 Realtime RSS(Twitter, 微博服务等)

    Twitter/微博等realtime RSS可以从此方案受益,按照常规的方案,订阅方为了获取realtime的结果,几乎需要以每分钟1次的频率来访问RSS API, 如果订阅方能够以PubSubHubbub的方式来访问RSS,那么RSS API的请求量几乎可以降为0

    Subscriber订阅方

    Subscriber比如RSS阅读器,搜索引擎等类似业务。Google Reader看似PubSubHubbub最大的赢家。
    另外在有hub的前提下,即使Publisher不支持PubSubHubbub, subscriber可以通过hub直接取到feed内容,就是说类似阅读器这样的应用现在就可以完全切换到PubSubHubbub体系上。

    不适合的场景

    Twitter client, 由于client处于防火墙后,通常也没有固定的可直接访问的HTTP Endpoint, 所以没法适用PubSubHubbub

    最后,PubSubHubbub是否在业界大范围的改变现状,我们拭目以待。

    12