Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1ef09fb

Browse files
committed
1 parent 2e0fe42 commit 1ef09fb

File tree

3 files changed

+255
-267
lines changed

3 files changed

+255
-267
lines changed

published/tech/20190811-Go-Context-and-Cancellation-by-Propagation.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
[context 包](https://blog.golang.org/context)在 Go 1.7 中引入,它为我们提供了一种在应用程序中处理 context 的方法。这些 context 可以为取消任务或定义超时提供帮助。通过 context 传播请求的值也很有用,但对于本文,我们将重点关注 context 的取消功能。
88

99
## 默认的 contexts
10+
1011
Go 的 `context` 包基于 TODO 或者 Background 来构建 context。
1112

1213
```go
@@ -41,6 +42,7 @@ func (r *Request) Context() context.Context {
4142
如果你在自己的包中工作并且没有任何可用的 context,在这种情况下你应该使用 TODO context。通常,或者如果你对必须使用的 context 有任何疑问,可以使用 TODO context。现在我们知道了主 context,让我们看看它是如何派生子 context 的。
4243

4344
## Contexts 树
45+
4446
父 context 派生出的子 context 会在在其内部结构中创建一个和父 context 之间的联系:
4547

4648
```go
@@ -80,6 +82,7 @@ func (c *cancelCtx) cancel(removeFromParent bool, err error) {
8082
这种取消传播允许我们定义更高级的例子,这些例子可以帮助我们根据主 context 处理多个/繁重的工作。
8183

8284
## 取消传播
85+
8386
让我们通过 Goroutine A 和 B 来展示一个取消的例子,它们将并行运行,因为拥有共同的 context ,当一个发生错误取消时,另外一个也会被取消:
8487

8588
![](https://raw.githubusercontent.com/studygolang/gctt-images/master/context-and-cancellation-by-propagation/image_4.png)
@@ -117,6 +120,7 @@ B - 200ms
117120
我们可以在这里看到 context 对于**多个 Goroutine 是线程安全的**。实际上,有可能是因为我们之前在结构中看到的 mutex,它保证了对 context 的并发安全。
118121

119122
## context 泄漏
123+
120124
正如我们在内部结构中看到的那样,当前 context 在 `Context` 属性中保持其父级的链接,而父级将当前 context 保留在 `children` 属性中。对 cancel 函数的调用将把当前 context 中的子项清除并删除与父项的链接:
121125

122126
```go
@@ -139,6 +143,7 @@ the cancel function returned by context.WithCancel should be called, not discard
139143
```
140144

141145
## 总结
146+
142147
`context` 包还有另外两个利用 cancel 函数的函数:`WithTimeout``WithDeadline`。在定义的超时/截止时间后,它们都会自动触发 cancel 函数。
143148

144149
`context` 包还提供了一个 `WithValue` 的方法,它允许我们在 context 中存储任何对键/值。此功能受到争议,因为它不提供明确的类型控制,可能导致糟糕的编程习惯。如果你想了解 `WithValue` 的更多信息,我建议你阅读[Jack Lindamood 关于 context 值的文章](https://medium.com/@cep21/how-to-correctly-use-context-context-in-go-1-7-8f2c0fafdf39)
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
首发于:https://studygolang.com/articles/35262
2+
3+
# Go 简单而强大的反向代理(Reverse Proxy)
4+
5+
在本文中,我们将了解反向代理,它的应用场景以及如何在 Golang 中实现它。
6+
7+
反向代理是位于 Web 服务器前面并将客户端(例如 Web 浏览器)的请求转发到 Web 服务器的服务器。它们让你可以控制来自客户端的请求和来自服务器的响应,然后我们可以利用这个特点,可以增加缓存、做一些提高网站的安全性措施等。
8+
9+
在我们深入了解有关反向代理之前,让我们快速看普通代理(也称为正向代理)和反向代理之间的区别。
10+
11+
**正向代理**中,代理代表原始客户端从另一个网站检索数据。它位于客户端(浏览器)前面,并确保没有后端服务器直接与客户端通信。所有客户端的请求都通过代理被转发,因此服务器只与这个代理通信(服务器会认为代理是它的客户端)。在这种情况下,代理可以隐藏真正的客户端。
12+
13+
![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20210525-Simple-and-Powerful-ReverseProxy-in-Go/forward-proxy.png)
14+
15+
另一方面,**反向代理**位于后端服务器的前面,确保没有客户端直接与服务器通信。所有客户端请求都会通过反向代理发送到服务器,因此客户端始终只与反向代理通信, 而从不会直接与实际服务器通信。在这种情况下,代理可以隐藏后端服务器。
16+
17+
几个常见的反向代理有 Nginx, HAProxy。
18+
19+
![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20210525-Simple-and-Powerful-ReverseProxy-in-Go/reverse-proxy.png)
20+
21+
## 反向代理使用场景
22+
23+
负载均衡(Load balancing):反向代理可以提供负载均衡解决方案,将传入的流量均匀地分布在不同的服务器之间,以防止单个服务器过载。
24+
25+
防止安全攻击:由于真正的后端服务器永远不需要暴露公共 IP,所以 DDoS 等攻击只能针对反向代理进行,
26+
这能确保在网络攻击中尽量多的保护你的资源,真正的后端服务器始终是安全的。
27+
28+
缓存:假设你的实际服务器与用户所在的地区距离比较远,那么你可以在当地部署反向代理,它可以缓存网站内容并为当地用户提供服务。
29+
30+
SSL 加密:由于与每个客户端的 SSL 通信会耗费大量的计算资源,因此可以使用反向代理处理所有与 SSL 相关的内容,然后释放你真正服务器上的宝贵资源。
31+
32+
## Golang 实现
33+
34+
```go
35+
import (
36+
"log"
37+
"net/http"
38+
"net/http/httputil"
39+
"net/url"
40+
)
41+
42+
// NewProxy takes target host and creates a reverse proxy
43+
// NewProxy 拿到 targetHost 后,创建一个反向代理
44+
func NewProxy(targetHost string) (*httputil.ReverseProxy, error) {
45+
url, err := url.Parse(targetHost)
46+
if err != nil {
47+
return nil, err
48+
}
49+
50+
return httputil.NewSingleHostReverseProxy(url), nil
51+
}
52+
53+
// ProxyRequestHandler handles the http request using proxy
54+
// ProxyRequestHandler 使用 proxy 处理请求
55+
func ProxyRequestHandler(proxy *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
56+
return func(w http.ResponseWriter, r *http.Request) {
57+
proxy.ServeHTTP(w, r)
58+
}
59+
}
60+
61+
func main() {
62+
// initialize a reverse proxy and pass the actual backend server url here
63+
// 初始化反向代理并传入真正后端服务的地址
64+
proxy, err := NewProxy("http://my-api-server.com")
65+
if err != nil {
66+
panic(err)
67+
}
68+
69+
// handle all requests to your server using the proxy
70+
// 使用 proxy 处理所有请求到你的服务
71+
http.HandleFunc("/", ProxyRequestHandler(proxy))
72+
log.Fatal(http.ListenAndServe(":8080", nil))
73+
}
74+
```
75+
76+
是的没错!这就是在 Go 中创建一个简单的反向代理所需的全部内容。我们使用标准库 `net/http/httputil` 创建了一个单主机的反向代理。到达我们代理服务器的任何请求都会被代理到位于 `http://my-api-server.com`。如果你对 Go 比较熟悉,这个代码的实现一目了然。
77+
78+
## 修改响应
79+
80+
`HttpUtil` 反向代理为我们提供了一种非常简单的机制来修改我们从服务器获得的响应,
81+
可以根据你的应用场景来缓存或更改此响应,让我们看看应该如何实现:
82+
83+
```go
84+
// NewProxy takes target host and creates a reverse proxy
85+
func NewProxy(targetHost string) (*httputil.ReverseProxy, error) {
86+
url, err := url.Parse(targetHost)
87+
if err != nil {
88+
return nil, err
89+
}
90+
91+
proxy := httputil.NewSingleHostReverseProxy(url)
92+
proxy.ModifyResponse = modifyResponse()
93+
return proxy, nil
94+
}
95+
96+
func modifyResponse() func(*http.Response) error {
97+
return func(resp *http.Response) error {
98+
resp.Header.Set("X-Proxy", "Magical")
99+
return nil
100+
}
101+
}
102+
103+
```
104+
105+
可以在 `modifyResponse` 方法中看到 ,我们设置了自定义 Header 头。同样,你也可以读取响应体正文,并对其进行更改或缓存,然后将其设置回客户端。
106+
107+
`modifyResponse` 中,可以返回一个错误(如果你在处理响应发生了错误),如果你设置了 `proxy.ErrorHandler`, `modifyResponse` 返回错误时会自动调用 `ErrorHandler` 进行错误处理。
108+
109+
```go
110+
// NewProxy takes target host and creates a reverse proxy
111+
func NewProxy(targetHost string) (*httputil.ReverseProxy, error) {
112+
url, err := url.Parse(targetHost)
113+
if err != nil {
114+
return nil, err
115+
}
116+
117+
proxy := httputil.NewSingleHostReverseProxy(url)
118+
proxy.ModifyResponse = modifyResponse()
119+
proxy.ErrorHandler = errorHandler()
120+
return proxy, nil
121+
}
122+
123+
func errorHandler() func(http.ResponseWriter, *http.Request, error) {
124+
return func(w http.ResponseWriter, req *http.Request, err error) {
125+
fmt.Printf("Got error while modifying response: %v \n", err)
126+
return
127+
}
128+
}
129+
130+
func modifyResponse() func(*http.Response) error {
131+
return func(resp *http.Response) error {
132+
return errors.New("response body is invalid")
133+
}
134+
}
135+
```
136+
137+
## 修改请求
138+
139+
你也可以在将请求发送到服务器之前对其进行修改。在下面的例子中,我们将会在请求发送到服务器之前添加了一个 Header 头。同样的,你可以在请求发送之前对其进行任何更改。
140+
141+
```go
142+
// NewProxy takes target host and creates a reverse proxy
143+
func NewProxy(targetHost string) (*httputil.ReverseProxy, error) {
144+
url, err := url.Parse(targetHost)
145+
if err != nil {
146+
return nil, err
147+
}
148+
149+
proxy := httputil.NewSingleHostReverseProxy(url)
150+
151+
originalDirector := proxy.Director
152+
proxy.Director = func(req *http.Request) {
153+
originalDirector(req)
154+
modifyRequest(req)
155+
}
156+
157+
proxy.ModifyResponse = modifyResponse()
158+
proxy.ErrorHandler = errorHandler()
159+
return proxy, nil
160+
}
161+
162+
func modifyRequest(req *http.Request) {
163+
req.Header.Set("X-Proxy", "Simple-Reverse-Proxy")
164+
}
165+
166+
```
167+
168+
## 完整代码
169+
170+
```go
171+
package main
172+
173+
import (
174+
"errors"
175+
"fmt"
176+
"log"
177+
"net/http"
178+
"net/http/httputil"
179+
"net/url"
180+
)
181+
182+
// NewProxy takes target host and creates a reverse proxy
183+
func NewProxy(targetHost string) (*httputil.ReverseProxy, error) {
184+
url, err := url.Parse(targetHost)
185+
if err != nil {
186+
return nil, err
187+
}
188+
189+
proxy := httputil.NewSingleHostReverseProxy(url)
190+
191+
originalDirector := proxy.Director
192+
proxy.Director = func(req *http.Request) {
193+
originalDirector(req)
194+
modifyRequest(req)
195+
}
196+
197+
proxy.ModifyResponse = modifyResponse()
198+
proxy.ErrorHandler = errorHandler()
199+
return proxy, nil
200+
}
201+
202+
func modifyRequest(req *http.Request) {
203+
req.Header.Set("X-Proxy", "Simple-Reverse-Proxy")
204+
}
205+
206+
func errorHandler() func(http.ResponseWriter, *http.Request, error) {
207+
return func(w http.ResponseWriter, req *http.Request, err error) {
208+
fmt.Printf("Got error while modifying response: %v \n", err)
209+
return
210+
}
211+
}
212+
213+
func modifyResponse() func(*http.Response) error {
214+
return func(resp *http.Response) error {
215+
return errors.New("response body is invalid")
216+
}
217+
}
218+
219+
// ProxyRequestHandler handles the http request using proxy
220+
func ProxyRequestHandler(proxy *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
221+
return func(w http.ResponseWriter, r *http.Request) {
222+
proxy.ServeHTTP(w, r)
223+
}
224+
}
225+
226+
func main() {
227+
// initialize a reverse proxy and pass the actual backend server url here
228+
proxy, err := NewProxy("http://my-api-server.com")
229+
if err != nil {
230+
panic(err)
231+
}
232+
233+
// handle all requests to your server using the proxy
234+
http.HandleFunc("/", ProxyRequestHandler(proxy))
235+
log.Fatal(http.ListenAndServe(":8080", nil))
236+
}
237+
238+
```
239+
240+
反向代理非常强大,如文章之前所说,它有很多应用场景。你可以根据你的情况对其进行自定义。 如果遇到任何问题,我非常乐意为你提供帮助。如果你觉得这篇文章有趣,请分享一下,让更多 Gopher 可以阅读! 非常感谢你的阅读。
241+
242+
---
243+
244+
via: https://blog.joshsoftware.com/2021/05/25/simple-and-powerful-reverseproxy-in-go/
245+
246+
作者:[Anuj Verma](https://blog.joshsoftware.com/author/devanujverma/)
247+
译者:[h1z3y3](https://www.h1z3y3.me)
248+
校对:[polaris1119](https://github.com/polaris1119)
249+
250+
本文由 [GCTT](https://github.com/studygolang/GCTT) 原创编译,[Go 中文网](https://studygolang.com/) 荣誉推出

0 commit comments

Comments
 (0)