服务计算 作业七 模仿实现Cloudgo

内容与要求

1、概述
开发简单 web 服务程序 cloudgo,了解 web 服务器工作原理。

  • 任务目标

    • 熟悉 go 服务器工作原理
    • 基于现有 web 库,编写一个简单 web 应用类似 cloudgo。
    • 使用 curl 工具访问 web 程序
    • 对 web 执行压力测试

2、任务要求

  • 基本要求

    • 编程 web 服务程序 类似 cloudgo 应用。
    • 支持静态文件服务
    • 支持简单 js 访问
    • 提交表单,并输出一个表格(必须使用模板)
    • 使用 curl 测试,将测试结果写入 README.md
    • 使用 ab 测试,将测试结果写入 README.md。并解释重要参数。
  • 扩展要求

    选择以下一个或多个任务,以博客的形式提交。

    • 1.通过源码分析、解释一些关键功能实现
    • 2.选择简单的库,如 mux 等,通过源码分析、解释它是如何实现扩展的原理,包括一些 golang 程序设计技巧。

tip:源代码阅读
阅读源代码是学习 golang 绕不开的任务,否则你无法达到你期望的水平与能力。 如何阅读源代码?这是一个非常复杂的话题,知识、经验和技巧都有很大作用。

  • X.1 实现原理阅读

    以 net/http 库 web 工作原理阅读为例:

    • 有原理图,分四个步骤:创建 ServerSocket, 绑定并 listen, accept 连接, 创建 go 线程服务一个连接。
    • 我们从入口函数 ListenAndServe 开始开始用 Ctrl 键开始追代码:
      • 关注函数、方法参数中的 接口 和 函数 参数,是接口一定要了解接口的定义。
      • 随时查阅 API 文档,了解相关类型的属性与方法
      • 忽视任何错误处理、分支处理。尽管其中有许多有趣的东西,也要放弃
      • 其中特别注意闭包、匿名函数、匿名类型这些编程技巧
      • 特别注意接口断言语法 var.(type)
      • 线程要注意上下文对象(context)的构建
  • X.2 DefaultServeMux 与 gorilla/mux 对比阅读

    以 net/http 库 DefaultServeMux 实现为例

    • 注意到类型 ServeMux 。 当然的知道它的任务是将 “用户请求中 path 映射到 Handler”
      • map –> (path/name?, handler)
      • muxEntry:Handler 是接口, pattern?
    • 关键代码
      • pathMatch 函数,你已经知道了,这就是 path == pattern 的简单匹配
      • 再看看 ServeMux 方法的代码,基本就验证了你的想法
        注意循序渐进,避免开始就搞复杂的东西。

    分析 gorilla/mux

    • 官网阅读它的功能与使用
    • 从源代码角度对比 DefaultServeMux 与 gorilla/mux
    • 有哪些收获?
  • X.3 Negroni中间件原理与实现

实现过程

静态文件服务

首先创建如下所示的文件结构

assets(静态文件虚拟根目录)
  |-- js
  |-- images
  +-- css

Go 的 net/http 包中提供了静态文件的服务,ServeFile 和 FileServer 等函数,所以参考指导博客内容,直接使用语句:

mx.PathPrefix("/").Handler(http.FileServer(http.Dir(webRoot + "/assets/")))

它的含义是将 path 以 “/” 前缀的 URL 都定位到 webRoot + “/assets/” 为虚拟根目录的文件系统。这样就可以访问到具体的静态文件了。如下:

  • 逐步添加main.css,hello.js,img等文件,可以发现在添加index.html之前,访问localhost:8080/是不会显示网页的,但是可以访问到具体的静态文件:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    原因是在访问网页时,首先匹配到语句mx.HandleFunc("/", homeHandler(formatter)).Methods("GET"),然后调用homeHandler函数:

    func homeHandler(formatter *render.Render) http.HandlerFunc {
    
    	return func(w http.ResponseWriter, req *http.Request) {
    		formatter.HTML(w, http.StatusOK, "index", struct {
    			ID      string `json:"id"`
    			Content string `json:"content"`
    		}{ID: "18342067", Content: "Hello from Go!"})
    	}
    }
    

    显然,此时没有index.html,所以不能加载出页面来。
    但是访问静态文件是直接通过语句mx.PathPrefix("/").Handler(http.FileServer(http.Dir(webRoot + "/assets/")))去访问asset目录下的文件,所以是可以访问的.

  • 添加index.html文件

<!DOCTYPE html>
<html>

<head>
    <link rel="stylesheet" type="text/css" href="css/main.css">
    <script src="http://code.jquery.com/jquery-latest.js"></script>
    <script src="js/hello.js"></script>
    <meta charset="utf-8">
    <title>Hello world</title>
</head>

