扫码订阅《 》或入驻星球,即可阅读文章!

GOLANG ROADMAP

阅读模式

  • 沉浸
  • 自动
  • 日常
首页
Go友会
  • 城市
  • 校园
Go学院
  • Go小课
  • Go小考
  • Go实战
  • 精品课
Go求职
  • 求职辅导🔥
  • Offer收割社群
  • 企业题库
  • 面试宝典
Go宝典
  • 在线宝典
  • B站精选
  • 推荐图书
  • 每日博文
Go仓库
实验区
  • Go周边
  • Go下载
  • Go月刊
消息
更多
  • 用户中心

    • 我的信息
    • 推广返利
  • 玩转星球

    • 星球介绍
    • 角色体系
    • 星主权益
  • 支持与服务

    • 联系星主
    • 成长记录
    • 常见问题
    • 吐槽专区
  • 合作交流

    • 渠道合作
    • 课程入驻
    • 友情链接
author-avatar

GOLANG ROADMAP


首页
Go友会
  • 城市
  • 校园
Go学院
  • Go小课
  • Go小考
  • Go实战
  • 精品课
Go求职
  • 求职辅导🔥
  • Offer收割社群
  • 企业题库
  • 面试宝典
Go宝典
  • 在线宝典
  • B站精选
  • 推荐图书
  • 每日博文
Go仓库
实验区
  • Go周边
  • Go下载
  • Go月刊
消息
更多
  • 用户中心

    • 我的信息
    • 推广返利
  • 玩转星球

    • 星球介绍
    • 角色体系
    • 星主权益
  • 支持与服务

    • 联系星主
    • 成长记录
    • 常见问题
    • 吐槽专区
  • 合作交流

    • 渠道合作
    • 课程入驻
    • 友情链接
  • Go语言Web编程

    • 课程介绍
  • Web基础

  • 表单

  • 访问数据库

  • session和数据存储

  • 文本处理

  • Web服务

  • 安全与加密

  • 国际化和本地化

  • 错误处理,调试和测试

  • 部署与维护

    • 第1节:部署与维护
    • 第2节:应用日志
    • 第3节:网站错误处理
    • 第4节:应用部署
    • 第5节:备份和恢复
    • 第6节:小结
  • 如何设计一个Web框架

  • 扩展Web框架

扫码订阅《 》或入驻星球,即可阅读文章!

第4节:应用部署


ASTA谢

程序开发完毕之后,我们现在要部署 Web 应用程序了,但是我们如何来部署这些应用程序呢?因为 Go 程序编译之后是一个可执行文件,编写过 C 程序的读者一定知道采用 daemon 就可以完美的实现程序后台持续运行,但是目前 Go 还无法完美的实现 daemon,因此,针对 Go 的应用程序部署,我们可以利用第三方工具来管理,第三方的工具有很多,例如 Supervisord、upstart、daemontools等,这小节我介绍目前自己系统中采用的工具 Supervisord。

# daemon

目前 Go 程序还不能实现 daemon,详细的见这个 Go 语言的 bug:<http://code.google.com/p/go/issues/detail?id=227>,大概的意思说很难从现有的使用的线程中 fork 一个出来,因为没有一种简单的方法来确保所有已经使用的线程的状态一致性问题。

但是我们可以看到很多网上的一些实现 daemon 的方法,例如下面两种方式:

  • MarGo 的一个实现思路,使用 Command 来执行自身的应用,如果真想实现,那么推荐这种方案
d := flag.Bool("d", false, "Whether or not to launch in the background(like a daemon)")
if *d {
	cmd := exec.Command(os.Args[0],
		"-close-fds",
		"-addr", *addr,
		"-call", *call,
	)
	serr, err := cmd.StderrPipe()
	if err != nil {
		log.Fatalln(err)
	}
	err = cmd.Start()
	if err != nil {
		log.Fatalln(err)
	}
	s, err := ioutil.ReadAll(serr)
	s = bytes.TrimSpace(s)
	if bytes.HasPrefix(s, []byte("addr: ")) {
		fmt.Println(string(s))
		cmd.Process.Release()
	} else {
		log.Printf("unexpected response from MarGo: `%s` error: `%v`\n", s, err)
		cmd.Process.Kill()
	}
}
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
  • 另一种是利用 syscall 的方案,但是这个方案并不完善:
package main
 
import (
	"log"
	"os"
	"syscall"
)

func daemon(nochdir, noclose int) int {
	var ret, ret2 uintptr
	var err uintptr
	darwin := syscall.OS == "darwin"
 
	// already a daemon
	if syscall.Getppid() == 1 {
		return 0
	}
 
	// fork off the parent process
	ret, ret2, err = syscall.RawSyscall(syscall.SYS_FORK, 0, 0, 0)
	if err != 0 {
		return -1
	}
 
	// failure
	if ret2 < 0 {
		os.Exit(-1)
	}
 
	// handle exception for darwin
	if darwin && ret2 == 1 {
		ret = 0
	}
 
	// if we got a good PID, then we call exit the parent process.
	if ret > 0 {
		os.Exit(0)
	}
 
	/* Change the file mode mask */
	_ = syscall.Umask(0)
 
	// create a new SID for the child process
	s_ret, s_errno := syscall.Setsid()
	if s_errno != 0 {
		log.Printf("Error: syscall.Setsid errno: %d", s_errno)
	}
	if s_ret < 0 {
		return -1
	}
 
	if nochdir == 0 {
		os.Chdir("/")
	}
 
	if noclose == 0 {
		f, e := os.OpenFile("/dev/null", os.O_RDWR, 0)
		if e == nil {
			fd := f.Fd()
			syscall.Dup2(fd, os.Stdin.Fd())
			syscall.Dup2(fd, os.Stdout.Fd())
			syscall.Dup2(fd, os.Stderr.Fd())
		}
	}
 
	return 0
}	
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

