Skip to content

Commit ce3e155

Browse files
authored
使用http做地址探测 (#73)
* 使用http做地址探测
1 parent f30abdb commit ce3e155

File tree

4 files changed

+101
-32
lines changed

4 files changed

+101
-32
lines changed

Diff for: .github/workflows/db-ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
uses: actions/checkout@v1
3434
- name: UT for etcd
3535
run: |
36-
time docker run -d -p 2379:2379 --name etcd quay.io/coreos/etcd etcd -name etcd --advertise-client-urls http://0.0.0.0:2379 --listen-client-urls http://0.0.0.0:2379
36+
time docker run -d -p 2379:2379 --name etcd quay.io/coreos/etcd:v3.5.15 etcd -name etcd --advertise-client-urls http://0.0.0.0:2379 --listen-client-urls http://0.0.0.0:2379
3737
while ! nc -z 127.0.0.1 2379; do
3838
sleep 1
3939
done

Diff for: .gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@
1212
*.out
1313

1414
# Dependency directories (remove the comment below to include it)
15-
# vendor/
15+
vendor/
1616
.idea

Diff for: addresspool/pool.go

+77-20
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
package addresspool
22

33
import (
4+
"context"
45
"fmt"
6+
"io"
57
"net"
8+
"net/http"
69
"os"
710
"strconv"
811
"sync"
912
"time"
1013

14+
"github.com/go-chassis/foundation/httpclient"
1115
"github.com/go-chassis/openlog"
1216

1317
"github.com/go-chassis/cari/discovery"
@@ -24,6 +28,16 @@ const (
2428
healthProbeTimeout = time.Second
2529
)
2630

31+
type HttpProbeOptions struct {
32+
Client *httpclient.Requests
33+
Protocol string
34+
Path string
35+
}
36+
37+
type Options struct {
38+
HttpProbeOptions *HttpProbeOptions // used in available check if set, tcp will be used if not set
39+
}
40+
2741
// Pool cloud server address pool
2842
type Pool struct {
2943
mutex sync.RWMutex
@@ -33,10 +47,11 @@ type Pool struct {
3347
sameAzAddress []string
3448
diffAzAddress []string
3549

36-
status map[string]string
37-
onceMonitor sync.Once
38-
quit chan struct{}
39-
onceQuit sync.Once
50+
status map[string]string
51+
onceMonitor sync.Once
52+
quit chan struct{}
53+
onceQuit sync.Once
54+
httpProbeOptions *HttpProbeOptions
4055
}
4156

4257
func (p *Pool) Close() {
@@ -46,23 +61,21 @@ func (p *Pool) Close() {
4661
}
4762

4863
// NewPool Get registry pool instance
49-
func NewPool(addresses []string) *Pool {
64+
func NewPool(addresses []string, opts ...Options) *Pool {
5065
p := &Pool{
5166
defaultAddress: removeDuplicates(addresses),
5267
status: make(map[string]string),
5368
}
54-
p.appendAddressToStatus(addresses)
55-
p.monitor()
56-
return p
57-
}
5869

59-
func (p *Pool) appendAddressToStatus(addresses []string) {
60-
for _, v := range addresses {
61-
if _, ok := p.status[v]; ok {
62-
continue
70+
if len(opts) > 0 && opts[0].HttpProbeOptions != nil {
71+
p.httpProbeOptions = opts[0].HttpProbeOptions
72+
if p.httpProbeOptions.Client == nil {
73+
openlog.Info(fmt.Sprintf("http client nil, make one with default options"))
74+
p.httpProbeOptions.Client, _ = httpclient.New(nil)
6375
}
64-
p.status[v] = statusAvailable
6576
}
77+
p.monitor()
78+
return p
6679
}
6780

6881
func (p *Pool) ResetAddress(addresses []string) {
@@ -72,7 +85,6 @@ func (p *Pool) ResetAddress(addresses []string) {
7285
p.diffAzAddress = []string{}
7386
p.sameAzAddress = []string{}
7487
p.status = make(map[string]string)
75-
p.appendAddressToStatus(addresses)
7688
}
7789

7890
func (p *Pool) SetAddressByInstances(instances []*discovery.MicroServiceInstance) error {
@@ -88,12 +100,10 @@ func (p *Pool) SetAddressByInstances(instances []*discovery.MicroServiceInstance
88100
uniqueAddrList := removeDuplicates(addrList)
89101
if p.isSameAzAddr(uniqueAddrList) {
90102
p.sameAzAddress = uniqueAddrList
91-
p.appendAddressToStatus(uniqueAddrList)
92103
openlog.Info(fmt.Sprintf("sync same az endpoints: %s", uniqueAddrList))
93104
continue
94105
}
95106
p.diffAzAddress = uniqueAddrList
96-
p.appendAddressToStatus(uniqueAddrList)
97107
openlog.Info(fmt.Sprintf("sync different az endpoints: %s", addrList))
98108
}
99109
return nil
@@ -167,13 +177,12 @@ func (p *Pool) checkConnectivity() {
167177
if _, exist := status[v]; exist {
168178
continue
169179
}
170-
conn, err := net.DialTimeout("tcp", v, healthProbeTimeout)
180+
err := p.doCheckConnectivity(v)
171181
if err != nil {
172-
openlog.Error("connectivity unavailable: " + v)
182+
openlog.Error(fmt.Sprintf("%s connectivity unavailable: %s", v, err))
173183
status[v] = statusUnavailable
174184
} else {
175185
status[v] = statusAvailable
176-
conn.Close()
177186
}
178187
}
179188

@@ -182,6 +191,54 @@ func (p *Pool) checkConnectivity() {
182191
p.mutex.Unlock()
183192
}
184193

194+
func (p *Pool) doCheckConnectivity(endpoint string) error {
195+
if p.httpProbeOptions != nil {
196+
return p.doCheckConnectivityWithHttp(endpoint)
197+
}
198+
199+
return p.doCheckConnectivityWithTcp(endpoint)
200+
}
201+
202+
func (p *Pool) doCheckConnectivityWithTcp(endpoint string) error {
203+
conn, err := net.DialTimeout("tcp", endpoint, healthProbeTimeout)
204+
if err != nil {
205+
return err
206+
}
207+
208+
err = conn.Close()
209+
if err != nil {
210+
openlog.Error(fmt.Sprintf("close conn failed when check connectivity: %s", err))
211+
}
212+
213+
return nil
214+
}
215+
216+
func (p *Pool) doCheckConnectivityWithHttp(endpoint string) error {
217+
u := p.httpProbeOptions.Protocol + "://" + endpoint + p.httpProbeOptions.Path
218+
resp, err := p.httpProbeOptions.Client.Get(context.Background(), u, nil)
219+
if err != nil {
220+
return err
221+
}
222+
223+
if resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices {
224+
return nil
225+
}
226+
// do tcp check if api not exist, ensure to compatible with old scenes
227+
if resp.StatusCode == http.StatusNotFound {
228+
return p.doCheckConnectivityWithTcp(endpoint)
229+
}
230+
231+
body, err := io.ReadAll(resp.Body)
232+
if err != nil {
233+
return fmt.Errorf("http status: %s, read resp error: %s", resp.Status, err)
234+
}
235+
err = resp.Body.Close()
236+
if err != nil {
237+
openlog.Error(fmt.Sprintf("close http resp.Body failed when check connectivity: %s", err))
238+
}
239+
return fmt.Errorf("http status: %s, resp: %s", resp.Status, string(body))
240+
}
241+
185242
func (p *Pool) monitor() {
186243
p.onceMonitor.Do(func() {
187244
var interval time.Duration

Diff for: addresspool/pool_test.go

+22-10
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,28 @@ import (
1313
)
1414

1515
func TestNewPool(t *testing.T) {
16+
mockHttpServer := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
17+
writer.WriteHeader(http.StatusOK)
18+
return
19+
}))
20+
1621
os.Setenv("CHASSIS_SC_HEALTH_CHECK_INTERVAL", "1")
1722

18-
defaultAddr := "127.0.0.1:30000"
23+
defaultAddr := mockHttpServer.Listener.Addr().String()
1924
// can get an address
2025
pool := NewPool([]string{defaultAddr})
2126
addr := pool.GetAvailableAddress()
2227
assert.Equal(t, defaultAddr, addr)
2328

2429
// check monitor started
30+
assert.NotEqual(t, statusAvailable, pool.status[defaultAddr]) // unavailable by default
31+
32+
time.Sleep(2*time.Second + 100*time.Millisecond)
2533
assert.Equal(t, statusAvailable, pool.status[defaultAddr])
34+
35+
mockHttpServer.Close()
2636
time.Sleep(2*time.Second + 100*time.Millisecond)
27-
assert.Equal(t, statusUnavailable, pool.status[defaultAddr]) // the status should be unavailable after check interval
37+
assert.NotEqual(t, statusAvailable, pool.status[defaultAddr]) // the status should be unavailable again
2838
}
2939

3040
func TestAddressPool_GetAvailableAddress_priority(t *testing.T) {
@@ -49,21 +59,27 @@ func TestAddressPool_GetAvailableAddress_priority(t *testing.T) {
4959
p.defaultAddress = []string{defaultAddr}
5060
p.sameAzAddress = []string{sameAzAddr}
5161
p.diffAzAddress = []string{diffAzAddr}
52-
p.appendAddressToStatus([]string{defaultAddr, sameAzAddr, diffAzAddr})
62+
p.status[sameAzAddr] = statusAvailable
63+
p.status[diffAzAddr] = statusAvailable
64+
p.status[defaultAddr] = statusAvailable
5365
},
5466
want: sameAzAddr,
5567
},
5668
{
5769
name: "diff az address available, return diff az address",
5870
preDo: func() {
5971
p.status[sameAzAddr] = statusUnavailable
72+
p.status[diffAzAddr] = statusAvailable
73+
p.status[defaultAddr] = statusAvailable
6074
},
6175
want: diffAzAddr,
6276
},
6377
{
6478
name: "same az/diff az address unavailable, return default address",
6579
preDo: func() {
80+
p.status[sameAzAddr] = statusUnavailable
6681
p.status[diffAzAddr] = statusUnavailable
82+
p.status[defaultAddr] = statusAvailable
6783
},
6884
want: defaultAddr,
6985
},
@@ -144,10 +160,6 @@ func TestAddressPool_SetAddressByInstances(t *testing.T) {
144160
assert.NoError(t, err)
145161
assert.Equal(t, []string{"192.168.2.1:30100", "192.168.2.2:30100"}, p.sameAzAddress)
146162
assert.Equal(t, []string{"192.168.1.1:30100", "192.168.1.2:30100"}, p.diffAzAddress)
147-
assert.Equal(t, statusAvailable, p.status["192.168.1.1:30100"])
148-
assert.Equal(t, statusAvailable, p.status["192.168.1.2:30100"])
149-
assert.Equal(t, statusAvailable, p.status["192.168.2.1:30100"])
150-
assert.Equal(t, statusAvailable, p.status["192.168.2.2:30100"])
151163
}
152164

153165
func TestAddressPool_checkConnectivity(t *testing.T) {
@@ -164,9 +176,9 @@ func TestAddressPool_checkConnectivity(t *testing.T) {
164176
// init, all address is available
165177
defaultAddr := "127.0.0.1:30000"
166178
p := NewPool([]string{defaultAddr, server1Addr, server2Addr})
167-
assert.Equal(t, statusAvailable, p.status[defaultAddr])
168-
assert.Equal(t, statusAvailable, p.status[server1Addr])
169-
assert.Equal(t, statusAvailable, p.status[server2Addr])
179+
assert.NotEqual(t, statusAvailable, p.status[defaultAddr])
180+
assert.NotEqual(t, statusAvailable, p.status[server1Addr])
181+
assert.NotEqual(t, statusAvailable, p.status[server2Addr])
170182

171183
// check connectivity, default address status should be unavailable, as it is fake
172184
p.checkConnectivity()

0 commit comments

Comments
 (0)