<body>
    <div id="image">
        <img src="images/cng.png" height="100%" width="100%" />
    </div>

    <div>
        <p>Sample Go Web Application!! </p>
        <p class="name">The ID is:  {{.ID}}</p>
        <p class="content">The content is:  {{.Content}}</p>
    </div>

    <form action="/signin" method="post">
        <p>Username: <input type="text" name="username"></p>
        <p>Password: <input type="password" name="password"></p>
        <input type="submit" value="signin">
    </form>
</body>

</html>

然后再次访问localhost:8080/
在这里插入图片描述
此时已经加载出了页面。

处理静态路径前缀

在 web 应用中,部分应用会将所有静态文件访问路径用独立前缀,例如: http://localhost:8080/static/js/hello.js,所以这时我们需要把这个独立前缀转换成所使用的静态路径,否则就会出现404(因为这个路径并不存在)

这时我们使用如下语句:

// for static
mx.PathPrefix("/static").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(webRoot+"/assets/"))))

它会把路径中的static去掉,然后再路径assets下继续访问,这就转换成了我们所使用的静态文件路径了。
在这里插入图片描述
在这里插入图片描述

简单js访问

使用指导博客中的案例,返回一个匿名结构,并用JSON序列化输出

mx.HandleFunc("/api/test", apiTestHandler(formatter)).Methods("GET")

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: "test user", Content: "Hello from Go!"})
	}
}
$(document).ready(function() {
    $.ajax({
        url: "/api/test"
    }).then(function(data) {
       $('.greeting-id').append(data.id);
       $('.greeting-content').append(data.content);
    });
});

js访问结果如下所示:
在这里插入图片描述
在这里插入图片描述

提交表单,并输出一个表格

既然要做一个页面,那不可避免的就想到首先要做一个登录页面,这就是常见表单的一种。所以,根据项目要求,就实现一个用户登录功能,并且再登录之后输出这个表单。

首先,创建表单

	<form action="/signin" method="post">
        <p>Username: <input type="text" name="username"></p>
        <p>Password: <input type="password" name="password"></p>
        <input type="submit" value="signin">
    </form>

实现功能函数

// index page To signin page
mx.HandleFunc("/signin", checkform).Methods("POST")

func checkform(w http.ResponseWriter, r *http.Request) {
	//fmt.Println("1111111111111111111")
	r.ParseForm()
	username := template.HTMLEscapeString(r.Form.Get("username"))
	password := template.HTMLEscapeString(r.Form.Get("password"))
	t := template.Must(template.New("signin.html").ParseFiles("./templates/signin.html"))
	//fmt.Println("1111111111111111111")
	err := t.Execute(w, struct {
		Username string
		Password string
	}{Username: username, Password: password})
	if err != nil {
		panic(err)
	}
}

输出页面

<html>
    
<head>
    <link rel="stylesheet" type="text/css" href="css/main.css">
    <script src="http://code.jquery.com/jquery-latest.js"></script>
    <script src="js/hello.js"></script>
    <meta charset="utf-8">
    <title>user infomation</title>
</head>

<body>
    <table>
        <tr>
            <td>Username</td>
            <td>Password</td>
        </tr>
        <tr>
            <td>{{.Username}}</td>
            <td>{{.Password}}</td>   
        </tr>
    </table>
</body>

</html>

这样,就完成了一个用户登录表单提交,并且再登录之后输出表单的功能。
演示如下:
在这里插入图片描述
在这里插入图片描述

测试

使用 curl 测试

在这里插入图片描述

使用ab 测试

一个非常详细的博客帮助我们理解这一内容。

常用参数解释
-n:总共的请求执行数,缺省是1;
-c:并发数,缺省是1;
-t:测试所进行的总时间,秒为单位,缺省50000s
-s: 每个response等待最大时长,缺省为30s
-p:POST时的数据文件
-w: 以HTML表的格式输出结果
在这里插入图片描述

输出参数解释

  • Server Software: 平台,被测试的Web服务器软件名称
  • Server Hostname: 连接的服务器域名
  • Server Port: 端口
  • Document Path: 测试页面的路径
  • Document Length: HTTP响应数据的正文长度
  • Concurrency Level: 并发数
  • Time taken for tests: 测试时长
  • Complete requests: 完成的请求数
  • Failed requests: 失败请求数
  • Total transferred: 所有请求的响应数据长度总和,包括每个HTTP响应数据的头信息和正文数据的长度
  • HTML transferred: 所有请求的响应数据中正文数据的总和
  • Requests per second: 吞吐率,Complete requests/Time taken for tests
  • Time per request: 用户平均请求等待时间,Time token for tests/(Complete requests/Concurrency Level)
  • Transfer rate: 网络传输速度,Total trnasferred/ Time taken for tests
  • Connection Times (ms),一个表格,将一个请求的响应时间分成网络链接(Connect),系统处理(Processing)和等待(Waiting)三个部分。

测试结果展示:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

源代码阅读

链接:https://blog.csdn.net/weixin_46092070/article/details/110008289

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值