概述
开发简单 web 服务程序 cloudgo,了解 web 服务器工作原理。
任务目标
- 熟悉 go 服务器工作原理
- 基于现有 web 库,编写一个简单 web 应用类似 cloudgo。
- 使用 curl 工具访问 web 程序
- 对 web 执行压力测试
相关知识
可参考课件
任务要求
基本要求
- 编程 web 服务程序 类似 Cloudgo 应用
-
框架选择
本次使用的web开发框架是Martini框架,Martini 是Go 语言的 Web 框架,使用 Go 的 net/http 接口开发,类似 Sinatra 或者 Flask 之类的框架,也可使用自己的 DB 层、会话管理和模板。具有如下特性:
- 无侵入设计
- 与其他 Go 的包配合工作
- 模块化设计,可轻松添加工具
- …
-
框架搭建
我们使用go get
命令安装所需软件包:
-
开发Cloudgo应用
在运行程序之前,可以参照官网github的使用方式,一个简单的例子如下所示:
package main
import "github.com/go-martini/martini"
func main() {
m := martini.Classic()
m.Get("/", func() string {
return "Hello world!"
})
m.Run()
}
这样就可以在localhost:3000上运行。
程序框架
我们的代码结构如下所示:
├── main.go
├── server
│ └── server.go
├── static
│ ├── image
│ │ ├── background.jpg
│ │ ├── icon.png
│ │ └── panda.png
│ ├── jigsaw.css
│ ├── jigsaw.js
│ └── js
│ └── jquery-3.3.1.min.js
└── templates
└── jigsaw.tmpl
其中:
- main.go为是主程序入口
- server/server.go使用框架建立一个服务器
- static为加载文件使用到的静态资源,包括js和css以及用到的图片资源
- templates内含render渲染的HTML文件
代码分析
- main.go
main.go主要作用是创建端口,调用server.go的Run
方法启动服务器
为了从用户输入获得指定的端口,我们使用了pflag包。其中,第一个参数是参数名,第二个参数简写,第三个参数参数的默认值,第四个参数此参数的解释
currentPort := pflag.StringP("port", "p", "1234", "Port for http listening")
pflag.Parse()
完整代码如下:
package main
import (
"os"
"./server"
"github.com/spf13/pflag"
)
func main() {
port := os.Getenv("PORT")
// set default port
if (len(port) == 0) {
port = "1234"
}
// parse port
currentPort := pflag.StringP("port", "p", "1234", "Port for http listening")
pflag.Parse()
if (len(*currentPort) != 0) {
port = *currentPort
}
// run server
server.Run(port)
}
- server.go
server.go的作用主要是调用Matrini框架启动了web服务器,这里我选择了一个之前web课程的作业代码,看能否渲染出效果:
这里还需要补充安装渲染HTML的工具:
go get github.com/unrolled/render
我们使用Get请求来渲染HTML:
m.Get("/", func(res http.ResponseWriter, req *http.Request) {
r := render.New()
r.HTML(res, http.StatusOK, "jigsaw", "World");
})
我们这里只用到了Get请求,没有用到Post请求,完整代码如下:
package server
import(
"net/http"
"github.com/go-martini/martini"
"github.com/unrolled/render"
)
func Run(port string) {
// default setting
m := martini.Classic()
// set path
m.Use(martini.Static("static"));
// Get request
m.Get("/", func(res http.ResponseWriter, req *http.Request) {
r := render.New()
r.HTML(res, http.StatusOK, "jigsaw", "World");
})
// run
m.RunOnAddr(":" + port)
}
效果展示
打开localhost:1234,我们可以看到HTML如下所示:
服务器的输出如下所示:
测试
使用 curl 工具访问 web 程序
使用curl -v命令来显示客户端与服务器交互的详细信息,其中Content显示的就是当前显示页面的HTML代码。
使用 ab测试
我们首选需要安装相关工具apache benchmark:
sudo yum -y install httpd-tools
ab测试的参数说明如下:
- -n 执行的请求数量
- -c 并发请求个数
- -t 测试所进行的最大秒数
- -p 包含了需要POST的数据的文件
- -T POST数据所使用的Content-type头信息
- -k 启用HTTP KeepAlive功能,即在一个HTTP会话中执行多个请求,默认时,不启用KeepAlive功能
- …
返回值包括: - Concurrency Level:并发数
- Time taken for tests:完成所有请求总共花费的时间
- Complete requests:成功请求的次数
- Failed requests:失败请求的次数
- Total transferred:总共传输的字节数
- HTML transferred:实际页面传输的字节数
- Requests per second:每秒请求数
- Time per request: [ms] (mean): 平均每个用户等待的时间
- Time per request: [ms] (mean, across all concurrent requests) :服务器处理的平均时间
- Transfer rate:传输速率
我们使用如下命令进行测试:
ab -n 10000 -c 1000 http://localhost:1234/
其表示执行10000个请求,1000个并发,结果如下:
总共花费了9s左右的时间来进行处理。