# 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服务程序 ### 文件结构 ![1](./Picture/1.png) ### 代码 #### 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 ``` **运行截图** ![2](./Picture/2.png) #### 服务与路由 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
Username:
Password:
``` ![6](./Picture/6.png) 点击按钮后发生跳转到http://localhost:8080/allusr,具体过程见后文**提交表单,并输出一个表格** #### 支持静态文件服务 利用`http.FileServer`来实现静态文件访问,将路径定位到`/assets/`文件夹下; ```go mx.PathPrefix("/").Handler(http.FileServer(http.Dir(webRoot + "/assets/"))) ``` **运行截图** ![3](./Picture/3.png) ![3](./Picture/4.png) #### 支持简单 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) } } ``` 显示 ![5](./Picture/5.png) 如果进入http://localhost:8080/注册新的用户 再重新访问http://localhost:8080/usr ![8](./Picture/8.png) 重复进行上述步骤,增加的用户都可以正确显示 ![10](./Picture/10.png) ![12](./Picture/12.png) #### 提交表单,并输出一个表格(必须使用模板) 同样进入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 {{range .UserList }} {{end}}
Username Password
{{.Username}} {{.Password}}
``` 因而界面显示 ![7](./Picture/7.png) 重复上述步骤注册新用户,可以看到用户列表正确更新了; ![9](./Picture/9.png) ![11](./Picture/11.png) #### 返回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/ ![13](./Picture/13.png) 访问http://localhost:8080/allusr,并提交表单 ![14](./Picture/14.png) 访问http://localhost:8080/allusr ![14](./Picture/15.png) 访问http://localhost:8080/api/get ![16](./Picture/16.png) 访问http://localhost:8080/api/unknown ![17](./Picture/17.png) ## ab测试 执行`ab -n 1000 -c 100 localhost:8080/`进行压力测试 其中`-n`参数是总共的请求执行次数,`-c`参数是并发数; ![18](./Picture/18.png) ![19](./Picture/19.png) 其中 - `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)