先看个图,从 Ipv6 的相关资讯可以看到,各大互联网公司都在推进 Ipv6 的部署和支持,这离不开工信部信通院(信息通信研究院)的大力推进。

Ipv6 的”好处”就不多说了,如果使用 Ipv6,每个设备都可以分配一个固定的地址,定位起来就方便多了,想想就很美(kong)好(bu)吧。

网络设施和软件的改进我们暂且不谈,就来谈下如何保障业务访问的连通性。

一谈到 Ipv6 ,大家印象里访问一个 Ipv6 的网站的过程是这样的:
通过 DNS 的 AAAA 解析获取到了域名对应的 Ipv6 的地址,然后就会通过 Ipv6 的地址访问网站,如果访问不通,业务就会挂掉,导致用户访问失败。

如果这个网址只支持 Ipv6 ,那就是这种现象,如果网址本身支持双栈,即同时支持 Ipv4 和 Ipv6 ,那么在某些环境下,会启用 Happy Eyeballs 算法,进行快速回退,使用 Ipv4 进行访问。
这个算法的提出是在2011年,最初推广 Ipv6 的时候,为了避免让大家担心使用 Ipv6 后如果网络不通,用户会流失。


阅读全文

在Docker内启用perf

今天在学习 极客时间的系列课程-《 Linux性能优化实战》时,通过Docker运行了一个容器,需要借助perf来分析性能瓶颈,正常启动后,在容器内通过apt-get install -y linux-tools安装好了perf工具,但是启动时,却提示以下信息:

┌─Error:───────────────────────────────────────────────────────────┐
│No permission to enable cycles event.                             │
│                                                                  │
│You may not have permission to collect system-wide stats.         │
│                                                                  │
│Consider tweaking /proc/sys/kernel/perf_event_paranoid,           │
│which controls use of the performance events system by            │
│unprivileged users (without CAP_SYS_ADMIN).                       │
│                                                                  │
│The current value is 3:                                           │
│                                                                  │
│  -1: Allow use of (almost) all events by all users               │
│>= 0: Disallow raw tracepoint access by users without CAP_IOC_LOCK│
│>= 1: Disallow CPU event access by users without CAP_SYS_ADMIN    │
│>= 2: Disallow kernel profiling by users without C                │
│                                                                  │
│                                                                  │
│Press any key...                                                  │
└──────────────────────────────────────────────────────────────────┘

提示没有权限,需要更改 /proc/sys/kernel/perf_event_paranoid 文件,然后尝试执行:

$ echo 0 > /proc/sys/kernel/perf_event_paranoid
bash: /proc/sys/kernel/perf_event_paranoid: Read-only file system

解决办法:
需要使用 –privileged 参数,所以必须要重新启动容器,重新安装相关package.
启动容器时:docker run --privileged xxx 即可. 再次执行perf top -g -p 1385
查看分析效果
阅读全文

HTTP/2 in GO(五)--大结局

相关阅读:

通过前边四章,我们了解了HTTP/2的特性,以及如何在Go中利用HTTP/2的相关特性进行一些开发工作。本章作为一个收尾,我来谈一些自己对HTTP/2的理解,以及HTTP/2的应用前景展望,个人观点,不一定对,欢迎大家留言讨论。

先回顾下:

HTTP/2新增特性

  • 二进制分帧(HTTP Frames)
  • 多路复用
  • 头部压缩
  • 服务端推送(server push)

HTTP/2通过多种多种技术手段(如:多路复用,头部压缩,优先级等),极大的优化了HTTP的C/S双端的数据交互体验,解决很多以往HTTP/1.1协议本身不能解决的问题。

HTTP/2的优势

  • 低延迟的内容传输(多路复用,优化RTT)
  • 带宽占用减小(头部压缩,编码,HPACK)
  • 连接数减少(多路复用,二进制分帧)

先来看下带宽和延迟对页面加载的影响,数据来源:HTTP/2 is here, let’s optimize! - Velocity SC 2015

  • 在5M以下的带宽内,对页面加载速度影响较大,5M以上的带宽,对页面加载速度影响较小。
  • 延迟的减少对页面加载时间的提升呈线性增长


阅读全文

HTTP/2 in GO(四)

相关阅读:

Start

上篇文章我们了解了如何在HTTP/2 server端进行Header信息的发送,同时保持连接不断开。这次我们在这个基础上,实现自动下发PUSH

先来实现一个最简单的Server Push例子, 我们在上次的demo基础上继续改进

package main

