服务计算(7)写一个简单 web 程序

开发 web 服务程序

1、概述

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

任务目标

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

2、任务要求

基本要求

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

 

go服务器工作原理:

在分布式计算中,计算机之间协作最基础、最简单的结构就是 C/S 架构 。一个 进程 扮演 Server 提供服务,一个或多个 进程 扮演 Client 发起服务请求。对于Go语言来说,只需要短短几行代码,就可以实现一个简单的http server,加上协程的加持,Go实现的http server拥有非常优秀的性能。如下图所示:

通过net/http标准库,我们可以启动一个http服务器,然后让这个服务器接收请求并返回响应。net/http标准库还提供了一个连接多路复用器(multiplexer)的接口以及一个默认的多路复用器。

Go语言创建一个服务器的步骤非常简单,只要调用ListenAndServe并传入网络地址以及负责处理请求的处理器(handler)作为参数就可以了。如果网络地址为空字符串,那么服务器默认使用80端口进行网络连接;如果处理器参数为nil,那么服务器将使用默认的多路复用器DefaultServeMux。代码实现:

package main

import "net/http"

func main() {
	http.ListenAndServe("", nil)
}

除了可以通过ListenAndServe的参数对服务器的网络地址和处理器进行配置之外,还可以通过Server结构对服务器进行更详细的配置,其中包括为请求读取操作设置超时时间,为响应写入操作设置超时时间、为Server结构设置错误日志记录器等。

package main

import "net/http"

func main() {
	server := http.Server{
		Addr:    "127.0.0.1:8080",
		Handler: nil,
	}
	server.ListenAndServe()
}

在Go语言中,一个处理器就是一个拥有ServeHTTP方法的接口,这个ServeHTTP方法需要接收两个参数:第一个参数是一个ResponseWriter接口,而第二个参数则是一个指向Request结构的指针。换句话说,任何接口只要拥有一个ServeHTTP方法,并且该方法带有以下签名,那么它就是一个处理器:

ServeHTTP(http.ResponseWriter, *http.Request)

基于现有 web 库,编写一个简单 web 应用类似 cloudgo

使用的框架是:negroni,mux,render。

首先安装这三个框架的包:

go get github.com/codegangsta/negroni
go get github.com/gorilla/mux
go get github.com/unrolled/render

代码:service.go:

package service

import (
	"net/http"
    "os"
    "github.com/codegangsta/negroni"
    "github.com/gorilla/mux"
    "github.com/unrolled/render"
    "html/template"
)

// NewServer configures and returns a Server.
func NewServer() *negroni.Negroni {
	// 返回一个Render实例的指针
    formatter := render.New(render.Options{
        Directory: "templates",
        Extensions: []string{".html"},
        IndentJSON: true,   // 输出时的格式是方便阅读的JSON
    })

    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("/api/test", apiTestHandler(formatter)).Methods("GET")
    mx.HandleFunc("/user", userHandler).Methods("POST")
	mx.PathPrefix("/").Handler(http.FileServer(http.Dir(webRoot + "/assets/")))
}

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: "17343040", Content: "Welcome from Go!"})
	}
}

func apiTestHandler(formatter *render.Render) http.HandlerFunc {
//支持 JavaScript 访问
	return func(w http.ResponseWriter, req *http.Request) {
		formatter.JSON(w, http.StatusOK, struct {
			ID      string `json:"id"`
			Content string `json:"content"`
		}{ID: "17343040", Content: "Hello from Go!"})
	}
}
//提交表单,并输出一个表格
func userHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	username := template.HTMLEscapeString(r.Form.Get("username"))
	password := template.HTMLEscapeString(r.Form.Get("password"))
	t := template.Must(template.New("user.html").ParseFiles("./templates/user.html"))
	err := t.Execute(w, struct {
		Username string
		Password string
	}{Username: username, Password: password})
	if err != nil {
		panic(err)
	}
}

main.go:

package main

import (
    "os"
    "./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)
}

功能测试:

输入go run main.go 然后打开浏览器访问http://localhost/8080

go run main.go

可以看到index页面如下:(有bug:图片访问出现了问题) 

index

在index页面输入Username和Password,点击Sign In后转到UserPage。 

userpage

 

curl 测试:

$ curl -v http://localhost:8080
* About to connect() to localhost port 8080 (#0)
*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:8080
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Mon, 23 Nov 2020 22:36:54 GMT
< Content-Length: 504
< Content-Type: text/html; charset=utf-8
< 
<html>
<head>
    <link rel="stylesheet" href="css/main.css"/>
    <script src="http://code.jquery.com/jquery-latest.js"></script>
    <script src="js/hello.js"></script>
</head>
<body>
    <img src="images/cng.png" height="400" width="400"/>
    Welcome to Go Web Application!!
        <div>
            <p class="greeting-id">The ID is {{.ID}}</p>
            <p class="greeting-content">The content is {{.Content}}</p>
        </div>
        <form class="box" action="./user" method="POST">
            <input type="text" name="username" placeholder="Username">
            <input type="password" name="password" placeholder="Password">
            <input type="submit" value="Sign In">
          </form>
</body>
</html>

* Connection #0 to host localhost left intact

ab测试:

ab -n 8080 -c 10 http://localhost:8080/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值