delveshal's blog


  • Home

  • Tags

  • Categories

  • Archives

zero copy transfer

Posted on 2018-08-31 | In go

zero-copy transfer

平时写 http server 会接触到传输文件的问题。最简单的版本大概是这样子的。

1
2
3
4
5
6
7
8
for {
n, _ := file.Read(buf)
if n > 0 {
w.Write(buf[:n])
}else{
break
}
}

对于一般的程序来说是没有问题的,但是如果需要频繁地传输大文件,那么这种办法效率不够高。因为这里不断地调用两个系统调用,read() 和 write()。对操作系统稍微熟悉的都会知道系统调用是在内核态执行而用户程序是跑在用户态。

读取文件就会先从磁盘文件读到内核缓冲区然后再拷贝到用户空间缓冲区,写操作也要先写到内核缓冲区然后再发送。
read+write

系统调用 sendfile() 就是用来解决这个底性能问题的。文件可以直接送到socket上,不需要经过用户空间。
sendfile()

Read more »

runc 笔记

Posted on 2018-06-12 | In docker

main 函数

isRootless 判读是否不能以root权限启动
环境变量:XDG_RUNTIME_DIR :针对每个用户的运行时目录。 通常针对每个用户单独挂载一个 “tmpfs” 文件系统实例。
os.ModeSticky ,filemode,特殊标记位,文件只允许创建者和root移除
因为默认记录runc运行状态是在/run/runc目录,如果runc不能以root权限启动则不够权限,需要用到XDG_RUNTIME_DIR。

Create

  1. checkArgs 检查参数个数
  2. revisePidFile 修改pid-file的路径为绝对路径
  3. setupSpec 读入配置文件,例如利用 runc spec 自动生成的 config.json
  4. startContainer 启动容器
    1. newNotifySocket 需要用到环境变量NOTIFY_SOCKET,一般为空,暂时先不分析。
    2. createContainer 创建容器对象需要传入cli上下文,容器id,从配置文件生成的spec
      1. 调用 specconv 包的 CreateLibcontainerConfig ,specconv 的作用是 “Package specconv implements conversion of specifications to libcontainer”
        1. 设置容器根文件系统目录
        2. 设置标签(labels,键值对)
        3. 根据create options(从 cli 中获取或自动生成的) 创建 configs.Config 对象
        4. 根据配置文件给Config对象配置mount(挂载点)
        5. createDevices 配置容器环境的白名单设备(例如:/dev/null, /dev/random)和用户指定设备
        6. createCgroupConfig 创建cgroup配置。note: “In rootless containers, any attempt to make cgroup changes will fail.” 依次处理devices,memory,cpu,pid,blockio,hugepagelimit,network。
        7. 配置 namespace,
          • NEWNET,如果newnet 并且没有配置路径,默认加入loopback(本地回环)
          • NEWUSER,配置user id 和 group id 的宿主主机和容器内部的映射。
        8. 处理配置文件的 process 字段。
        9. createHooks 配置钩子,Prestart,Poststart,Poststop

未完待续。。。

docker之路:overlay2

Posted on 2018-06-11 | In docker

docker之路:overlay2

If your Linux kernel is version 4.0 or higher, and you use Docker CE, consider using the newer overlay2, which has potential performance advantages over the aufs storage driver.

docker 官方建议如果可以的话建议使用overlay2.

overlay 架构图

overlay.png

overlay 有三层,merge,upperdir,lowerdir。upperdir 是可读写层,对应docker的容器层。lowerdir 是只读层,对应docker的镜像层。merged 是前面两层的统一视图,upperdir 的文件可以屏蔽lowerdir相同的文件。

Read more »

实现写文件的原子性

Posted on 2018-06-04 | In file

实现写文件的原子性

最近在阅读docker源码时发现保存container configuration的时候用到atomicFileWriter把写文件变成原子操作,源码。

实现步骤

  1. 使用ioutil.TempFile新建一个临时文件
  2. 跟平时一样使用Write写入文件
  3. 需要 close 文件时先调用sync函数保证数据落盘,然后才真正 close 文件,修改权限等,最后利用系统调用os.Rename

为什么可以这样做

Read more »