import (
    "html/template"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/header", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Add("X-custom-header", "custom header")
        w.WriteHeader(http.StatusNoContent)

        if f, ok := w.(http.Flusher); ok {
            f.Flush()
        }
        select {}
    })

    // 用于push的 handler
    http.HandleFunc("/crt", func(w http.ResponseWriter, r *http.Request) {
        tpl := template.Must(template.ParseFiles("server.crt"))
        tpl.Execute(w, nil)
    })

    // 请求该Path会触发Push
    http.HandleFunc("/push", func(w http.ResponseWriter, r *http.Request) {
        pusher, ok := w.(http.Pusher)
        if !ok {
            log.Println("not support server push")
        } else {
            err := pusher.Push("/crt", nil)
            if err != nil {
                log.Printf("Failed for server push: %v", err)
            }
        }
        w.WriteHeader(http.StatusOK)
    })

    log.Println("start listen on 8080...")
    log.Fatal(http.ListenAndServeTLS(":8080", "server.crt", "server.key", nil))
}

以上代码添加了两个Hanlder,一个是 /crt,返回我们的证书内容,这个是用来给做客户端push的内容。另一个是 /push,请求该链接时,我们会将 /crt 的内容主动 push 到客户端。

GO服务启动后,我们通过h2c来访问下/push : 先在一个终端通过 h2c start -d 启动进行输出显示,然后另外开一个终端窗口发起请求 h2c connect localhost:8080h2c get /push :


阅读全文

HTTP/2 in GO(三)

相关阅读:

Start

前边两章讲了很多HTTP/2概念性的东西,看起来比较无趣,从这次开始,我们从一些实际用途开始讲起。
本次讲一个非常简单的功能,然后把其内部实现串一下。
这次要实现的功能非常简单,就是一个http2的server,对客户端的请求,只返回一个header信息,并且保持连接,以便在后续任何时候进行一些其他的响应操作。目前看起来这个场景可能没有太大作用,其实HTTP/2做为一个超文本传输协议,目前我们能想到的应用场景还都是普通的web业务,但是老外们的思路就比较广,已经把一些HTTP/2的特性在特定的场景发挥出来了,比如 Amazon的Alexa,Apple的APNS 等。这次实现的这个小功能,就是Alexa里用到的一小部分.
Amazon的avs(Alexa Voice Service)通过HTTP/2实现了全双工的传输功能,其下行功能就用到了这块,Alexa跟avs建立链接后,客户端会发起一个GET /v20160207/directives的请求,服务端接受请求后,返回一个200的头信息,并hold住链接,后续使用该链接通过Server Push功能给客户端主动发送指令。
本次开始,我们先不管Server Push,先从发送Header这个小功能开始吧。

HTTP/2在GO语言的实现中没有支持h2c,所以我们必须使用带证书的加密方式,那么首先需要有一张证书。
我们可以使用openssl自己生成一张:

openssl req -newkey rsa:2048 -nodes -keyout server.key -x509 -days 365 -out server.crt

然后按提示随便输入一些内容就可以得到两个文件,server.keyserver.crt,其实就是相当于私钥和公钥。当然这个证书是不能在互联网上正常流通使用的,因为证书是自己签发的,没有人给你做担保,能确认这个证书跟它所标识的内容提供方是匹配的。所以我们在做请求测试的时候,需要客户端忽略证书校验才可以。

服务端GO示例的代码如下:

package main

import (
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/header", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Add("X-custom-header", "custom header")
        w.WriteHeader(http.StatusNoContent)

        if f, ok := w.(http.Flusher); ok {
            f.Flush()
        }
        select {}
    })

    log.Println("start listen on 8080...")
    log.Fatal(http.ListenAndServeTLS(":8080", "server.crt", "server.key", nil))
}

服务运行起来后我们在一个较新的支持HTTP/2的curl命令下执行:

curl  "https://localhost:8080/header"  -k -i --http2
  • -k 参数表示忽略证书校验,避免客户端拿到证书后校验不通过而拒绝链接
  • -i 参数表示显示返回的header信息
  • --http2 表示启用http/2,这个参数也可以不带,因为客户端支持的话,会优先使用http/2去链接,服务端不支持的时候降级到http/1.1

curl with --http2

这样就实现了只返回了一个header信息,并且链接没有断开。
我们再通过前边介绍过的h2c来看下请求的效果:

这里有图

可以看到返回的只有一个Header信息,并且是没有END_STREAM标记的。