上面提出了两种实现 Go 的 daemon 方案,但是我还是不推荐大家这样去实现,因为官方还没有正式的宣布支持 daemon,当然第一种方案目前来看是比较可行的,而且目前开源库 skynet 也在采用这个方案做 daemon。

# Supervisord

上面已经介绍了 Go 目前是有两种方案来实现他的 daemon,但是官方本身还不支持这一块,所以还是建议大家采用第三方成熟工具来管理我们的应用程序,这里我给大家介绍一款目前使用比较广泛的进程管理软件:Supervisord。Supervisord 是用 Python 实现的一款非常实用的进程管理工具。supervisord 会帮你把管理的应用程序转成 daemon 程序,而且可以方便的通过命令开启、关闭、重启等操作,而且它管理的进程一旦崩溃会自动重启,这样就可以保证程序执行中断后的情况下有自我修复的功能。

我前面在应用中踩过一个坑,就是因为所有的应用程序都是由 Supervisord 父进程生出来的,那么当你修改了操作系统的文件描述符之后,别忘记重启Supervisord,光重启下面的应用程序没用。当初我就是系统安装好之后就先装了 Supervisord,然后开始部署程序,修改文件描述符,重启程序,以为文件描述符已经是 100000了,其实 Supervisord 这个时候还是默认的 1024 个,导致他管理的进程所有的描述符也是 1024. 开放之后压力一上来系统就开始报文件描述符用光了,查了很久才找到这个坑。

# Supervisord 安装

Supervisord 可以通过 sudo easy_install supervisor 安装,当然也可以通过 Supervisord 官网下载后解压并转到源码所在的文件夹下执行 setup.py install 来安装。

  • 使用 easy_install 必须安装 setuptools

    打开 http://pypi.python.org/pypi/setuptools#files,根据你系统的 python 的版本下载相应的文件,然后执行 sh setuptoolsxxxx.egg,这样就可以使用 easy_install 命令来安装 Supervisord。

# Supervisord 配置

Supervisord 默认的配置文件路径为 /etc/supervisord.conf,通过文本编辑器修改这个文件,下面是一个示例的配置文件:

;/etc/supervisord.conf
[unix_http_server]
file = /var/run/supervisord.sock
chmod = 0777
chown= root:root

[inet_http_server]
# Web管理界面设定
port=9001
username = admin
password = yourpassword

[supervisorctl]
; 必须和'unix_http_server'里面的设定匹配
serverurl = unix:///var/run/supervisord.sock

[supervisord]
logfile=/var/log/supervisord/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB       ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10          ; (num of main logfile rotation backups;default 10)
loglevel=info               ; (log level;default info; others: debug,warn,trace)
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=true              ; (start in foreground if true;default false)
minfds=1024                 ; (min. avail startup file descriptors;default 1024)
minprocs=200                ; (min. avail process descriptors;default 200)
user=root                 ; (default is current user, required if root)
childlogdir=/var/log/supervisord/            ; ('AUTO' child log dir, default $TEMP)

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

; 管理的单个进程的配置,可以添加多个 program
[program:blogdemon]
command=/data/blog/blogdemon
autostart = true
startsecs = 5
user = root
redirect_stderr = true
stdout_logfile = /var/log/supervisord/blogdemon.log

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

# Supervisord 管理

Supervisord 安装完成后有两个可用的命令行 supervisor 和 supervisorctl,命令使用解释如下:

  • supervisord,初始启动 Supervisord,启动、管理配置中设置的进程。
  • supervisorctl stop programxxx,停止某一个进程 (programxxx),programxxx 为 [program:blogdemon] 里配置的值,这个示例就是blogdemon。
  • supervisorctl start programxxx,启动某个进程
  • supervisorctl restart programxxx,重启某个进程
  • supervisorctl stop all,停止全部进程,注:start、restart、stop 都不会载入最新的配置文件。
  • supervisorctl reload,载入最新的配置文件,并按新的配置启动、管理所有进程。

# 小结

这小节我们介绍了 Go 如何实现 daemon 化,但是由于目前 Go 的 daemon 实现的不足,需要依靠第三方工具来实现应用程序的 daemon 管理的方式,所以在这里介绍了一个用 python 写的进程管理工具 Supervisord,通过 Supervisord 可以很方便的把我们的 Go 应用程序管理起来。

  • daemon
  • Supervisord
  • Supervisord 安装
  • Supervisord 配置
  • Supervisord 管理
  • 小结