云原生之旅 - 1)Golang 入门 简单 HTTP Server
前言
本人最近几年一直在学习并且实践云原生,也从测试转型到DevOps,公司的所有服务也从数据中心搬到云端,回顾过去几年学到的知识,觉得是时候总结一下了,所以准备以云原生为题材写下这一过程中所有学到的知识点,希望对大家有所帮助。
在我带你开始云原生之旅前,需要入门一下golang,没办法,谁让它是云原生领域里正红的发紫的“角儿,从最底层的 containerd,到编排层的 Kubernetes,再到如今正红的发紫的 Istio,随便拿出一个来都是golang写的。我相信你学了Golang之后,会发现它非常好用,library丰富,部署简单,容易上手,咱也不想成为资深开发,但我们自动化,写工具之类的还是需要掌握一门语言的,我反正已经放弃其它学过的语言了。
本篇文章讲述如何构建一个简单的HTTP服务器,Golang的标准库net/http 让这一切变得很容易,仅适用于golang新手(已了解过Golang语言特性)。
关键词:Golang,入门,HTTP server,DevOps,云原生,Golang 例子
Learning By Example
让我们用Pizza餐厅点单作为例子,定义 Pizza 餐厅 API 3个endpoints 如下:
- Get 请求 - List all pizzas:
/pizzas
- POST 请求 - Make a pizza order:
/orders
- Get 请求 - List all orders:
/orders
代码如下:
1 package main 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "log" 7 "net/http" 8 ) 9 10 type Pizza struct { 11 ID int `json:"id"` 12 Name string `json:"name"` 13 Price int `json:"price"` 14 } 15 16 type Pizzas []Pizza 17 18 func (ps Pizzas) FindByID(ID int) (Pizza, error) { 19 for _, pizza := range ps { 20 if pizza.ID == ID { 21 return pizza, nil 22 } 23 } 24 25 return Pizza{}, fmt.Errorf("couldn't find pizza with ID: %d", ID) 26 } 27 28 type Order struct { 29 PizzaID int `json:"pizza_id"` 30 Quantity int `json:"quantity"` 31 Total int `json:"total"` 32 } 33 34 type Orders []Order 35 36 type pizzasHandler struct { 37 pizzas *Pizzas 38 } 39 40 func (ph pizzasHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 41 w.Header().Set("Content-Type", "application/json") 42 43 switch r.Method { 44 case http.MethodGet: 45 if len(*ph.pizzas) == 0 { 46 http.Error(w, "Error: No pizzas found", http.StatusNotFound) 47 return 48 } 49 50 json.NewEncoder(w).Encode(ph.pizzas) 51 default: 52 http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) 53 } 54 } 55 56 type ordersHandler struct { 57 pizzas *Pizzas 58 orders *Orders 59 } 60 61 func (oh ordersHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 62 w.Header().Set("Content-Type", "application/json") 63 64 switch r.Method { 65 case http.MethodPost: 66 var o Order 67 68 if len(*oh.pizzas) == 0 { 69 http.Error(w, "Error: No pizzas found", http.StatusNotFound) 70 return 71 } 72 73 err := json.NewDecoder(r.Body).Decode(&o) 74 if err != nil { 75 http.Error(w, "Can't decode body", http.StatusBadRequest) 76 return 77 } 78 79 p, err := oh.pizzas.FindByID(o.PizzaID) 80 if err != nil { 81 http.Error(w, fmt.Sprintf("Error: %s", err), http.StatusBadRequest) 82 return 83 } 84 85 o.Total = p.Price * o.Quantity 86 *oh.orders = append(*oh.orders, o) 87 json.NewEncoder(w).Encode(o) 88 case http.MethodGet: 89 json.NewEncoder(w).Encode(oh.orders) 90 default: 91 http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) 92 } 93 } 94 95 func main() { 96 var orders Orders 97 pizzas := Pizzas{ 98 Pizza{ 99 ID: 1, 100 Name: "Veggie", 101 Price: 12, 102 }, 103 Pizza{ 104 ID: 2, 105 Name: "Cheese", 106 Price: 11, 107 }, 108 Pizza{ 109 ID: 3, 110 Name: "Meat", 111 Price: 10, 112 }, 113 } 114 115 mux := http.NewServeMux() 116 mux.Handle("/pizzas", pizzasHandler{&pizzas}) 117 mux.Handle("/orders", ordersHandler{&pizzas, &orders}) 118 119 log.Fatal(http.ListenAndServe(":8080", mux)) 120 }
pizzas 和 orders 数据都保留在内存中,为了尽可能简单,没有任何数据存储持久化,如果需要,我可以开设另一篇来讲 gorm 之类的,也是现学的 :)
也不检查请求的header,客户端发的data body
运行
本地启动
go run main.go
### https://www.cnblogs.com/wade-xu/p/16705918.html ###
测试
调用 get all pizzas 接口
创建一个 order 订4个 Veggie pizzas
curl -X POST localhost:8080/orders -d '{"pizza_id":1,"quantity":4}' | jq
再创建一个Order 订2个 Cheese pizzas
curl -X POST localhost:8080/orders -d '{"pizza_id":2,"quantity":2}' | jq { "pizza_id": 2, "quantity": 2, "total": 22 }
最后 Get orders 看下
Simple HTTP server works well.
### https://www.cnblogs.com/wade-xu/p/16705918.html ###
感谢阅读,如果您觉得本文的内容对您的学习有所帮助,您可以打赏和推荐,您的鼓励是我创作的动力。