/
hystrix_handler.go
129 lines (102 loc) · 3.2 KB
/
hystrix_handler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package main
import (
"errors"
"fmt"
"github.com/afex/hystrix-go/hystrix"
"github.com/hashicorp/consul/api"
"github.com/longjoy/micro-go-book/common/discover"
"github.com/longjoy/micro-go-book/common/loadbalance"
"log"
"net/http"
"net/http/httputil"
"strings"
"sync"
)
var (
ErrNoInstances = errors.New("query service instance error")
)
type HystrixHandler struct {
// 记录hystrix是否已配置
hystrixs map[string]bool
hystrixsMutex *sync.Mutex
discoveryClient discover.DiscoveryClient
loadbalance loadbalance.LoadBalance
logger *log.Logger
}
func NewHystrixHandler(discoveryClient discover.DiscoveryClient, loadbalance loadbalance.LoadBalance, logger *log.Logger) *HystrixHandler {
return &HystrixHandler{
discoveryClient: discoveryClient,
logger: logger,
hystrixs: make(map[string]bool),
loadbalance: loadbalance,
hystrixsMutex: &sync.Mutex{},
}
}
func (hystrixHandler *HystrixHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
reqPath := req.URL.Path
if reqPath == "" {
return
}
//按照分隔符'/'对路径进行分解,获取服务名称serviceName
pathArray := strings.Split(reqPath, "/")
serviceName := pathArray[1]
if serviceName == "" {
// 路径不存在
rw.WriteHeader(404)
return
}
if _, ok := hystrixHandler.hystrixs[serviceName]; !ok {
hystrixHandler.hystrixsMutex.Lock()
if _, ok := hystrixHandler.hystrixs[serviceName]; !ok {
//把serviceName作为 hystrix 命令命名
hystrix.ConfigureCommand(serviceName, hystrix.CommandConfig{
// 进行 hystrix 命令自定义
})
hystrixHandler.hystrixs[serviceName] = true
}
hystrixHandler.hystrixsMutex.Unlock()
}
err := hystrix.Do(serviceName, func() error {
//调用consul api查询serviceName的服务实例列表
instances := hystrixHandler.discoveryClient.DiscoverServices(serviceName, hystrixHandler.logger)
instanceList := make([]*api.AgentService, len(instances))
for i := 0; i < len(instances); i++ {
instanceList[i] = instances[i].(*api.AgentService)
}
// 使用负载均衡算法选取实例
selectInstance, err := hystrixHandler.loadbalance.SelectService(instanceList)
if err != nil {
return ErrNoInstances
}
//创建Director
director := func(req *http.Request) {
//重新组织请求路径,去掉服务名称部分
destPath := strings.Join(pathArray[2:], "/")
hystrixHandler.logger.Println("service id ", selectInstance.ID)
//设置代理服务地址信息
req.URL.Scheme = "http"
req.URL.Host = fmt.Sprintf("%s:%d", selectInstance.Address, selectInstance.Port)
req.URL.Path = "/" + destPath
}
var proxyError error
// 返回代理异常,用于记录 hystrix.Do 执行失败
errorHandler := func(ew http.ResponseWriter, er *http.Request, err error) {
proxyError = err
}
proxy := &httputil.ReverseProxy{
Director: director,
ErrorHandler: errorHandler,
}
proxy.ServeHTTP(rw, req)
// 将执行异常反馈 hystrix
return proxyError
}, func(e error) error {
hystrixHandler.logger.Println("proxy error ", e)
return errors.New("fallback excute")
})
// hystrix.Do 返回执行异常
if err != nil {
rw.WriteHeader(500)
rw.Write([]byte(err.Error()))
}
}