本次的实践内容到这里就可以结束了,最终实现的代码很简单,但是为什么这样可以实现呢,在缺少相关资料的情况下,很难知道这样做是可以实现该目的的,那么接下来就从Go语言中对HTTP/2的实现来一探究竟吧:


阅读全文

在http中设置超时,或在time.Sleep等方法中需要设置一个时间段时,如果使用 整型变量 * time.Second 之类的,就会报以下错误:

t := 3
t * time.Second
// mismatched types int and time.Duration

但是如果使用 整型数字 * time.Second,就没有问题

3 * time.Second
// ok,没问题。

那怎么使用变量进行超时设置呢,解决办法就是使用 time.Duration:

t := 3
t.Duration(t) * time.Second
//ok,没有问题了

为什么会这样呢,我们可以看下time.Duration的定义

type Duration int64

其实就是一个int类型

const (
    Nanosecond  Duration = 1
    Microsecond          = 1000 * Nanosecond
    Millisecond          = 1000 * Microsecond
    Second               = 1000 * Millisecond
    Minute               = 60 * Second
    Hour                 = 60 * Minute
)

time.Second 之类的常量也是 Druation 类型,所以,如果是变量进行相乘的计算时,两个参数必须都是time.Druation类型的;当是一个数字时,默认是可以转成time.Duration来进行计算的。


阅读全文

HTTP/2 in GO(二)

相关阅读:

上一篇文章中介绍了HTTP/2的二进制分帧和多路复用的特性,这次来介绍下头部压缩和服务端推送。

HTTP/2新增特性

  • 二进制分帧(HTTP Frames)
  • 多路复用
  • 头部压缩
  • 服务端推送(Server Push)

头部压缩

在HTTP/1.x中,每次HTTP请求都会携带需要的header信息,这些信息以纯文本形式传递,所以每次的请求和响应,都会浪费一些带宽,如果header信息中包含cookie等之类的信息,那么浪费的带宽就更可观了。为了减少带宽开销和提升性能,HTTP/2 使用 HPACK 压缩格式压缩请求和响应标头元数据,这种格式采用两种简单但是强大的技术:

  • 这种格式支持通过静态Huffman 编码对传输的header字段进行编码,从而减小了传输的大小。
  • 这种格式要求客户端和服务器同时维护和更新一个包含之前见过的header字段的索引列表(换句话说,它可以建立一个共享的压缩上下文),此列表随后会用作参考,对之前传输的值进行有效编码。

利用 Huffman 编码,可以在传输时对各个值进行压缩,而利用之前传输值的索引列表,我们可以通过传输索引值的方式对重复值进行编码,索引值可用于有效查询和重构完整的标头键值对。

客户端和服务端都有一个内置的静态表,部分内容如下:

静态表
+-------+-----------------------------+---------------+
| Index | Header Name                 | Header Value  |
+-------+-----------------------------+---------------+
| 1     | :authority                  |               |
| 2     | :method                     | GET           |
| 3     | :method                     | POST          |
| 4     | :path                       | /             |
| 5     | :path                       | /index.html   |
| 6     | :scheme                     | http          |
| 7     | :scheme                     | https         |
| 8     | :status                     | 200           |
| 9     | :status                     | 204           |
| 10    | :status                     | 206           |
| 11    | :status                     | 304           |
| 12    | :status                     | 400           |
| 13    | :status                     | 404           |
| 14    | :status                     | 500           |
| 15    | accept-charset              |               |
| 16    | accept-encoding             | gzip, deflate |
| 17    | accept-language             |               |
...
| 58    | user-agent                  |               |
| 59    | vary                        |               |
| 60    | via                         |               |
| 61    | www-authenticate            |               |
+-------+-----------------------------+---------------+

可以看到,部分静态表已经包含了value,比如 Index=2:method = GET,当客户端发起请求时,如果发起的是GET请求,那么只需要在Header信息中携带一个Index=2的索引即可,服务端收到通过静态表即可查出对应的请求头信息。 在静态表中传输的Header Block是这种格式的:

     0   1   2   3   4   5   6   7
   +---+---+---+---+---+---+---+---+
   | 1 |        Index (7+)         |
   +---+---------------------------+

从图中可以看到,只需要8-bit即可实现一个method的Header传输:

with index

对于静态表中不存在value的值,或者value的值跟想传递的值不一样时,就不能只传递简单的Index了;比如对于:path的头信息,如果要请求的path不在静态表里,就需要用到 Huffman 编码 了。


阅读全文

作者的图片

DigDeeply

Technology Stack: PHP/Openresty/GoLang, and so on…

Web Develop Eneigneer

Beijing China