Pustaka jaringan Go menyertakan http.ServeMux
tipe struktur, yang mendukung multiplexing permintaan HTTP (perutean):Server web merutekan permintaan HTTP untuk sumber daya yang dihosting, dengan URI seperti /sales4today , ke penangan kode; pawang melakukan logika yang sesuai sebelum mengirim respons HTTP, biasanya halaman HTML. Berikut sketsa arsitekturnya:
+------------+ +--------+ +---------+
HTTP request---->| web server |---->| router |---->| handler |
+------------+ +--------+ +---------+
Dalam panggilan ke ListenAndServe
metode untuk memulai server HTTP
http.ListenAndServe(":8888", nil) // args: port & router
argumen kedua dari nil
berarti DefaultServeMux
digunakan untuk perutean permintaan.
gorilla/mux
paket memiliki mux.Router
ketik sebagai alternatif dari DefaultServeMux
atau multiplexer permintaan yang disesuaikan. Dalam ListenAndServe
panggilan, mux.Router
instance akan menggantikan nil
sebagai argumen kedua. Apa yang membuat mux.Router
jadi menarik paling baik ditunjukkan melalui contoh kode:
1. Contoh aplikasi web mentah
minyak mentah aplikasi web (lihat di bawah) mendukung empat operasi CRUD (Create Read Update Delete), yang cocok dengan empat metode permintaan HTTP:POST, GET, PUT, dan DELETE. Dalam mentah app, sumber daya yang dihosting adalah daftar pasangan klise, masing-masing klise dan klise yang saling bertentangan seperti pasangan ini:
Out of sight, out of mind. Absence makes the heart grow fonder.
Pasangan klise baru dapat ditambahkan, dan yang sudah ada dapat diedit atau dihapus.
minyak mentah aplikasi web
package main
import (
"gorilla/mux"
"net/http"
"fmt"
"strconv"
)
const GETALL string = "GETALL"
const GETONE string = "GETONE"
const POST string = "POST"
const PUT string = "PUT"
const DELETE string = "DELETE"
type clichePair struct {
Id int
Cliche string
Counter string
}
// Message sent to goroutine that accesses the requested resource.
type crudRequest struct {
verb string
cp *clichePair
id int
cliche string
counter string
confirm chan string
}
var clichesList = []*clichePair{}
var masterId = 1
var crudRequests chan *crudRequest
// GET /
// GET /cliches
func ClichesAll(res http.ResponseWriter, req *http.Request) {
cr := &crudRequest{verb: GETALL, confirm: make(chan string)}
completeRequest(cr, res, "read all")
}
// GET /cliches/id
func ClichesOne(res http.ResponseWriter, req *http.Request) {
id := getIdFromRequest(req)
cr := &crudRequest{verb: GETONE, id: id, confirm: make(chan string)}
completeRequest(cr, res, "read one")
}
// POST /cliches
func ClichesCreate(res http.ResponseWriter, req *http.Request) {
cliche, counter := getDataFromRequest(req)
cp := new(clichePair)
cp.Cliche = cliche
cp.Counter = counter
cr := &crudRequest{verb: POST, cp: cp, confirm: make(chan string)}
completeRequest(cr, res, "create")
}
// PUT /cliches/id
func ClichesEdit(res http.ResponseWriter, req *http.Request) {
id := getIdFromRequest(req)
cliche, counter := getDataFromRequest(req)
cr := &crudRequest{verb: PUT, id: id, cliche: cliche, counter: counter, confirm: make(chan string)}
completeRequest(cr, res, "edit")
}
// DELETE /cliches/id
func ClichesDelete(res http.ResponseWriter, req *http.Request) {
id := getIdFromRequest(req)
cr := &crudRequest{verb: DELETE, id: id, confirm: make(chan string)}
completeRequest(cr, res, "delete")
}
func completeRequest(cr *crudRequest, res http.ResponseWriter, logMsg string) {
crudRequests<-cr
msg := <-cr.confirm
res.Write([]byte(msg))
logIt(logMsg)
}
func main() {
populateClichesList()
// From now on, this gorountine alone accesses the clichesList.
crudRequests = make(chan *crudRequest, 8)
go func() { // resource manager
for {
select {
case req := <-crudRequests:
if req.verb == GETALL {
req.confirm<-readAll()
} else if req.verb == GETONE {
req.confirm<-readOne(req.id)
} else if req.verb == POST {
req.confirm<-addPair(req.cp)
} else if req.verb == PUT {
req.confirm<-editPair(req.id, req.cliche, req.counter)
} else if req.verb == DELETE {
req.confirm<-deletePair(req.id)
}
}
}()
startServer()
}
func startServer() {
router := mux.NewRouter()
// Dispatch map for CRUD operations.
router.HandleFunc("/", ClichesAll).Methods("GET")
router.HandleFunc("/cliches", ClichesAll).Methods("GET")
router.HandleFunc("/cliches/{id:[0-9]+}", ClichesOne).Methods("GET")
router.HandleFunc("/cliches", ClichesCreate).Methods("POST")
router.HandleFunc("/cliches/{id:[0-9]+}", ClichesEdit).Methods("PUT")
router.HandleFunc("/cliches/{id:[0-9]+}", ClichesDelete).Methods("DELETE")
http.Handle("/", router) // enable the router
// Start the server.
port := ":8888"
fmt.Println("\nListening on port " + port)
http.ListenAndServe(port, router); // mux.Router now in play
}
// Return entire list to requester.
func readAll() string {
msg := "\n"
for _, cliche := range clichesList {
next := strconv.Itoa(cliche.Id) + ": " + cliche.Cliche + " " + cliche.Counter + "\n"
msg += next
}
return msg
}
// Return specified clichePair to requester.
func readOne(id int) string {
msg := "\n" + "Bad Id: " + strconv.Itoa(id) + "\n"
index := findCliche(id)
if index >= 0 {
cliche := clichesList[index]
msg = "\n" + strconv.Itoa(id) + ": " + cliche.Cliche + " " + cliche.Counter + "\n"
}
return msg
}
// Create a new clichePair and add to list
func addPair(cp *clichePair) string {
cp.Id = masterId
masterId++
clichesList = append(clichesList, cp)
return "\nCreated: " + cp.Cliche + " " + cp.Counter + "\n"
}
// Edit an existing clichePair
func editPair(id int, cliche string, counter string) string {
msg := "\n" + "Bad Id: " + strconv.Itoa(id) + "\n"
index := findCliche(id)
if index >= 0 {
clichesList[index].Cliche = cliche
clichesList[index].Counter = counter
msg = "\nCliche edited: " + cliche + " " + counter + "\n"
}
return msg
}
// Delete a clichePair
func deletePair(id int) string {
idStr := strconv.Itoa(id)
msg := "\n" + "Bad Id: " + idStr + "\n"
index := findCliche(id)
if index >= 0 {
clichesList = append(clichesList[:index], clichesList[index + 1:]...)
msg = "\nCliche " + idStr + " deleted\n"
}
return msg
}
//*** utility functions
func findCliche(id int) int {
for i := 0; i < len(clichesList); i++ {
if id == clichesList[i].Id {
return i;
}
}
return -1 // not found
}
func getIdFromRequest(req *http.Request) int {
vars := mux.Vars(req)
id, _ := strconv.Atoi(vars["id"])
return id
}
func getDataFromRequest(req *http.Request) (string, string) {
// Extract the user-provided data for the new clichePair
req.ParseForm()
form := req.Form
cliche := form["cliche"][0] // 1st and only member of a list
counter := form["counter"][0] // ditto
return cliche, counter
}
func logIt(msg string) {
fmt.Println(msg)
}
func populateClichesList() {
var cliches = []string {
"Out of sight, out of mind.",
"A penny saved is a penny earned.",
"He who hesitates is lost.",
}
var counterCliches = []string {
"Absence makes the heart grow fonder.",
"Penny-wise and dollar-foolish.",
"Look before you leap.",
}
for i := 0; i < len(cliches); i++ {
cp := new(clichePair)
cp.Id = masterId
masterId++
cp.Cliche = cliches[i]
cp.Counter = counterCliches[i]
clichesList = append(clichesList, cp)
}
}
Untuk fokus pada perutean dan validasi permintaan, mentah aplikasi tidak menggunakan halaman HTML sebagai tanggapan atas permintaan. Sebaliknya, permintaan menghasilkan pesan respons teks biasa:Daftar pasangan klise adalah respons terhadap permintaan GET, konfirmasi bahwa pasangan klise baru telah ditambahkan ke daftar adalah respons terhadap permintaan POST, dan seterusnya. Penyederhanaan ini memudahkan pengujian aplikasi, khususnya gorilla/mux
komponen, dengan utilitas baris perintah seperti curl .
gorilla/mux
paket dapat diinstal dari GitHub. minyak mentah aplikasi berjalan tanpa batas waktu; karenanya, itu harus diakhiri dengan Control-C atau yang setara. Kode untuk mentah aplikasi, bersama dengan README dan contoh curl tes, tersedia di situs web saya.
2. Perutean permintaan
mux.Router
memperluas perutean gaya REST, yang memberikan bobot yang sama pada metode HTTP (mis., GET) dan URI atau jalur di akhir URL (mis., /klise ). URI berfungsi sebagai kata benda untuk kata kerja HTTP (metode). Misalnya, dalam permintaan HTTP garis awal seperti
GET /cliches
artinya dapatkan semua pasangan klise , sedangkan garis awal seperti
POST /cliches
artinya membuat pasangan klise dari data di badan HTTP .
Dalam mentah aplikasi web, ada lima fungsi yang bertindak sebagai penangan permintaan untuk lima variasi permintaan HTTP:
ClichesAll(...) # GET: get all of the cliche pairs
ClichesOne(...) # GET: get a specified cliche pair
ClichesCreate(...) # POST: create a new cliche pair
ClichesEdit(...) # PUT: edit an existing cliche pair
ClichesDelete(...) # DELETE: delete a specified cliche pair
Setiap fungsi membutuhkan dua argumen:sebuah http.ResponseWriter
untuk mengirim tanggapan kembali ke pemohon, dan penunjuk ke http.Request
, yang merangkum informasi dari permintaan HTTP yang mendasarinya. gorilla/mux
package memudahkan untuk mendaftarkan penangan permintaan ini dengan server web, dan untuk melakukan validasi berbasis regex.
startServer
fungsi di mentah aplikasi mendaftarkan penangan permintaan. Pertimbangkan pasangan pendaftaran ini, dengan router
sebagai mux.Router
contoh:
router.HandleFunc("/", ClichesAll).Methods("GET")
router.HandleFunc("/cliches", ClichesAll).Methods("GET")
Pernyataan ini berarti bahwa permintaan GET untuk garis miring tunggal / atau /klise harus diarahkan ke ClichesAll
fungsi, yang kemudian menangani permintaan. Misalnya, ikal permintaan (dengan % sebagai prompt baris perintah)
% curl --request GET localhost:8888/
menghasilkan respons ini:
1: Out of sight, out of mind. Absence makes the heart grow fonder.
2: A penny saved is a penny earned. Penny-wise and dollar-foolish.
3: He who hesitates is lost. Look before you leap.
Ketiga pasangan klise tersebut merupakan data awal dalam mentah aplikasi.
Dalam pasangan pernyataan pendaftaran ini
router.HandleFunc("/cliches", ClichesAll).Methods("GET")
router.HandleFunc("/cliches", ClichesCreate).Methods("POST")
URInya sama (/klise ) tetapi kata kerjanya berbeda:GET dalam kasus pertama, dan POST dalam kasus kedua. Registrasi ini mencontohkan perutean gaya REST karena perbedaan dalam kata kerja saja sudah cukup untuk mengirimkan permintaan ke dua penangan yang berbeda.
Lebih dari satu metode HTTP diperbolehkan dalam pendaftaran, meskipun ini melemahkan semangat perutean gaya REST:
router.HandleFunc("/cliches", DoItAll).Methods("POST", "GET")
Permintaan HTTP dapat dirutekan pada fitur selain kata kerja dan URI. Misalnya, pendaftaran
router.HandleFunc("/cliches", ClichesCreate).Schemes("https").Methods("POST")
memerlukan akses HTTPS untuk permintaan POST untuk membuat pasangan klise baru. Dengan cara yang sama, pendaftaran mungkin memerlukan permintaan untuk memiliki elemen header HTTP tertentu (mis., kredensial autentikasi).
3. Minta validasi
gorilla/mux
package mengambil pendekatan intuitif yang mudah untuk meminta validasi melalui ekspresi reguler. Pertimbangkan penangan permintaan ini untuk dapatkan satu operasi:
router.HandleFunc("/cliches/{id:[0-9]+}", ClichesOne).Methods("GET")
Registrasi ini mengesampingkan permintaan HTTP seperti
% curl --request GET localhost:8888/cliches/foo
karena foo bukan bilangan desimal. Permintaan menghasilkan kode status 404 (Tidak Ditemukan) yang sudah dikenal. Menyertakan pola regex dalam pendaftaran handler ini memastikan bahwa ClichesOne
fungsi dipanggil untuk menangani permintaan hanya jika URI permintaan diakhiri dengan nilai bilangan bulat desimal:
% curl --request GET localhost:8888/cliches/3 # ok
Sebagai contoh kedua, pertimbangkan permintaan
% curl --request PUT --data "..." localhost:8888/cliches
Permintaan ini menghasilkan kode status 405 (Metode Buruk) karena /klise URI terdaftar, di mentah aplikasi, hanya untuk permintaan GET dan POST. Permintaan PUT, seperti permintaan GET one, harus menyertakan id numerik di akhir URI:
router.HandleFunc("/cliches/{id:[0-9]+}", ClichesEdit).Methods("PUT")
4. Masalah konkurensi
gorilla/mux
router mengeksekusi setiap panggilan ke penangan permintaan terdaftar sebagai goroutine terpisah, yang berarti bahwa konkurensi dimasukkan ke dalam paket. Misalnya, jika ada sepuluh permintaan simultan seperti
% curl --request POST --data "..." localhost:8888/cliches
lalu mux.Router
meluncurkan sepuluh goroutine untuk menjalankan ClichesCreate
penangan.
Dari lima operasi permintaan GET all, GET one, POST, PUT, dan DELETE, tiga yang terakhir mengubah sumber daya yang diminta, clichesList
yang dibagikan yang menampung pasangan klise. Oleh karena itu, minyak mentah aplikasi perlu menjamin konkurensi yang aman dengan mengoordinasikan akses ke clichesList
. Dalam istilah yang berbeda namun setara, mentah aplikasi harus mencegah kondisi balapan di clichesList
. Dalam lingkungan produksi, sistem database dapat digunakan untuk menyimpan sumber daya seperti clichesList
, dan konkurensi yang aman kemudian dapat dikelola melalui transaksi basis data.
minyak mentah app mengambil pendekatan Go yang direkomendasikan untuk konkurensi yang aman:
- Hanya satu goroutine, manajer sumber daya dimulai di mentah aplikasi
startServer
fungsi, memiliki akses keclichesList
setelah server web mulai mendengarkan permintaan. - Penangan permintaan seperti
ClichesCreate
danClichesAll
kirim (penunjuk ke)crudRequest
instance ke saluran Go (secara default aman untuk thread), dan pengelola sumber daya saja yang membaca dari saluran ini. Manajer sumber daya kemudian melakukan operasi yang diminta padaclichesList
.
Arsitektur safe-concurrency dapat digambarkan sebagai berikut:
crudRequest read/write
request handlers------------->resource manager------------>clichesList
Dengan arsitektur ini, tidak ada penguncian eksplisit dari clichesList
diperlukan karena hanya satu goroutine, pengelola sumber daya, yang mengakses clichesList
setelah permintaan CRUD mulai masuk.
Untuk menjaga minyak mentah aplikasi secara bersamaan mungkin, penting untuk memiliki pembagian kerja yang efisien antara penangan permintaan, di satu sisi, dan manajer sumber daya tunggal, di sisi lain. Di sini, untuk ditinjau, adalah ClichesCreate
penangan permintaan:
func ClichesCreate(res http.ResponseWriter, req *http.Request) {
cliche, counter := getDataFromRequest(req)
cp := new(clichePair)
cp.Cliche = cliche
cp.Counter = counter
cr := &crudRequest{verb: POST, cp: cp, confirm: make(chan string)}
completeRequest(cr, res, "create")
}
Lebih banyak sumber daya Linux
- Lembar contekan perintah Linux
- Lembar contekan perintah Linux tingkat lanjut
- Kursus online gratis:Ikhtisar Teknis RHEL
- Lembar contekan jaringan Linux
- Lembar contekan SELinux
- Lembar contekan perintah umum Linux
- Apa itu container Linux?
- Artikel Linux terbaru kami
Pengendali permintaan ClichesCreate
memanggil fungsi utilitas getDataFromRequest
, yang mengekstrak klise dan kontra-klise baru dari permintaan POST. ClichesCreate
fungsi kemudian membuat ClichePair
baru , menyetel dua bidang, dan membuat crudRequest
untuk dikirim ke manajer sumber daya tunggal. Permintaan ini mencakup saluran konfirmasi, yang digunakan pengelola sumber daya untuk mengembalikan informasi ke pengendali permintaan. Semua pekerjaan penyiapan dapat dilakukan tanpa melibatkan pengelola sumber daya karena clichesList
belum diakses.
completeRequest
fungsi utilitas dipanggil di akhir ClichesCreate
fungsi dan penangan permintaan lainnya
completeRequest(cr, res, "create") // shown above
membawa pengelola sumber daya ke dalam permainan dengan menempatkan crudRequest
ke dalam crudRequests
saluran:
func completeRequest(cr *crudRequest, res http.ResponseWriter, logMsg string) {
crudRequests<-cr // send request to resource manager
msg := <-cr.confirm // await confirmation string
res.Write([]byte(msg)) // send confirmation back to requester
logIt(logMsg) // print to the standard output
}
Untuk permintaan POST, manajer sumber daya memanggil fungsi utilitas addPair
, yang mengubah clichesList
sumber:
func addPair(cp *clichePair) string {
cp.Id = masterId // assign a unique ID
masterId++ // update the ID counter
clichesList = append(clichesList, cp) // update the list
return "\nCreated: " + cp.Cliche + " " + cp.Counter + "\n"
}
Manajer sumber daya memanggil fungsi utilitas serupa untuk operasi CRUD lainnya. Perlu diulangi bahwa resource manager adalah satu-satunya goroutine yang membaca atau menulis clichesList
setelah server web mulai menerima permintaan.
Untuk aplikasi web jenis apa pun, gorilla/mux
package menyediakan perutean permintaan, validasi permintaan, dan layanan terkait dalam API yang langsung dan intuitif. minyak mentah aplikasi web menyoroti fitur utama paket. Cobalah paket tersebut, dan kemungkinan besar Anda akan menjadi pembeli.