docker之路:docker 开发环境配置

Posted on 2018-06-04 | In docker

docker之路:docker 开发环境配置

可以直接通过官方教程进行搭建,但是如果没有翻墙是很难搭建成功的。
这里我采用另外一种办法,使用dockercore/docker,这个镜像包含了所需要用到的依赖。镜像大概2G。

git clone

路径最好是$GOPATH/src/github.com/docker,$GOPATH可以是项目独有的。

1
2
3
git clone git@github.com:docker/docker.git
# 由于dockercore/docker的版本的17.03.2-ce(目前最新release版本),所有我们要checkout一下
git checkout 17.03.2-ce

docker pull

1
2
3
4
docker pull dockercore/docker
docker run --rm -i --privileged -e BUILDFLAGS -e KEEPBUNDLE -e DOCKER_BUILD_GOGC -e DOCKER_BUILD_PKGS -e DOCKER_CLIENTONLY -e DOCKER_DEBUG -e DOCKER_EXPERIMENTAL -e DOCKER_GITCOMMIT -e DOCKER_GRAPHDRIVER=devicemapper -e DOCKER_INCREMENTAL_BINARY -e DOCKER_REMAP_ROOT -e DOCKER_STORAGE_OPTS -e DOCKER_USERLANDPROXY -e TESTDIRS -e TESTFLAGS -e TIMEOUT -v "/home/delveshal/GoglandProjects/src/github.com/docker/docker:/go/src/github.com/docker/docker" -t "dockercore/docker:latest" bash
# 编译二进制文件,生成目录是bundles
./hack/make.sh binary

现在就可以编辑源码然后使用上面的 make.sh binary 就可以进行编译

golang 实现文件断点续传 demo

Posted on 2018-05-17

golang 实现文件断点续传 demo

实现http断点续传其中一种办法是使用范围请求。

涉及头部:

request

  • Range 表示获取数据范围。例如:Range: bytes=0-1023表示获取下标为0到1023的字节,一共1024个。

response

  • Content-Range 表示返回数据的范围。例如Content-Range: bytes 0-1023/146515表示当前http报文的body数据是0-1023,文件一共有146515个字节。

  • Accept-Ranges 表示接受range的单位,目前只定义了bytes。服务器返回这个头部则表明接收范围请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package main

import (
"flag"
"fmt"
"github.com/gorilla/mux"
"io"
"log"
"net/http"
"os"
"strconv"
"strings"
)

func main() {
path := ""
port := ""
flag.StringVar(&path,"dir", "", "the dir to server")
flag.StringVar(&port,"port", "", "server port")
flag.Parse()
port = ":" + port
if strings.LastIndexByte(path,'/') != len(path) - 1{
path = path + "/"
}
r := mux.NewRouter()
r.HandleFunc("/{file}", func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
f := vars["file"]
var start, end int64
fmt.Sscanf(r.Header.Get("Range"), "bytes=%d-%d", &start, &end)
file, err := os.Open(path + f)
if err != nil {
log.Println(err.Error())
http.NotFound(w, r)
return
}
info, err := file.Stat()
if err != nil {
log.Println(err.Error())
http.NotFound(w, r)
return
}
if start < 0 ||start >= info.Size() ||end < 0 || end >= info.Size(){
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(fmt.Sprintf("out of index, length:%d",info.Size())))
return
}
if end == 0 {
end = info.Size() - 1
}
w.Header().Add("Accept-ranges", "bytes")
w.Header().Add("Content-Length", strconv.FormatInt(end-start+1, 10))
w.Header().Add("Content-Range", "bytes "+strconv.FormatInt(start, 10)+"-"+strconv.FormatInt(end, 10)+"/"+strconv.FormatInt(info.Size()-start, 10))
w.Header().Add("Content-Disposition", "attachment; filename="+info.Name())
_, err = file.Seek(start, 0)
if err != nil {
log.Println(err.Error())
w.WriteHeader(http.StatusInternalServerError)
return
}
_, err = io.CopyN(w, file, end-start+1)
if err != nil {
log.Println(err.Error())
return
}
})
http.ListenAndServe(port, r)
}

待改进

