本文仅介绍golang web开发框架httprouter在开发简单的REST API f服务器上的使用,golang开发工具等。
本文要点:
1. golang http包的介绍
2. curl http协议分析工具的介绍和使用
3. web开发框架 httprouter的介绍和使用
4. ab开发压力测试工具的介绍和使用学习前提
1.了解http的基础知识
2.了解“C/S架构”知识
3.了解golang中接口的用法
HTTP是互联网上应用最为广泛的一种网络协议,定义了客户端和服务端之间请求与响应的传输标准。
Go lang标准库內建提供了net/http包,涵盖了HTTP客户端和服务端的具体实现,使用net/http包,我们可以很方便地编写HTTP客户端和服务端程序。
说起请求数据, 你最先想到的是拿东西,我们来看看golang的net/http包中GET方法的原型和使用
func (c *Client)Get(url string) (r *response, err error)
要求请求一个资源只需要调用http.Get()方法
res, err := http.Get("http://example.com/")
if err != nil {
//handle error
return
}
defer res.Body.close()
//output to standard output stream
io.Copy(os.Stdout, res.Body)
...
当然拿了别人的东西,我们也不会忘记还回去,golang以POST方式发送数据也很简单。
func (c *Client) Post(url string, bodyType string ,body io.Reader) (r *Response, err error)
第一个参数是目标URL,第二个参数是POST数据的资源类型(MIMETYPE),第三个是数据的比特流(以[]byte形式)。比如我们要上传一张图片,就可以像下面这样写。
res, err := http.Post("http://example.com/upload", "image/jpeg", &imageDataBuf)
if err != nil {
// handle error
return
}
if res.StatusCode != http.StatusOK {
// handle error
return
}
...
在web开发中,经常离不开表单提交,golang的net/http包则很好地封装了表单提交的方法,http.PostForm()就很好地实现了标准编码格式为application/x-www-form-urlencoded的表单提交,比如我们要提交一篇新文章。
res, err := http.PostForm("http://example.com", url.Values{"title":{"article title"}, "content":{"article body"}})
if err != nil {
//handle error
return
}
...
此外,还有请求的头部信息,( *http.Client)http.Head(),更优雅地定制你的请求,( *http.Client)http.Do(),好了,少年,自己去探索吧。
然而如果你觉得上面的这些方法能满足你的需求,那么你就太容易满足了,而golang就是这么不想让你满足,一个自称“简单,简单,简单”的语言,怎么能轻易放弃?
除了上述介绍的基本的HTTP操作外,golang标准库也暴露了比较底层的HTTP相关库,让开发者可以基于这些库灵活地定制HTTP服务器和使用HTTP服务。
前面使用的基本方法实际上都是基于http.DefaultClient进行调用的,比如http.Get()等价于http.DefaultClient.Get(),所以呢?既然存在默认的Client,是不是就存在不默认的Client?那是必须的!
client := &http.Client{
Transport: &http.Transport{
Dial: func(netw, addr string) (net.Conn, error) {
deadline := time.Now().Add(25 * time.Second)
c, err := net.DialTimeout(netw, addr, time.Second*20)
if err != nil {
return nil, err
}
c.SetDeadline(deadline)
return c, nil
},
},
}
但是您需要一点尝试的勇气,如果没有,请找梁静茹。
给你个眼(链)神(接),自己去体会:https://studygolang.com/articles/253
也讲讲HTTP服务端技术吧。
net/http包提供的http.ListenAndServe()方法,可以在指定的地址进行监听,开启一个HTTP服务,服务端该方法的原型如下:
func ListenAndServe(addr string, handler Handler) error
该方法用于在指定的TCP网络地址addr进行监听,然后调用服务端处理程序传处理传入的请求。第一个参数是监听地址,第二个参数是服务端的处理程序,一般为nil,这意味这服务端调用http.DefaultServeMux(既然存在默认,是不是也就存在不默认呢?哈哈),而服务端编写的业务逻辑处理程序http。Handle()或者http.HandleFunc()默认注入http.DefaultServeMux中,具体的代码如下:
func handIt(w http.ResponseWriter, r *http.Request) {
fmt.Printf(w, "hello %q", html.EscapeString(r,URL.Path))
}
http.Handle("/path",pathHandler)
http.HandleFunc("/path1", handIt)
log.Fatal(http.ListenAndServe(":8080",nil))
...
说了既然有默然就会有自定义。
可以通过自定义更多地控制服务端的行为。
s := http,Serve {
Addr: ":8080"
Handler: myHandler,
ReadTimeout: 10*time.Second
WriteTimeout: 10*time.Second
MaxHeaderByte: 1 << 10
}
log.Fatal(s.ListenAndServe())
...
OK,优雅使用!对于golang的net/http包,您还有什么不满意?
curl是个什么东西?以前似乎没听过?╮(╯▽╰)╭,没关系,百度一下不就好,百度又没被墙,您说是不是啊。
curl是利用URL语法在命令行方式下工作的开源文件传输工具。它被广泛应用在Unix、多种Linux发行版
中,并且有DOS和Win32、Win64下的移植版本。
百度了也看不懂。没关系,先找个教程照着做,用了再说也不迟。
墙裂推荐潘大神的这篇博客:http://blog.csdn.net/pmlpml/article/details/78404838
我们写个小东西测试一下:
package main
import (
"fmt"
"log"
"net/http"
"github.com/julienschmidt/httprouter"
)
func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprint(w, "welcome!\n")
}
func main() {
router := httprouter.New()
router.GET("/", Index)
log.Fatal(http.ListenAndServe(":8080", router))
}
看一下输出了些什么?
-v 是什么东西?
不如逛逛小花园?http://www.cnblogs.com/sunada2005/p/3829772.html
sysuygm@localhost:~$ curl -v http://localhost:8080/
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Wed, 08 Nov 2017 02:57:33 GMT
< Content-Length: 9
< Content-Type: text/plain; charset=utf-8
<
welcome!
* Connection #0 to host localhost left intact
sysuygm@localhost:~$ ^C
正如潘大神所说,每个人都有一颗做框架的心呐,您说是不是啊?除了net/http提供强大工具以外,您可能会在web服务器开发上陷入选择困难症,反正我是这样的。至于httprouter有什么独特的风骚,这个我也说不出来。这个也许能给您答案:http://blog.csdn.net/real_myth/article/details/76161540
不管怎么样,先用了再说。二不多说,上码为敬。
一个简单的RESTful API:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"github.com/julienschmidt/httprouter"
)
// path为 ‘/’的handler
func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprint(w, "welcome!\n")
}
// 请求获取的实体
type Book struct {
ISDN string "'json':'isdn'"
Title string "'json':'title'"
Author string "'json':'author'"
Pages int "'json':''pages"
}
//响应时返回的数据 Data
type JsonResponse struct {
Meta interface{} "json:'status'"
Data interface{} "json:'data'"
}
//请求错误的响应处理
type JsonErrorResponse struct {
Error *ApiError "json:'error'"
}
type ApiError struct {
Status int16 "json:'status'"
Title string "json:'title'"
}
var bookstore = make(map[string]*Book)
// path为 ‘/books’的handler
func BookIndex(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
books := []*Book{}
for _, book := range bookstore {
books = append(books, book)
}
response := &JsonResponse{Data: &books}
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(response); err != nil {
panic(err)
}
}
// path为 ‘/books/:json’的handler
func BookShow(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
isdn := params.ByName("isbn")
book, ok := bookstore[isdn]
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
if !ok {
w.WriteHeader(http.StatusNotFound)
response := JsonErrorResponse{Error: &ApiError{Status: 404, Title: "Record Not Found"}}
if err := json.NewEncoder(w).Encode(response); err != nil {
panic(err)
}
}
response := JsonResponse{Data: book}
if err := json.NewEncoder(w).Encode(response); err != nil {
panic(err)
}
}
func main() {
router := httprouter.New()
router.GET("/", Index)
router.GET("/books/", BookIndex)
router.GET("/books/:json", BookShow)
//测试数据
bookstore["123"] = &Book{
ISDN: "123",
Title: "Silence of the Lambs",
Author: "Thomas Harris",
Pages: 367,
}
//测试数据
bookstore["124"] = &Book{
ISDN: "124",
Title: "tO KILL a mocking bird",
Author: "Thomas Harris",
Pages: 320,
}
log.Fatal(http.ListenAndServe(":8080", router))
}
有本事自己看英文去:https://medium.com/@gauravsingharoy/build-your-first-api-server-with-httprouter-in-golang-732b7b01f6ab
Apache的ab命令模拟多线程并发请求,测试服务器负载压力,也可以测试nginx、lighthttp、IIS等其它
Web服务器的压力。
对上面写的简单服务器进行压力测试,看看输出些什么?
特别注意页面响应所消耗的时间和流量!
sysuygm@localhost:~$ ab -n 1000 -c 100 http://localhost:8080/books
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
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:
Server Hostname: localhost
Server Port: 8080
Document Path: /books
Document Length: 42 bytes
Concurrency Level: 100
Time taken for tests: 0.055 seconds
Complete requests: 1000
Failed requests: 0
Non-2xx responses: 1000
Total transferred: 192000 bytes
HTML transferred: 42000 bytes
Requests per second: 18234.20 [#/sec] (mean)
Time per request: 5.484 [ms] (mean)
Time per request: 0.055 [ms] (mean, across all concurrent requests)
Transfer rate: 3418.91 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 0.7 1 4
Processing: 0 4 5.5 2 25
Waiting: 0 3 5.3 1 23
Total: 2 5 5.4 3 27
Percentage of the requests served within a certain time (ms)
50% 3
66% 3
75% 4
80% 5
90% 19
95% 21
98% 23
99% 23
100% 27 (longest request)
sysuygm@localhost:~$ ab -n 1000 -c 100 http://localhost:8080/books/:json
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
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:
Server Hostname: localhost
Server Port: 8080
Document Path: /books/:json
Document Length: 78 bytes
Concurrency Level: 100
Time taken for tests: 0.035 seconds
Complete requests: 1000
Failed requests: 0
Non-2xx responses: 1000
Total transferred: 207000 bytes
HTML transferred: 78000 bytes
Requests per second: 28743.89 [#/sec] (mean)
Time per request: 3.479 [ms] (mean)
Time per request: 0.035 [ms] (mean, across all concurrent requests)
Transfer rate: 5810.53 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 1 1 0.4 1 3
Processing: 0 2 0.6 2 4
Waiting: 0 1 0.5 1 4
Total: 2 3 0.8 3 7
Percentage of the requests served within a certain time (ms)
50% 3
66% 3
75% 3
80% 3
90% 4
95% 5
98% 6
99% 6
100% 7 (longest request)
sysuygm@localhost:~$
觉得好用,就赶紧入手吧。
golang的http client源码简析 :https://studygolang.com/articles/5774
HTTP 协议 与 golang web 应用服务:http://blog.csdn.net/pmlpml/article/details/78404838
面向对象设计思想与 golang 编程:http://blog.csdn.net/pmlpml/article/details/78326769