概述
开发简单 web 服务程序 cloudgo,了解 web 服务器工作原理。
任务目标
- 熟悉 go 服务器工作原理
- 基于现有 web 库,编写一个简单 web 应用类似 cloudgo。
- 使用 curl 工具访问 web 程序
- 对 web 执行压力测试
任务要求
基本要求
- 编程 web 服务程序 类似 cloudgo 应用。
a. 支持静态文件服务
b. 支持简单 js 访问
c. 提交表单,并输出一个表格(必须使用模板) - 使用 curl 测试,将测试结果写入 README.md
- 使用 ab 测试,将测试结果写入 README.md。并解释重要参数。
拓展要求
选择以下一个或多个任务,以博客的形式提交。
- 通过源码分析、解释一些关键功能实现
- 选择简单的库,如 mux 等,通过源码分析、解释它是如何实现扩展的原理,包括一些 golang 程序设计技巧。
实现
环境:Windows10 Go1.15
参考老师的课程文件中的内容
模仿写出了以下代码:
main.go:用于创建并启动一个服务器,通过run这个代码来开启服务器;
package main
import (
"os"
flag "github.com/spf13/pflag"
"github.com/user/acloudgo/CloudGo/service"
)
const (
PORT string = "8888"
)
func main() {
port := os.Getenv("PORT")
if len(port) == 0 {
port = PORT
}
pPort := flag.StringP("port", "p", PORT, "PORT for httpd listening")
flag.Parse()
if len(*pPort) != 0 {
port = *pPort
}
server := service.NewServer()
server.Run(":" + port)
}
server.go中有多个函数,分别也都是模仿老师课程文件中完成的:
newServer、initRoutes函数:创建并返回了一个服务器;
func NewServer() *negroni.Negroni {
formatter := render.New(render.Options{
Directory: "templates",
Extensions: []string{".html"},
IndentJSON: true,
})
n := negroni.Classic()
mx := mux.NewRouter()
initRoutes(mx, formatter)
n.UseHandler(mx)
return n
}
func initRoutes(mx *mux.Router, formatter *render.Render) {
webRoot := os.Getenv("WEBROOT")
if len(webRoot) == 0 {
if root, err := os.Getwd(); err != nil {
panic("Could not retrive working directory")
} else {
webRoot = root
//fmt.Println(root)
}
}
mx.HandleFunc("/test", testHandler(formatter)).Methods("GET")
mx.HandleFunc("/", homeHandler(formatter)).Methods("GET")
mx.HandleFunc("/user", userHandler).Methods("POST")
mx.PathPrefix("/").Handler(http.FileServer(http.Dir(webRoot + "/assets/")))
}
其中
mx.PathPrefix("/").Handler(http.FileServer(http.Dir(webRoot + "/assets/")))
这条语句是将/后带参数的url定位到webRoot+‘/assets’中,如localhost:8888/img就定位到assets/img文件夹中,从而实现了静态文件的访问。
testHandler函数:用于支持简单的js访问;
func testHandler(formatter *render.Render) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
formatter.JSON(w, http.StatusOK, struct {
S_ID string `json:"s_id"`
Name string `json:"name"`
}{S_ID: "18342007", Name: "zengty"})
}
}
对应的js代码:
$(document).ready(function() {
$.ajax({
url: "/test"
}).then(function(data) {
$('.Content').append(data.S_ID);
});
});
homeHandler、userHandler函数:返回起始页面和用户登录之后的表格页面,这两个页面分别由templates中的page.html、user.html定义,都使用了模板,userHandler实现了提交表单返回表格的功能;
func homeHandler(formatter *render.Render) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
formatter.HTML(w, http.StatusOK, "page", struct {
Content string `json:"content"`
}{Content: "Input your name and S_ID."})
}
}
func userHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
name := template.HTMLEscapeString(r.Form.Get("name"))
s_id := template.HTMLEscapeString(r.Form.Get("s_id"))
t := template.Must(template.New("user.html").ParseFiles("./templates/user.html"))
err := t.Execute(w, struct {
Name string
S_ID string
}{Name: name, S_ID: s_id})
if err != nil {
panic(err)
}
}
可以看到homeHandler中模板参数是Content提示语,userHandler的参数是从page里的form得到的name和s_id,然后利用模板分别在page和html中显示出来,效果在后面统一列举。
page.html:
<html>
<head>
<link rel="stylesheet" href="main.css" />
</head>
<body>
<form class="box" action="./user" method="POST">
<h1 class="Content">{{.Content}}</h1>
<p>Name:<input type="text" name="name"></p>
<p>S_ID:<input type="text" name="s_id"></p>
<p><input type="submit" value="Register"></p>
</form>
</body>
</html>
user.html:
<html>
<head>
<link rel="stylesheet" href="main.css" />
</head>
<body>
<table border="1">
<tr>
<td>Name</td>
<td>{{.Name}}</td>
</tr>
<tr>
<td>S_ID</td>
<td>{{.S_ID}}</td>
</tr>
</table>
</body>
</html>
效果
- 静态文件访问:
assets中文件结构如下:
访问结果:
- js访问:
通过curl工具访问,可以看到返回的是一个JSON格式的数据,因此实现了js访问。
- 提交表单返回表格
开始的页面如下:
然后输入name和s_id提交之后能够跳转到/user界面:
测试
curl测试
在win10中安装curl的教程如下 csdn;
参数的含义可以在网上找到 curl命令详解;
这里调试主要用到了-v参数(输出信息)。
- curl -v http://localhost:8888
测试首页,成功
C:\Users\12586>curl -v http://localhost:8888
* Rebuilt URL to: http://localhost:8888/
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8888 (#0)
> GET / HTTP/1.1
> Host: localhost:8888
> User-Agent: curl/7.55.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/html; charset=UTF-8
< Date: Tue, 24 Nov 2020 11:10:14 GMT
< Content-Length: 368
<
<html>
<head>
<link rel="stylesheet" href="main.css" />
</head>
<body>
<form class="box" action="./user" method="POST">
<h1 class="Content">Input your name and S_ID.</h1>
<p>Name:<input type="text" name="name"></p>
<p>S_ID:<input type="text" name="s_id"></p>
<p><input type="submit" value="Register"></p>
</form>
</body>
</html>* Connection #0 to host localhost left intact
- curl -v http://localhost:8888/img/a.png
测试静态文件,成功
C:\Users\12586>curl -v http://localhost:8888/img/a.png
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8888 (#0)
> GET /img/a.png HTTP/1.1
> Host: localhost:8888
> User-Agent: curl/7.55.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 54600
< Content-Type: image/png
< Last-Modified: Mon, 23 Nov 2020 14:13:22 GMT
< Date: Tue, 24 Nov 2020 11:14:37 GMT
<
Warning: Binary output can mess up your terminal. Use "--output -" to tell
Warning: curl to output it to your terminal anyway, or consider "--output
Warning: <FILE>" to save to a file.
* Failed writing body (0 != 3924)
* Closing connection 0
- curl -v http://localhost:8888/test
测试 返回json,成功
C:\Users\12586>curl -v http://localhost:8888/test
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8888 (#0)
> GET /test HTTP/1.1
> Host: localhost:8888
> User-Agent: curl/7.55.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=UTF-8
< Date: Tue, 24 Nov 2020 11:15:29 GMT
< Content-Length: 45
<
{
"s_id": "18342007",
"name": "zengty"
}
* Connection #0 to host localhost left intact
- curl
测试提交表单后的页面,但是由于没有提交表单,因此失败;
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8888 (#0)
> GET /user HTTP/1.1
> Host: localhost:8888
> User-Agent: curl/7.55.1
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Content-Type: text/plain; charset=utf-8
< X-Content-Type-Options: nosniff
< Date: Tue, 24 Nov 2020 11:18:29 GMT
< Content-Length: 19
<
404 page not found
* Connection #0 to host localhost left intact
ab压力测试
安装教程也能在网上找到:Windows下ab的安装和压测
在根目录下,输入ab -help可以看到各个参数的含义
Usage: ab [options] [http://]hostname[:port]/path
Options are:
-n requests Number of requests to perform
-c concurrency Number of multiple requests to make at a time
-t timelimit Seconds to max. to spend on benchmarking
This implies -n 50000
-s timeout Seconds to max. wait for each response
Default is 30 seconds
-b windowsize Size of TCP send/receive buffer, in bytes
-B address Address to bind to when making outgoing connections
-p postfile File containing data to POST. Remember also to set -T
-u putfile File containing data to PUT. Remember also to set -T
-T content-type Content-type header to use for POST/PUT data, eg.
'application/x-www-form-urlencoded'
Default is 'text/plain'
-v verbosity How much troubleshooting info to print
-w Print out results in HTML tables
-i Use HEAD instead of GET
-x attributes String to insert as table attributes
-y attributes String to insert as tr attributes
-z attributes String to insert as td or th attributes
-C attribute Add cookie, eg. 'Apache=1234'. (repeatable)
-H attribute Add Arbitrary header line, eg. 'Accept-Encoding: gzip'
Inserted after all normal header lines. (repeatable)
-A attribute Add Basic WWW Authentication, the attributes
are a colon separated username and password.
-P attribute Add Basic Proxy Authentication, the attributes
are a colon separated username and password.
-X proxy:port Proxyserver and port number to use
-V Print version number and exit
-k Use HTTP KeepAlive feature
-d Do not show percentiles served table.
-S Do not show confidence estimators and warnings.
-q Do not show progress when doing more than 150 requests
-l Accept variable document length (use this for dynamic pages)
-g filename Output collected data to gnuplot format file.
-e filename Output CSV file with percentages served
-r Don't exit on socket receive errors.
-m method Method name
-h Display usage information (this message)
大致含义是:
-n 测试中请求总数
-c 一次产生的请求个数(并发数)
-t 测试持续的最长时间
-s 每次请求的最长等待时间
-b TCP连接中buffer的长度
-B 进行连接使用的地址
-p 需要post的文件
-u 需要put的文件
-T post/put数据的类型头文件
-v 显示信息的详细程度
-w 输出结果到html表中
-i 使用head替代get
……(后面的用的比较少,而且这次也没有用到就不继续往下列了)
这次测试中,主要使用的参数是-n、-c和-p。
- ab -n8888 -c10 http://localhost:8888/
结果:
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
Completed 888 requests
Completed 1776 requests
Completed 2664 requests
Completed 3552 requests
Completed 4440 requests
Completed 5328 requests
Completed 6216 requests
Completed 7104 requests
Completed 7992 requests
Completed 8880 requests
Finished 8888 requests
Server Software:
Server Hostname: localhost
Server Port: 8888
Document Path: /
Document Length: 368 bytes
Concurrency Level: 10
Time taken for tests: 2.528 seconds
Complete requests: 8888
Failed requests: 0
Total transferred: 4310680 bytes
HTML transferred: 3270784 bytes
Requests per second: 3516.03 [#/sec] (mean)
Time per request: 2.844 [ms] (mean)
Time per request: 0.284 [ms] (mean, across all concurrent requests)
Transfer rate: 1665.31 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.4 0 1
Processing: 1 3 0.6 3 5
Waiting: 0 2 0.7 2 5
Total: 1 3 0.5 3 5
Percentage of the requests served within a certain time (ms)
50% 3
66% 3
75% 3
80% 3
90% 3
95% 4
98% 4
99% 4
100% 5 (longest request)
- ab -n8888 -c10 http://localhost:8888/css/a1.css/
结果:
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
Completed 888 requests
Completed 1776 requests
Completed 2664 requests
Completed 3552 requests
Completed 4440 requests
Completed 5328 requests
Completed 6216 requests
Completed 7104 requests
Completed 7992 requests
Completed 8880 requests
Finished 8888 requests
Server Software:
Server Hostname: localhost
Server Port: 8888
Document Path: /css/a1.css/
Document Length: 0 bytes
Concurrency Level: 10
Time taken for tests: 2.596 seconds
Complete requests: 8888
Failed requests: 0
Non-2xx responses: 8888
Total transferred: 986568 bytes
HTML transferred: 0 bytes
Requests per second: 3423.72 [#/sec] (mean)
Time per request: 2.921 [ms] (mean)
Time per request: 0.292 [ms] (mean, across all concurrent requests)
Transfer rate: 371.13 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.4 0 1
Processing: 1 3 0.5 3 5
Waiting: 0 2 0.7 2 5
Total: 1 3 0.5 3 5
Percentage of the requests served within a certain time (ms)
50% 3
66% 3
75% 3
80% 3
90% 3
95% 4
98% 4
99% 4
100% 5 (longest request)
- ab -n8888 -c10 -p test.txt http://localhost:8888/
其中test.txt存放在根目录下,内容:
实现了对post的压力测试,结果:
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
Completed 888 requests
Completed 1776 requests
Completed 2664 requests
Completed 3552 requests
Completed 4440 requests
Completed 5328 requests
Completed 6216 requests
Completed 7104 requests
Completed 7992 requests
Completed 8880 requests
Finished 8888 requests
Server Software:
Server Hostname: localhost
Server Port: 8888
Document Path: /
Document Length: 83 bytes
Concurrency Level: 10
Time taken for tests: 2.648 seconds
Complete requests: 8888
Failed requests: 0
Total transferred: 2177560 bytes
Total body sent: 1297648
HTML transferred: 737704 bytes
Requests per second: 3356.58 [#/sec] (mean)
Time per request: 2.979 [ms] (mean)
Time per request: 0.298 [ms] (mean, across all concurrent requests)
Transfer rate: 803.09 [Kbytes/sec] received
478.58 kb/s sent
1281.66 kb/s total
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.4 0 2
Processing: 1 3 0.6 3 10
Waiting: 0 2 0.8 2 10
Total: 1 3 0.6 3 10
Percentage of the requests served within a certain time (ms)
50% 3
66% 3
75% 3
80% 3
90% 3
95% 4
98% 4
99% 5
100% 10 (longest request)
拓展要求
服务计算作业七——mux包源码分析,从与DefaultServeMux对比入手