如果在断点后再继续传输并且对象发生变化,那么会出现错误,所以要加上 Last-Modified 或者 ETag 来判断对象是否已经改变。

leetcode 题解(一)

Posted on 2018-05-14 | In leetcode

leetcode 31. Next Permutation

解题思路

题目的意思的找到当前全排列的下一个排列序列。
例如 1,2,3 的全排列
1,2,3
1,3,2
2,1,3
2,3,1
3,1,2
3,2,1

那么 1,2,3 下一个就是 1,3,2。
规定: 最后一个的下一个是第一个。 3,2,1 -> 1,2,3

首先,很明显可以发现:如果一个序列是非递增,那么这个序列是不能再重排,获得一个更大的数。
如果把非递增数列看成序列的子结构,那么这一部分将不可以通过重排获得更大的数,只能加入一位数参与重排。重排的方法是从后往前找到第一个前面一个小于后面一个,即a[i] > a[i-1],a[k] <= a[k-1],k > i。 那么把a[i] 与右边的序列重排,如果要使得序列变大同时又尽可能小,那么只能选比a[i]刚好大的a[j],两个交换,这时候还不是最小的,所以要把非递增序列从小到大排列,即把非递增序列反转.
例如:
1,2,4,3 ,那么 4,3 是一个部分非递增序列,只能加入2参与重排。3 刚好比 2 大,两个交换变成1,3,4,2,然后反转成1,3,2,4.

Read more »

实现一个简单的http-proxy demo

Posted on 2018-05-06 | In http

http-proxy的两种形式

  • 第一种是 RFC 7230 - HTTP/1.1: Message Syntax and Routing(即修订后的 RFC 2616,HTTP/1.1 协议的第一部分)描述的普通代理。这种代理扮演的是「中间人」角色,对于连接到它的客户端来说,它是服务端;对于要连接的服务端来说,它是客户端。它就负责在两端之间来回传送 HTTP 报文。
    image.png

    Read more »

今日头条笔试题-或与加

Posted on 2018-04-25

问题描述

给定 x, k ,求满足 x + y = x | y 的第 k 小的正整数 y 。 | 是二进制的或(or)运算,例如 3 | 5 = 7。
比如当 x=5,k=1时返回 2,因为5+1=6 不等于 5|1=5,而 5+2=7 等于 5 | 2 = 7。

输入描述

每组测试用例仅包含一组数据,每组数据为两个正整数 x , k。 满足 0 < x , k ≤ 2,000,000,000。

输出描述

输出一个数y。

示例 1

输入

1
5 1

输出

1
2

解题思路

如果要使 x + y = x | y 成立,那么x的二进制码是1的地方,y只能是0。
如果要找到第k小的y,那么满足$y \ge k$,因为如果1..k都满足要求,那么第k小就是k。
现在问题已经转化为固定位为0,填充其它位使其满足第k小。
这时候就要把k绕开固定0位,按二进制插入到新值中。

Read more »

今日头条笔试题--木棒拼图

Posted on 2018-04-24

问题描述

有一个由很多木棒构成的集合,每个木棒有对应的长度,请问能否用集合中的这些木棒以某个顺序首尾相连构成一个面积大于 0 的简单多边形且所有木棒都要用上,简单多边形即不会自交的多边形。

初始集合是空的,有两种操作,要么给集合添加一个长度为 L 的木棒,要么删去集合中已经有的某个木棒。每次操作结束后你都需要告知是否能用集合中的这些木棒构成一个简单多边形。

输入描述

每组测试用例仅包含一组数据,每组数据第一行为一个正整数 n 表示操作的数量(1 ≤ n ≤ 50000) , 接下来有n行,每行第一个整数为操作类型 i (i ∈ {1,2}),第二个整数为一个长度 L(1 ≤ L ≤ 1,000,000,000)。如果 i=1 代表在集合内插入一个长度为 L 的木棒,如果 i=2 代表删去在集合内的一根长度为 L 的木棒。输入数据保证删除时集合中必定存在长度为 L 的木棒,且任意操作后集合都是非空的。

Read more »
12

delveshal

18 posts
8 categories
22 tags
© 2018 delveshal
Powered by Hexo
|
Theme — NexT.Muse v6.0.6