# Web-Like-Cloudgo
**Repository Path**: nonli/web-like-cloudgo
## Basic Information
- **Project Name**: Web-Like-Cloudgo
- **Description**: web like cloudgo
service-computing hw
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2020-11-23
- **Last Updated**: 2020-12-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# README
## 概述
开发简单 web 服务程序 cloudgo,了解 web 服务器工作原理。
## Web服务程序
### 文件结构

### 代码
#### main
启动服务相关
```go
package main
import (
"os"
"gitee.com/nonli/cloudgo/service"
flag "github.com/spf13/pflag"
)
const (
PORT string = "8080"
)
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)
}
```
**启动服务**
.../cloudgo/service/
```shell
go install
```
.../clodgo/
```shell
go run main.go
```
**运行截图**

#### 服务与路由
service.go
设置路由、中间件等基础配置;
```go
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("/", homeHandler(formatter)).Methods("GET")
mx.HandleFunc("/allusr", postDataHandler(formatter)).Methods("POST")
mx.HandleFunc("/allusr", getDataHandler(formatter)).Methods("GET")
mx.HandleFunc("/usr", getUserHandler(formatter)).Methods("GET")
mx.HandleFunc("/api/unknown", NotImplemented)
mx.HandleFunc("/api/test", apiTestHandler(formatter)).Methods("GET")
mx.PathPrefix("/").Handler(http.FileServer(http.Dir(webRoot + "/assets/")))
}
```
#### 表单提交
访问http://localhost:8080/时
通过
```go
mx.HandleFunc("/", homeHandler(formatter)).Methods("GET")
```
来加载页面,其中用html语句实现了表单
```html
```

点击按钮后发生跳转到http://localhost:8080/allusr,具体过程见后文**提交表单,并输出一个表格**
#### 支持静态文件服务
利用`http.FileServer`来实现静态文件访问,将路径定位到`/assets/`文件夹下;
```go
mx.PathPrefix("/").Handler(http.FileServer(http.Dir(webRoot + "/assets/")))
```
**运行截图**


#### 支持简单 js 访问
使用js返回最新数据,其中`userData.html`中使用了js,如下图可见js成功运行;
get.js
```javascript
$(document).ready(function () {
$.ajax({
url: "/api/get"
}).then(function (data) {
for (let index = 0; index < data.length; index++) {
const usr = data[index]
let table = $("
")
table.append($(" | ").text(usr.username))
table.append($(" | ").text(usr.password))
$("tbody").append(table)
}
});
});
```
当访问http://localhost:8080/usr时,根据
```go
mx.HandleFunc("/usr", getUserHandler(formatter)).Methods("GET")
```
执行了
```go
func getUserHandler(formatter *render.Render) http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
formatter.HTML(writer, http.StatusOK, "userData", nil)
}
}
```
返回`userData.html`模版,其中加载了`get.js`文件,该文件又访问了http://localhost:8080/api/get来获取最新的用户列表;
根据
```go
mx.HandleFunc("/api/get", getUser(formatter)).Methods("GET")
```
调用handler来以JSON格式返回用户列表;
```go
func getUser(formatter *render.Render) http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
formatter.JSON(writer, http.StatusOK, userlist)
}
}
```
显示

如果进入http://localhost:8080/注册新的用户
再重新访问http://localhost:8080/usr

重复进行上述步骤,增加的用户都可以正确显示


#### 提交表单,并输出一个表格(必须使用模板)
同样进入http://localhost:8080/注册新的用户
根据表单会跳转到http://localhost:8080/allusr
```go
mx.HandleFunc("/allusr", postDataHandler(formatter)).Methods("POST")
```
handler会记录表单数据,同时返回模版`form.html`,并返回用户列表`UserList []user`;
```go
func postDataHandler(formatter *render.Render) http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
request.ParseForm()
newUser := parseUser(request.Form)
userlist = append(userlist, newUser)
formatter.HTML(writer, http.StatusOK, "form", struct {
UserList []user
}{UserList: userlist})
}
}
```
其中`user`的定义
```go
type user struct {
Username string `json:"username"`
Password string `json:"password"`
}
```
form.html中相关的关键语句,遍历了切片,并在表格中显示了其内容;
```html
| Username |
Password |
{{range .UserList }}
| {{.Username}} |
{{.Password}} |
{{end}}
```
因而界面显示

重复上述步骤注册新用户,可以看到用户列表正确更新了;


#### 返回JSON信息
访问http://localhost:8080/api/test
根据路由执行
```go
func apiTestHandler(formatter *render.Render) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
formatter.JSON(w, http.StatusOK, struct {
ID string `json:"id"`
Content string `json:"content"`
}{ID: "8675309", Content: "Hello from Go!"})
}
}
```
可以返回
```json
{
"id": "8675309",
"content": "Hello from Go!"
}
```
#### 实现501返回码
访问http://localhost:8080/api/unknown
路由
```go
mx.HandleFunc("/api/unknown", NotImplemented)
```
根据路由访问
```go
func NotImplemented(w http.ResponseWriter, r *http.Request) {
http.Error(w, "501 page not implemented, coder is lazy", http.StatusNotFound)
}
func NotImplementedHandler() http.Handler { return http.HandlerFunc(NotImplemented) }
```
返回
```
501 page not implemented, coder is lazy
```
## curl测试
访问http://localhost:8080/

访问http://localhost:8080/allusr,并提交表单

访问http://localhost:8080/allusr

访问http://localhost:8080/api/get

访问http://localhost:8080/api/unknown

## ab测试
执行`ab -n 1000 -c 100 localhost:8080/`进行压力测试
其中`-n`参数是总共的请求执行次数,`-c`参数是并发数;


其中
- `Document Path`请求的文件
- `Document Length`请求的单个文件的大小
- `Concurrency Level`并发数
- `Time taken for tests`测试总花费
- `Complete requeses`测试发起的请求数
- `Failed requests`失败的请求数量
- `Total transferred`总传输量
- `HTML transferred`接受的文件的总大小
- `Requests per second`平均每秒完成的请求
- `Time per request(mean)`用户角度看,完成一个请求的平均时间
- `Time per request(mean, across all concurrent requests)`服务器完成一个请求的平均时间
- `Transfer rate`网络传输速度
- `Connection Times`对`Time per request(mean)`进行细分统计,统计连接、处理、等待和total四个方面,计算了最小值、均值、标准差、中位数和最大值;
- `Percentage of the requests served within a certain time`处理请求时间的百分位数,a% 的请求能够在 t 时间内处理完毕
## 扩展内容
[net/http实现原理阅读](https://blog.csdn.net/nonoli287/article/details/110007491)