Skip to content

Commit

Permalink
update part 4.4 4.5 4.6
Browse files Browse the repository at this point in the history
  • Loading branch information
quocanh1897 committed Sep 12, 2019
1 parent d2d46bb commit cd4d8af
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 97 deletions.
93 changes: 0 additions & 93 deletions ch3-rpc/ch3-05-grpc-advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,6 @@ func filter(
}
```

<<<<<<< HEAD
Ta áp dụng cho hàm `SayHello` của service `Greeter`:

```go
Expand Down Expand Up @@ -336,9 +335,6 @@ $ ./3-interceptor
Kết quả cho thấy request của client đi qua hàm filter (là interceptor) gặp hàm `SayHello` throw ra panic, filter lấy panic này trả về cho client.

Tuy nhiên, chỉ một interceptor có thể được gắn cho một service trong gRPC framework, cho nên tất cả chức năng interceptor chỉ có thể thực hiện trong một hàm. Package go-grpc-middleware trong project opensource [grpc-ecosystem](https://github.com/grpc-ecosystem) có hiện thực cơ chế hỗ trợ cho một chuỗi interceptor dựa trên gRPC.
=======
Tuy nhiên, chỉ một interceptor có thể được gắn cho một service trong gRPC framework, cho nên tất cả chức năng interceptor chỉ có thể thực hiện trong một hàm. Package go-grpc-middleware trong project Open source [grpc-ecosystem](https://github.com/grpc-ecosystem) có hiện thực cơ chế hỗ trợ cho một chuỗi interceptor dựa trên gRPC.
>>>>>>> 76ab621db1f62d8cc10e7bd242125b8be848ab04

Một ví dụ về cách sử dụng một chuỗi interceptor trong package go-grpc-middleware:

Expand All @@ -356,92 +352,3 @@ myServer := grpc.NewServer(
```

Xem chi tiết: [go-grpc-middleware](https://github.com/grpc-ecosystem/go-grpc-middleware)
<<<<<<< HEAD
=======

## 3.5.4 gRPC kết hợp với Web service dùng chung port

gRPC được xây dựng bên trên giao thức HTTP/2 nên chúng ta có thể đặt gRPC service vào các port giống như một web service bình thường.

- Với các service không sử dụng TLS:

```go
func main() {
mux := http.NewServeMux()

h2Handler := h2c.NewHandler(mux, &http2.Server{})
server = &http.Server{Addr: ":3999", Handler: h2Handler}
server.ListenAndServe()
}
```

- Các service sử dụng TLS:

```go
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintln(w, "hello")
})
http.ListenAndServeTLS(port, "server.crt", "server.key",
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
mux.ServeHTTP(w, r)
return
}),
)
}
```

- Kích hoạt một gRPC service với các chứng chỉ riêng (xem phần 4.5.1):

```go
func main() {
creds, err := credentials.NewServerTLSFromFile("server.crt", "server.key")
if err != nil {
log.Fatal(err)
}
grpcServer := grpc.NewServer(grpc.Creds(creds))
...
}
```

Vì gRPC service đã hiện thực phương thức `ServeHTTP` trước đó nên nó có thể được sử dụng làm đối tượng xử lý định tuyến Web (routing). Nếu đặt gRPC và Web service lại với nhau, sẽ dẫn đến xung đột link gRPC và link Web. Chúng ta cần phân biệt giữa hai loại service này khi xử lý.

Việc tạo ra các handler xử lý việc routing hỗ trợ cả Web và gRPC có thể thực hiện như sau:

```go
func main() {
...
http.ListenAndServeTLS(port, "server.crt", "server.key",
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// đảm bảo nếu không phải là HTTP/2 thì sẽ không hỗ trợ gRPC
if r.ProtoMajor != 2 {
mux.ServeHTTP(w, r)
return
}
// nếu header có chỉ định grpc thì thực thi
// lời gọi tương ứng
if strings.Contains(
r.Header.Get("Content-Type"), "application/grpc",
) {
grpcServer.ServeHTTP(w, r)
return
}
// nếu không thì thực thi server HTTP
mux.ServeHTTP(w, r)
return
}),
)
}
```

Bạn đọc có thể xem code chi tiết tại [đây](../examples/ch3/ch3.5/4-with-web-services/main.go).

Theo cách này chúng ta có thể cung cấp cả web serive và gRPC chung port cùng một lúc.
>>>>>>> 76ab621db1f62d8cc10e7bd242125b8be848ab04
89 changes: 89 additions & 0 deletions ch4-web/ch4-01-intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# 4.1. Giới thiệu về Web

Trong lập trình web, đối với các trang web nhỏ và đơn giản (như blog, portfolio, ...) ta sẽ sử dụng template, đối với các trang web phức tạp và phát triển về lâu dài ta sẽ cần chia thành front end, back end riêng biệt để tiện cho các team làm việc độc lập.

Ngày nay có rất nhiều công nghệ hỗ trợ cho phát triển front end, mà nổi bật là Vue, React. Front end server được build để xử lý việc hiển thị giao diện còn các xử lý logic liên quan tới database nó sẽ phải gọi xuống api ở Back end.

Back end cũng có thể chia thành nhiều service nhỏ (để scale được) và cung cấp các api viết bằng gRPC hoặc RESTful, tất cả có thể viết bằng nhiều ngôn ngữ như Java, C#, JS, PHP, Python, Rust, ... nhưng ở đây chúng ta sẽ tìm hiểu về Go.

Phần này sẽ đề cập về cách xây dựng một chương trình web đơn giản bằng thư viện chuẩn của Go, sau đó giới thiệu các framework web trong cộng đồng Open source.

## 4.1.1 Dùng thư viện chuẩn net/http

Gói thư viện [net/http](https://golang.org/pkg/net/http/) đã cung cấp những hàm cơ bản cho việc routing URL, chúng ta sẽ dùng nó để viết một chương trình `http echo server`:

***echo.go:***

```go
package main
// các gói thư viện cần import
import (
"io/ioutil"
"log"
"net/http"
)
// hàm routing echo, gồm hai params
// r *http.Request : dùng để đọc yêu cầu từ client
// wr http.ResponseWriter : dùng để ghi phản hồi về client
func echo(wr http.ResponseWriter, r *http.Request) {
// đọc thông điệp mà client gửi tới trong r.Body
msg, err := ioutil.ReadAll(r.Body)
// phản hồi về client lỗi nếu có
if err != nil {
wr.Write([]byte("echo error"))
return
}
// phản hồi về client chính thông điệp mà client gửi
writeLen, err := wr.Write(msg)
// nếu lỗi xảy ra, hoặc kích thước thông điệp phản hồi khác
// kích thước thông điệp nhận được
if err != nil || writeLen != len(msg) {
log.Println(err, "write len:", writeLen)
}
}
// hàm main của chương trình
func main() {
// mapping url ứng với hàm routing echo
http.HandleFunc("/", echo)
// địa chỉ http://127.0.0.1:8080/
err := http.ListenAndServe(":8080", nil)
// log ra lỗi nếu bị trùng port
if err != nil {
log.Fatal(err)
}
}
```

Kết quả khi chạy chương trình:

```sh
$ go run echo.go &
$ curl http://127.0.0.1:8080/ -d '"Hello, World"'
"Hello, World"
```

## 4.1.2 Dùng thư viện bên ngoài

Bởi vì gói thư viện chuẩn [net/http](https://golang.org/pkg/net/http/) của Golang chỉ hỗ trợ những hàm routing và hàm chức năng cơ bản. Cho nên trong cộng đồng Golang có ý tưởng là viết thêm các thư viện hỗ trợ routing khác ngoài `net/http`.

Thông thường, nếu các dự án routing HTTP của bạn có những đặc điểm sau: [URI](https://vi.wikipedia.org/wiki/URI) cố định, và tham số không truyền thông qua URI, thì nên dùng thư viện chuẩn là đủ. Nhưng với những trường hợp phức tạp hơn, thư viện chuẩn `net/http` vẫn còn thiếu các chức năng hỗ trợ. Ví dụ, xét các route sau:

```sh
GET /card/:id
POST /card/:id
DELETE /card/:id
GET /card/:id/name
GET /card/:id/relations
```

Có thể thấy rằng, cùng là đường dẫn có chứa `/card/:id`, nhưng có phương thức khác nhau hoặc nhánh con khác nhau sẽ dẫn đến logic xử lý khác nhau, cách xử lý những đường dẫn trùng tên như vậy thường sẽ phức tạp. Khi đó chúng ta có thể nghĩ đến việc sử dụng một số framework routing bên ngoài từ cộng đồng Open source.

Framework web của Go có thể được chia thành hai loại sau:

1. Router framework ([HttpRouter](https://github.com/julienschmidt/httprouter), [Gin](https://github.com/gin-gonic/gin), [Gorilla](https://github.com/gorilla/mux),...)

2. MVC class framework ([Revel](https://github.com/revel/revel), [Beego](https://github.com/astaxie/beego), [Iris](https://github.com/kataras/iris),...)

Chúng ta có thể xem thống kê các framwork web phổ biến được dùng trong cộng đồng Golang [ở đây](https://github.com/mingrammer/go-web-framework-stars/blob/master/README.md).

[Tiếp theo](ch4-02-router.md)
8 changes: 4 additions & 4 deletions ch4-web/ch4-06-service-flow-limitation.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 4.6 Service Flow Limit

Một chương trình máy tính có thể mắc phải một số các vấn đề bottleneck (tắt nghẽn):
Một chương trình máy tính có thể mắc phải một số các vấn đề bottleneck (tắc nghẽn):

- Bottleneck do CPU tính toán.
- Bottleneck do băng thông mạng.
Expand Down Expand Up @@ -95,12 +95,12 @@ Kết quả của thử nghiệm là khoảng 70.000 QPS và thời gian phản

Chương trình của chúng ta chưa có chứa logic nghiệp vụ nên có thể dễ dàng đánh giá được QPS như thế, trên thực tế khi gặp một mô-đun có số lượng logic nghiệp vụ lớn và số lượng code lớn, thì vấn đề bottleneck có thể phải trả qua nhiều lần stress test mới có thể phát hiện ra.

Một số hệ thống thường bị bottleneck do mạng, chẳng hạn như dịch vụ CDN và dịch vụ Proxy. Một số là do CPU/GPU, như các dịch vụ xác minh đăng nhập và dịch vụ xử lý hình ảnh. Số khác do truy cập vào disk bị tắt nghẽn như hệ thống lưu trữ, cơ sở dữ liệu. Các nút thắt chương trình khác nhau được phản ánh ở những nơi khác nhau và các dịch vụ đơn chức năng được đề cập ở trên tương đối dễ phân tích. Nếu bạn
Một số hệ thống thường bị bottleneck do mạng, chẳng hạn như dịch vụ CDN và dịch vụ Proxy. Một số là do CPU/GPU, như các dịch vụ xác minh đăng nhập và dịch vụ xử lý hình ảnh. Số khác do truy cập vào disk bị tắc nghẽn như hệ thống lưu trữ, cơ sở dữ liệu. Các nút thắt chương trình khác nhau được phản ánh ở những nơi khác nhau và các dịch vụ đơn chức năng được đề cập ở trên tương đối dễ phân tích.

<div align="center">
<img src="../images/bottleneck.gif" width="400">
<br/>
<span align="center"><i>3 nơi có khả năng tắt nghẽn: disk, CPU, NIC</i></span>
<span align="center"><i>3 nơi có khả năng tắc nghẽn: disk, CPU, NIC</i></span>
<br/>
<br/>
</div>
Expand Down Expand Up @@ -293,7 +293,7 @@ cur = cur > cap ? cap : cur

Chúng ta sử dụng chênh lệch thời gian giữa t1, t2 kết hợp với các tham số ti, k1 thì có thể biết được số lượng token trong bucket trước khi lấy ra token. Về mặt lý thuyết là không cần thiết sử dụng hoạt động điền token vào channel ở ví dụ trước. Miễn là mỗi lần ta đều tính số lượng token trong bucket thì có thể nhận được số lượng token chính xác. Sau khi nhận được số lượng token rồi thì chỉ cần thực hiện những thao tác cần thiết như phép trừ số lượng token. Hãy nhớ sử dụng lock để đảm bảo an toàn với tính concurrency. Thư viện [juju/ratelimit](https://github.com/juju/ratelimit) đang thực hiện theo cách này.

## 4.6.3 Vấn đề tắt nghẽn Service và Quality of Service
## 4.6.3 Vấn đề tắc nghẽn Service và Quality of Service

Trước đây chúng ta đã nói nhiều về việc bottleneck ở CPU, IO và một số loại khác nữa, vấn đề này có thể phát hiện tương đối nhanh chóng từ hầu hết các công ty có monitoring, nếu hệ thống gặp vấn đề về hiệu suất thì quan sát biểu đồ monitor về response là phương án nhanh nhất để phát hiện nguyên nhân.

Expand Down

0 comments on commit cd4d8af

Please sign in to comment.