开发 web 服务程序——cloudgo
一、功能介绍
- 编程 web 服务程序 类似 cloudgo 应用
- 支持静态文件服务
- 简单js访问
- 提交表单,并输出一个表格(必须使用模板)
- 支持文件上传(拓展
- 实现文件下载(拓展
二、Web框架选择
本次作业中选用beego来搭建web服务,因为beego有比较好的中文文档(便于理解),而且自带bee工具比较实用
2.1 安装beego:
go get github.com/astaxie/beego
2.2 安装bee工具:
go get github.com/beego/bee
- 安装完成后输入
bee version
来检测是否安装成功,正确反馈如下:
- bee工具非常实用,我们只需要在
$GOPATH/src
目录下使用bee new <项目名>
即可新建项目: - 进入对应项目文件目录后,使用
bee run
命令即可运行程序 - 之后便可在beego框架上进行程序实现
2.3 Beego/Bee工具安装排坑
- 解决安装Beego/Bee过程中可能遇到一些问题,版本不匹配等,详情见我的另一篇博客
- 连接Beego/Bee工具安装排坑
三、程序实现:
- 目录结构:
- conf:存储配置信息:端口号,name等
- controllers:存储控制器设置,在路由设置中调用对应的控制器
- routers:路由设置,设置对应的url调用的控制器等
- static:静态文件目录,存储文件的静态文件访问和本次实现的文件上传下载的文件存储
- test:bee新建项目后,自带的测试程序(不用管它
- views:存放模板文件
- main:实现服务的运行等设置
.
├── cloudgo
├── conf
│ └── app.conf
├── controllers
│ ├── default.go
│ ├── file.go
│ ├── loadfile.go
│ ├── login.go
│ └── user.go
├── go.mod
├── go.sum
├── lastupdate.tmp
├── main.go
├── models
├── routers
│ └── router.go
├── static
│ ├── css
│ ├── img
│ │ ├── img0.jpg
│ │ ├── img1.jpeg
│ │ └── img2.jpeg
│ └── js
│ └── reload.min.js
├── tests
│ └── default_test.go
└── views
├── hello.js
├── index.html
├── index.tpl
├── login.tpl
├── table.html
└── upload.tpl
10 directories, 23 files
3.1 实现简单的js访问
实现js数据的Get请求:并通过该请求来进行网页的更新(实现原理仿照课件案例),同时我们简单试用了一下json的tag功能,写自闭了,随手加的
- controller:user.go
- Get:首先我们自定义一个结构体用于存储数据,之后当收到请求后,返回一个结构体转换的json数据
c.Data["json"] = rejs
,这里还可以进行是否是ajax的判断,但是作业要求没有就emmm当作一个debug用途把
- Get:首先我们自定义一个结构体用于存储数据,之后当收到请求后,返回一个结构体转换的json数据
package controllers
import (
"fmt"
beego "github.com/astaxie/beego/server/web"
)
type UserController struct {
beego.Controller
}
type JSONStruct struct {
ID string `json:"id"`
Content string `json:"content"`
}
func (c *UserController) Get() {
fmt.Println("是否为ajax请求", c.IsAjax())
var rejs JSONStruct
rejs.Content = "hello"
rejs.ID = "111"
c.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Origin", "*") //允许跨域
c.Data["json"] = rejs
c.ServeJSON()
return
}
- 路由配置:router.go
- 为json访问配置对应的url路径
beego.Router("/user", &controllers.UserController{})
- 查验是否正确返回json数据:(这里使用接口测试工具,之后下面有详细的curl测试)
之后为了实现要求的通过返回的json数据来进行网页数据的更新,我们要在另一个界面上搭建网页,并向该接口发送get请求
- 我们这里使用初始页面来进行,首先更改控制器controller:
- 控制器中只要设定tplname为我们的html界面即可(这里的网页界面由一个div和两个input、一个button组成)
package controllers
import (
beego "github.com/astaxie/beego/server/web"
)
type MainController struct {
beego.Controller
}
func (c *MainController) Get() {
c.Data["Website"] = "beego.me"
c.Data["Email"] = "astaxie@gmail.com"
c.TplName = "index.html"
}
- 网页文件为:
- 在ajax中对对应的url及逆行发送请求,并在success的回调函数中,通过get到的数据对页面进行更新,即可实现要求功能,在这里实现我们js访问功能的查验
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>json_visit</title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js">
</script>
<script>
$(document).ready(function(){
$("button").click(function(){
$.ajax({url:"http://localhost:8080/user",success:function(result){
$("#div1").html(result);
document.getElementById("field1").value=result.id;
document.getElementById("field2").value=result.content;
}});
});
});
</script>
</head>
<body>
Field1: <input type="text" id="field1" value=""><br>
Field2: <input type="text" id="field2" value=" "><br><br>
<div id="div1"><h2>ajax get server message</h2></div>
<button>Got</button>
</body>
</html>
- 路由设置:
beego.Router("/", &controllers.MainController{})
运行结果:
- 初始界面:
- 点击按钮后访问服务端数据:
- 同时因为我们加了一个输出,我们可以在命令行查看服务端的响应情况:可以看到是由服务端响应了ajax请求,更新了网页
3.2 实现静态文件访问
实现静态文件的url访问方式,这里由于使用了beego的框架,所以实现起来很简单,只要在对应目录下设置静态路劲,即可在网页上进行访问
- 由于是静态路径访问在beego中只需要设定对应的目录,所以不需要设置路由和控制器,只要在main.go文件中加入:
- 第一个参数是对应的url,也就是访问路径,第二个参数为本地目录的相对地址
func main() {
beego.SetStaticPath("/static", "static")
beego.Run()
}
- 结果验证:
- 我们在static中加入几张照片,之后通过路径来访问它:
3.3 提交表单,并输出一个表格(使用模板
通过form的表单的自带的action 请求功能,在对应的controller中进行处理
- 控制器controller实现:login.go
- 首先在controller中定义表单数据对应的结构体(这里也随手试了下tag,不影响结果删了即可
- 在get方法中设定对应的url的模板(因为要使用模板
- 之后通过模板文件中的表单提交,来进行post方法的访问,用提交数据输出一个表格,这里用到了beego的动态模板的数据传输方法
- 再beego中使用动态模板对属性赋值需要再模板中将参数设置为
{{.Name}}
,之后再控制器中设置this.Data["Name"] = 参数
,然后设置对应的模板即可this.Tplname
package controllers
import (
beego "github.com/astaxie/beego/server/web"
)
type RegController struct {
beego.Controller
}
type LoginStruct struct {
NAme string `form:"Name"`
Pwd string
Age int
Sex string
}
func (this *RegController) Get() {
this.TplName = "login.tpl"
}
func (this *RegController) Post() {
var regmessage LoginStruct
if error := this.ParseForm(®message); error != nil { //传入user指针
this.Ctx.WriteString("出错了!")
} else {
this.Data["Name"] = regmessage.NAme
this.Data["Age"] = regmessage.Age
this.Data["Password"] = regmessage.Pwd
this.Data["Sex"] = regmessage.Sex
this.TplName = "table.html"
}
}
- 登录模板
- 模板中我们要关注的是action和method,这里我们设定action为我们的处理数据的
url
,method为我们的方法名Post
- 之后我们的每个数据项要与控制器中的对应
- 模板中我们要关注的是action和method,这里我们设定action为我们的处理数据的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body>
<form action="/register" method="post">
<table>
<tbody>
<tr>
<td>用户名</td>
<td>
<input type="text" name ="Name" value="">
</td>
</tr>
<tr>
<td>密码</td>
<td>
<input type="password" name ="Pwd" value="">
</td>
</tr>
<tr>
<td>性别</td>
<td>
<input type="radio" name ="Sex" value="man"checked="checked">男
<input type="radio" name ="Sex" value="woman">女
</td>
</tr>
<tr>
<td>年龄</td>
<td>
<input type="text" name ="Age" value="">
</td>
</tr>
<tr>
<td>
<input type="submit" value="确认">
</td>
<td>
<input type="reset" value="重置">
</td>
</tr>
</tbody>
</table>
</form>
</body>
</html>
- 表格模板:
- 表格模板中要设定对应的动态模板来获得数据,对应控制器中的
Data
- 表格模板中要设定对应的动态模板来获得数据,对应控制器中的
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Table</title>
</head>
<body>
<h4>table:</h4>
<table border="1">
<tr>
<td>Name</td>
<td>{{.Name}}</td>
</tr>
<tr>
<td>Password</td>
<td>{{.Password}}</td>
</tr>
<tr>
<td>Sex</td>
<td>{{.Sex}}</td>
</tr>
<tr>
<td>Age</td>
<td>{{.Age}}</td>
</tr>
</table>
</body>
</html>
- 路由设置:将控制器绑定到对应的url
beego.Router("/register", &controllers.RegController{})
-
实现结果:
-
初始界面:
-
表单提交:
3.4 文件下载功能
这里使用beego的download功能实现
- 控制器:loadfile.go
- 我们设置get方法为下载一个我们的服务端的文件,第一个参数是本地的相对地址,第二个为在网页上显示的文件名
type FileOptDownloadController struct {
beego.Controller
}
func (this *FileOptDownloadController) Get() {
this.Ctx.Output.Download("static/img/img0.jpg", "lufei.jpg")
}
- 路由配置:router
beego.Router("/downloadfile", &controllers.FileOptDownloadController{})
- 运行结果:
3.5 文件上传功能
通过beego的getfile和savetofile实现,使用post请求来实现
- 控制器:file.go
- 设定对应的模板,在模板中执行对应的请求
- 在post方法中实现文件的读取
type UploadController struct {
beego.Controller
}
func (this *UploadController) Get() {
this.TplName = "upload.tpl"
}
func (this *UploadController) Post() {
file, head, err := this.GetFile("file")
if err != nil {
this.Ctx.WriteString("获取文件失败")
return
}
defer file.Close()
filename := head.Filename
err = this.SaveToFile("file", "static/"+filename)
if err != nil {
this.Ctx.WriteString("上传失败1")
} else {
this.Ctx.WriteString("上传成功")
}
}
- 模板:比较简单,只要用一个表单来进行设定的action和method即可发送请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/uploadfile" enctype="multipart/form-data" method="post">
<input type="file" name ="file">
<input type="submit" value="上传">
</form>
</body>
</html>
- 路由设定:
beego.Router("/uploadfile", &controllers.UploadController{})
-
实现结果:
-
初始界面:
- 选择文件后:
- 上传后
- 在本地查看上传结果:
可以看到成功上传到static目录下
四、Curl测试
- 首页测试
curl -v http://localhost:8080
这是一个get方法请求返回了对应的tpl界面
* Rebuilt URL to: http://localhost:8080/
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Length: 724
< Content-Type: text/html; charset=utf-8
< Server: beegoServer:2.0.0-alpha
< Set-Cookie: beegosessionID=c256d6a926ffec9b2e8fcf23b29b24e4; Path=/; HttpOnly
< Date: Sun, 22 Nov 2020 13:32:48 GMT
<
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>json_visit</title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js">
</script>
<script>
$(document).ready(function(){
$("button").click(function(){
$.ajax({url:"http://localhost:8080/user",success:function(result){
$("#div1").html(result);
document.getElementById("field1").value=result.id;
document.getElementById("field2").value=result.content;
}});
});
});
</script>
</head>
<body>
Field1: <input type="text" id="field1" value=""><br>
Field2: <input type="text" id="field2" value=" "><br><br>
<div id="div1"><h2>ajax get server message</h2></div>
<button>Got</button>
</body>
* Connection #0 to host localhost left intact
- json页user
curl -v http://localhost:8080/user
由上买你可知,这个页面的get请求返回的是json数据,所以本次curl也成功返回
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /user HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Access-Control-Allow-Origin: *
< Content-Length: 39
< Content-Type: application/json; charset=utf-8
< Server: beegoServer:2.0.0-alpha
< Set-Cookie: beegosessionID=25bf566d28cbb7a4042f6b9453e29636; Path=/; HttpOnly
< Date: Sun, 22 Nov 2020 13:33:51 GMT
<
{
"id": "111",
"content": "hello"
* Connection #0 to host localhost left intact
- login 页
curl -v http://localhost:8080/register
也是返回对应的tpl页面数据
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /register HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Length: 1119
< Content-Type: text/html; charset=utf-8
< Server: beegoServer:2.0.0-alpha
< Set-Cookie: beegosessionID=ee35b3fa267c4835a0739b127b724f51; Path=/; HttpOnly
< Date: Sun, 22 Nov 2020 13:35:51 GMT
<
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body>
<form action="/register" method="post">
<table>
<tbody>
<tr>
<td>用户名</td>
<td>
<input type="text" name ="Name" value="">
</td>
</tr>
<tr>
<td>密码</td>
<td>
<input type="password" name ="Pwd" value="">
</td>
</tr>
<tr>
<td>性别</td>
<td>
<input type="radio" name ="Sex" value="man"checked="checked">男
<input type="radio" name ="Sex" value="woman">女
</td>
</tr>
<tr>
<td>年龄</td>
<td>
<input type="text" name ="Age" value="">
</td>
</tr>
<tr>
<td>
<input type="submit" value="确认">
</td>
<td>
<input type="reset" value="重置">
</td>
</tr>
</tbody>
</table>
</form>
</body>
</html>
* Connection #0 to host localhost left intact
五、 ab测试
结果重要参数解释:
Server Software: 测试服务器的Web服务器软件名字
Server Hostname: 请求的域名
Server Port: 测试的访问端口
Document Path: 请求URL的绝问文件路径
Document Length: 表示HTTP相应的正文的长度
Concurrency Level: 标识并发的用户数(-c属性来设置)
Time taken for tests: 执行完所有的请求所花费的时间
Complete requests: 表示测试的总请求数
Failed requests: 失败的请求
Total transferred: 表示所有请求的响应数据的总和,包含头信息和正文长度.
HTML transferred: 表示所有请求中响应数据的正文长度 (不包含头信息的长度)
Requests per second: 这个就是我们要的吞吐率,表示每秒处理的请求数
Time per request: 表示每个用户平均等待的时间. 等于 Time taken for tests / (Complete requests / Concurrency Level)
Time per request:(mean, across all concurrent requests) 服务器平均请求处理的时间. 正是吞详细地址的倒数,为:Time per request / Concurrency Level 每个请求花费了多少秒
Transfer rate: 这些请求在单位内,从服务器获取的数据长度. 等于: Total transferred / Time taken for tests
Percentage of the requests served within a certain time (ms): 这部分数据用于描述每个请求处理时间的分页情况,百分之五十不超过183毫秒,. 百分之九十不超过249毫秒,百分百不超过374毫秒.
- test1:
ab -n 1000 -c 100 http://localhost:8080/
- -n <请求数量> -c <并发请求数量>
This is ApacheBench, Version 2.3 <$Revision: 1807734 $>
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 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests
Server Software: beegoServer:2.0.0-alpha
Server Hostname: localhost
Server Port: 8080
Document Path: /
Document Length: 724 bytes
Concurrency Level: 100
Time taken for tests: 1.251 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 953000 bytes
HTML transferred: 724000 bytes
Requests per second: 799.68 [#/sec] (mean)
Time per request: 125.051 [ms] (mean)
Time per request: 1.251 [ms] (mean, across all concurrent requests)
Transfer rate: 744.23 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 1.7 0 8
Processing: 4 123 60.2 105 327
Waiting: 0 118 63.7 103 327
Total: 4 123 60.2 105 327
Percentage of the requests served within a certain time (ms)
50% 105
66% 146
75% 159
80% 168
90% 209
95% 220
98% 291
99% 310
100% 327 (longest request)
- test2
ab -n 10000 -c 1000 http://localhost:8080/
- -n <请求数量> -c <并发请求数量>
- 这次加大测试之后,并发1000发现连接崩掉了,发现只进行了1948次测试
This is ApacheBench, Version 2.3 <$Revision: 1807734 $>
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 1000 requests
apr_socket_recv: Connection reset by peer (104)
Total of 1948 requests completed
-
本地的服务端返回信息为1s中太多的请求导致失败,并且之后再进行任何测试都会失败,(本地服务搞炸了?
-
重启bee run 网页服务后回复正常
-
以下是重启后的结果:
sunhaonan@sunhaonan-VirtualBox:~/gopath/src/cloudgo$ ab -n 1000 -c 100 http://localhost:8080/
This is ApacheBench, Version 2.3 <$Revision: 1807734 $>
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 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests
Server Software: beegoServer:2.0.0-alpha
Server Hostname: localhost
Server Port: 8080
Document Path: /
Document Length: 724 bytes
Concurrency Level: 100
Time taken for tests: 1.304 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 953000 bytes
HTML transferred: 724000 bytes
Requests per second: 766.81 [#/sec] (mean)
Time per request: 130.411 [ms] (mean)
Time per request: 1.304 [ms] (mean, across all concurrent requests)
Transfer rate: 713.64 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 2.5 0 12
Processing: 2 127 51.1 122 357
Waiting: 0 119 58.7 122 357
Total: 2 128 51.4 122 357
Percentage of the requests served within a certain time (ms)
50% 122
66% 143
75% 155
80% 162
90% 194
95% 220
98% 244
99% 279
100% 357 (longest request)
源码分析和库学习
七、个人总结
本次实验主要了解了使用go来搭建web服务,同时了解了beego等的go语言web服务框架,尝试进行了get,post等请求的操作,和文件的处理操作,主要花费时间的是框架的搭建和使用方法的学习,花费了较多时间