这篇讲另两个文件http.go、http_server.go,这两个文件和第六讲go语言nsq源码解读六 tcp.go、tcp_server.go里的两个文件是相对应的。那两个文件用于处理tcp请求,而这两个是处理http请求的。

http_sesrver.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
package util

import (
    "log"
    "net"
    "net/http"
    "strings"
)

//创建httpServer,并注册了处理函数,处理函数在nsqlookupd里指定到了http.go里的httpServer类型,其有一个方法ServeHTTP
func HTTPServer(listener net.Listener, handler http.Handler) {
    log.Printf("HTTP: listening on %s", listener.Addr().String())

    server := &http.Server{
        Handler: handler,
    }
    err := server.Serve(listener)
    // theres no direct way to detect this error because it is not exposed
    if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
        log.Printf("ERROR: http.Serve() - %s", err.Error())
    }

    log.Printf("HTTP: closing %s", listener.Addr().String())
}

http.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
package nsqlookupd

import (
    "fmt"
    "io"
    "log"
    "net/http"

    "github.com/bitly/go-nsq"
    "github.com/bitly/nsq/util"
)

type httpServer struct {
    context *Context
}

//根据URL进行请求分发,不同的URL,调用不同的方法来处理,如果路径不合法,则返回404
func (s *httpServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    switch req.URL.Path {
    case "/ping":
        s.pingHandler(w, req)
    case "/info":
        s.infoHandler(w, req)
    case "/lookup":
        s.lookupHandler(w, req)
    case "/topics":
        s.topicsHandler(w, req)
    case "/channels":
        s.channelsHandler(w, req)
    case "/nodes":
        s.nodesHandler(w, req)
    case "/delete_topic":
        s.deleteTopicHandler(w, req)
    case "/delete_channel":
        s.deleteChannelHandler(w, req)
    case "/tombstone_topic_producer":
        s.tombstoneTopicProducerHandler(w, req)
    case "/create_topic":
        s.createTopicHandler(w, req)
    case "/create_channel":
        s.createChannelHandler(w, req)
    case "/debug":
        s.debugHandler(w, req)
    default:
        util.ApiResponse(w, 404, "NOT_FOUND", nil)
    }
}

//向ping请求返回ok
func (s *httpServer) pingHandler(w http.ResponseWriter, req *http.Request) {
    w.Header().Set("Content-Length", "2")
    io.WriteString(w, "OK")
}

//查询所有的topic
func (s *httpServer) topicsHandler(w http.ResponseWriter, req *http.Request) {
    topics := s.context.nsqlookupd.DB.FindRegistrations("topic", "*", "").Keys()
    data := make(map[string]interface{})
    data["topics"] = topics
    util.ApiResponse(w, 200, "OK", data)
}

//获取符合指定topic的channel,URL中需有名为topic的参数
func (s *httpServer) channelsHandler(w http.ResponseWriter, req *http.Request) {
    reqParams, err := util.NewReqParams(req)
    if err != nil {
        util.ApiResponse(w, 500, "INVALID_REQUEST", nil)
        return
    }

    topicName, err := reqParams.Get("topic")
    if err != nil {
        util.ApiResponse(w, 500, "MISSING_ARG_TOPIC", nil)
        return
    }

    channels := s.context.nsqlookupd.DB.FindRegistrations("channel", topicName, "*").SubKeys()
    data := make(map[string]interface{})
    data["channels"] = channels
    util.ApiResponse(w, 200, "OK", data)
}

//查询服务,
func (s *httpServer) lookupHandler(w http.ResponseWriter, req *http.Request) {
    reqParams, err := util.NewReqParams(req)
    if err != nil {
        util.ApiResponse(w, 500, "INVALID_REQUEST", nil)
        return
    }

    topicName, err := reqParams.Get("topic")
    if err != nil {
        util.ApiResponse(w, 500, "MISSING_ARG_TOPIC", nil)
        return
    }

    registration := s.context.nsqlookupd.DB.FindRegistrations("topic", topicName, "")

    if len(registration) == 0 {
        util.ApiResponse(w, 500, "INVALID_ARG_TOPIC", nil)
        return
    }

    //在DB里根据条件查询
    channels := s.context.nsqlookupd.DB.FindRegistrations("channel", topicName, "*").SubKeys()
    producers := s.context.nsqlookupd.DB.FindProducers("topic", topicName, "")
    producers = producers.FilterByActive(s.context.nsqlookupd.options.InactiveProducerTimeout,
        s.context.nsqlookupd.options.TombstoneLifetime)
    data := make(map[string]interface{})

    //查询到channels和producers,以JSON格式返回
    data["channels"] = channels
    data["producers"] = producers.PeerInfo()

    util.ApiResponse(w, 200, "OK", data)
}

//创建一个topic
func (s *httpServer) createTopicHandler(w http.ResponseWriter, req *http.Request) {
    reqParams, err := util.NewReqParams(req)
    if err != nil {
        util.ApiResponse(w, 500, "INVALID_REQUEST", nil)
        return
    }

    topicName, err := reqParams.Get("topic")
    if err != nil {
        util.ApiResponse(w, 500, "MISSING_ARG_TOPIC", nil)
        return
    }

    if !nsq.IsValidTopicName(topicName) {
        util.ApiResponse(w, 500, "INVALID_TOPIC", nil)
        return
    }

    //Registration使用的category为topic
    log.Printf("DB: adding topic(%s)", topicName)
    key := Registration{"topic", topicName, ""}
    s.context.nsqlookupd.DB.AddRegistration(key)

    util.ApiResponse(w, 200, "OK", nil)
}

//删除topic,逻辑比较简单,从URL中取到"topic"参数的值,然后查询registrations并删除
func (s *httpServer) deleteTopicHandler(w http.ResponseWriter, req *http.Request) {
    reqParams, err := util.NewReqParams(req)
    if err != nil {
        util.ApiResponse(w, 500, "INVALID_REQUEST", nil)
        return
    }

    topicName, err := reqParams.Get("topic")
    if err != nil {
        util.ApiResponse(w, 500, "MISSING_ARG_TOPIC", nil)
        return
    }

    //category为channel的Registration
    registrations := s.context.nsqlookupd.DB.FindRegistrations("channel", topicName, "*")
    for _, registration := range registrations {
        log.Printf("DB: removing channel(%s) from topic(%s)", registration.SubKey, topicName)
        s.context.nsqlookupd.DB.RemoveRegistration(registration)
    }

    //category为topic的Registration
    registrations = s.context.nsqlookupd.DB.FindRegistrations("topic", topicName, "")
    for _, registration := range registrations {
        log.Printf("DB: removing topic(%s)", topicName)
        s.context.nsqlookupd.DB.RemoveRegistration(registration)
    }

    util.ApiResponse(w, 200, "OK", nil)
}

//将指定的Producer标记为墓碑状态
func (s *httpServer) tombstoneTopicProducerHandler(w http.ResponseWriter, req *http.Request) {
    reqParams, err := util.NewReqParams(req)
    if err != nil {
        util.ApiResponse(w, 500, "INVALID_REQUEST", nil)
        return
    }

    topicName, err := reqParams.Get("topic")
    if err != nil {
        util.ApiResponse(w, 500, "MISSING_ARG_TOPIC", nil)
        return
    }
    //获取node参数的值
    node, err := reqParams.Get("node")
    if err != nil {
        util.ApiResponse(w, 500, "MISSING_ARG_NODE", nil)
        return
    }

    log.Printf("DB: setting tombstone for producer@%s of topic(%s)", node, topicName)
    producers := s.context.nsqlookupd.DB.FindProducers("topic", topicName, "")
    for _, p := range producers {
        //从这句可看出,URL中node参数的值为"BroadcastAddress:HttpPort"的格式
        thisNode := fmt.Sprintf("%s:%d", p.peerInfo.BroadcastAddress, p.peerInfo.HttpPort)

        //将指定node标记为墓碑状态
        if thisNode == node {
            p.Tombstone()
        }
    }

    util.ApiResponse(w, 200, "OK", nil)
}

//创建channel
func (s *httpServer) createChannelHandler(w http.ResponseWriter, req *http.Request) {
    reqParams, err := util.NewReqParams(req)
    if err != nil {
        util.ApiResponse(w, 500, "INVALID_REQUEST", nil)
        return
    }

    topicName, channelName, err := util.GetTopicChannelArgs(reqParams)
    if err != nil {
        util.ApiResponse(w, 500, err.Error(), nil)
        return
    }

    log.Printf("DB: adding channel(%s) in topic(%s)", channelName, topicName)

    //用"channel"作为category,定义Registration类型的变量并加入DB
    key := Registration{"channel", topicName, channelName}
    s.context.nsqlookupd.DB.AddRegistration(key)

    //用"topic"作为category,定义Registration类型的变量并加入DB
    log.Printf("DB: adding topic(%s)", topicName)
    key = Registration{"topic", topicName, ""}
    s.context.nsqlookupd.DB.AddRegistration(key)

    util.ApiResponse(w, 200, "OK", nil)
}

//根据参数中的topicName和channelName获取所有Registration并删除
func (s *httpServer) deleteChannelHandler(w http.ResponseWriter, req *http.Request) {
    reqParams, err := util.NewReqParams(req)
    if err != nil {
        util.ApiResponse(w, 500, "INVALID_REQUEST", nil)
        return
    }

    topicName, channelName, err := util.GetTopicChannelArgs(reqParams)
    if err != nil {
        util.ApiResponse(w, 500, err.Error(), nil)
        return
    }

    //根据参数查找Registration
    registrations := s.context.nsqlookupd.DB.FindRegistrations("channel", topicName, channelName)
    if len(registrations) == 0 {
        util.ApiResponse(w, 404, "NOT_FOUND", nil)
        return
    }

    //删除查找到的所有Registration
    log.Printf("DB: removing channel(%s) from topic(%s)", channelName, topicName)
    for _, registration := range registrations {
        s.context.nsqlookupd.DB.RemoveRegistration(registration)
    }

    util.ApiResponse(w, 200, "OK", nil)
}

// note: we can't embed the *Producer here because embeded objects are ignored for json marshalling
type node struct {
    RemoteAddress    string   `json:"remote_address"`
    Hostname         string   `json:"hostname"`
    BroadcastAddress string   `json:"broadcast_address"`
    TcpPort          int      `json:"tcp_port"`
    HttpPort         int      `json:"http_port"`
    Version          string   `json:"version"`
    Tombstones       []bool   `json:"tombstones"`
    Topics           []string `json:"topics"`
}

func (s *httpServer) nodesHandler(w http.ResponseWriter, req *http.Request) {
    // dont filter out tombstoned nodes
    //FilterByActive第二个参数设为0,标记为tombstoned的节点并未被过滤
    producers := s.context.nsqlookupd.DB.FindProducers("client", "", "").FilterByActive(
        s.context.nsqlookupd.options.InactiveProducerTimeout, 0)
    nodes := make([]*node, len(producers))
    for i, p := range producers {
        topics := s.context.nsqlookupd.DB.LookupRegistrations(p.peerInfo.id).Filter("topic", "*", "").Keys()

        // for each topic find the producer that matches this peer
        // to add tombstone information
        tombstones := make([]bool, len(topics))
        for j, t := range topics {
            topicProducers := s.context.nsqlookupd.DB.FindProducers("topic", t, "")
            for _, tp := range topicProducers {
                if tp.peerInfo == p.peerInfo {
                    tombstones[j] = tp.IsTombstoned(s.context.nsqlookupd.options.TombstoneLifetime)
                }
            }
        }

        nodes[i] = &node{
            RemoteAddress:    p.peerInfo.RemoteAddress,
            Hostname:         p.peerInfo.Hostname,
            BroadcastAddress: p.peerInfo.BroadcastAddress,
            TcpPort:          p.peerInfo.TcpPort,
            HttpPort:         p.peerInfo.HttpPort,
            Version:          p.peerInfo.Version,
            Tombstones:       tombstones,
            Topics:           topics,
        }
    }

    data := make(map[string]interface{})
    data["producers"] = nodes
    util.ApiResponse(w, 200, "OK", data)
}

//返回版本号
func (s *httpServer) infoHandler(w http.ResponseWriter, req *http.Request) {
    util.ApiResponse(w, 200, "OK", struct {
        Version string `json:"version"`
    }{
        Version: util.BINARY_VERSION,
    })
}

//用于调试,会返回RegistrationDB中所有的producer
func (s *httpServer) debugHandler(w http.ResponseWriter, req *http.Request) {
    s.context.nsqlookupd.DB.RLock()
    defer s.context.nsqlookupd.DB.RUnlock()

    data := make(map[string][]map[string]interface{})
    //遍历Map
    for r, producers := range s.context.nsqlookupd.DB.registrationMap {
        key := r.Category + ":" + r.Key + ":" + r.SubKey
        data[key] = make([]map[string]interface{}, 0)
        //遍历registration下所有的producers
        for _, p := range producers {
            m := make(map[string]interface{})
            m["id"] = p.peerInfo.id
            m["hostname"] = p.peerInfo.Hostname
            m["broadcast_address"] = p.peerInfo.BroadcastAddress
            m["tcp_port"] = p.peerInfo.TcpPort
            m["http_port"] = p.peerInfo.HttpPort
            m["version"] = p.peerInfo.Version
            m["last_update"] = p.peerInfo.lastUpdate.UnixNano()
            m["tombstoned"] = p.tombstoned
            m["tombstoned_at"] = p.tombstonedAt.UnixNano()
            data[key] = append(data[key], m)
        }
    }

    util.ApiResponse(w, 200, "OK", data)
}

由于http.go里多处用到util.ApiResponse方法,这个方法定义在util/api_response.go文件里,所以这里简单解释一下这个文件:
api_response.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
package util

import (
    "encoding/json"
    "fmt"
    "net/http"
    "strconv"
)

//将数据以JSON格式返回给客户端,在nsqlookupd里,主要是在给nsqadmin提供的接口里使用。
//nsqadmin通过HTTP的方式获取nsqlookupd用ApiResponse函数返回的JSON数据,解析,然后显示在页面上。
func ApiResponse(w http.ResponseWriter, statusCode int, statusTxt string, data interface{}) {

    //将数据转化为JSON格式输出
    response, err := json.Marshal(struct {
        StatusCode int         `json:"status_code"`
        StatusTxt  string      `json:"status_txt"`
        Data       interface{} `json:"data"`
    }{
        statusCode,
        statusTxt,
        data,
    })

    //转json字符串出错时返回500错误
    if err != nil {
        response = []byte(fmt.Sprintf(`{"status_code":500, "status_txt":"%s", "data":null}`, err.Error()))
    }

    w.Header().Set("Content-Type", "application/json; charset=utf-8")
    w.Header().Set("Content-Length", strconv.Itoa(len(response)))
    w.WriteHeader(statusCode)
    w.Write(response)
}

go语言nsq源码解读八 http.go、http_server.go的更多相关文章

  1. (转)go语言nsq源码解读二 nsqlookupd、nsqd与nsqadmin

    转自:http://www.baiyuxiong.com/?p=886 ---------------------------------------------------------------- ...

  2. go语言 nsq源码解读三 nsqlookupd源码nsqlookupd.go

    从本节开始,将逐步阅读nsq各模块的代码. 读一份代码,我的思路一般是: 1.了解用法,知道了怎么使用,对理解代码有宏观上有很大帮助. 2.了解各大模块的功能特点,同时再想想,如果让自己来实现这些模块 ...

  3. go语言nsq源码解读七 lookup_protocol_v1.go

    本篇将解读nsqlookup处理tcp请求的核心代码文件lookup_protocol_v1.go. 1234567891011121314151617181920212223242526272829 ...

  4. go语言nsq源码解读一-基本介绍

    简单介绍一下nsq. 参考 http://feilong.me/2013/05/nsq-realtime-message-processing-system 的介绍:NSQ是由知名短链接服务商bitl ...

  5. go语言nsq源码解读二 nsqlookupd、nsqd与nsqadmin

    nsqlookupd: 官方文档解释见:http://bitly.github.io/nsq/components/nsqlookupd.html 用官方话来讲是:nsqlookupd管理拓扑信息,客 ...

  6. go语言nsq源码解读九 tcp和http中channel、topic的增删

    通过前面多篇文章,nsqlookupd基本已经解读完毕了,不过在关于channel和topic的增删上还比较模糊,所以本篇将站在宏观的角度来总结一下,tcp.go和http.go两个文件中关于chan ...

  7. go语言nsq源码解读六 tcp.go、tcp_server.go

    本篇讲nsqlookupd中tcp.go.tcp_server.go tcp_server.go位于util目录下. 12345678910111213141516171819202122232425 ...

  8. go语言 nsq源码解读四 nsqlookupd源码options.go、context.go和wait_group_wrapper.go

    本节会解读nsqlookupd.go文件中涉及到的其中三个文件:options.go.context.go和wait_group_wrapper.go. options.go 123456789101 ...

  9. go语言nsq源码解读五 nsqlookupd源码registration_db.go

    本篇将讲解registration_db.go文件. 1234567891011121314151617181920212223242526272829303132333435363738394041 ...

随机推荐

  1. Cocoa编程之IBAction和IBOutlet含义

    IBAction / IBOutlet / IBOutletCollection 时间 2014-05-05 15:00:00  NSHipster原文  http://nshipster.com/i ...

  2. C# / VB.NET合并PDF指定页

    在前面的文章中,我们已经知道如何合并.拆分多个PDF文件,在这篇文章中的合并.拆分PDF文档主要是以方便文档管理的目的来操作文档,在文档查阅.管理及存储上很方便实用.但是我们如果想要合并多个文档中的部 ...

  3. 排序算法入门之堆排序(Java实现)

    堆排序 在学习了二叉堆(优先队列)以后,我们来看看堆排序.堆排序总的运行时间为O(NlonN). 堆的概念 堆是以数组作为存储结构. 可以看出,它们满足以下规律: 设当前元素在数组中以R[i]表示,那 ...

  4. JAVA物联网九大核心热点技术

    1. Unix/Linux平台技术: Unix系统原理.Unix常用命令.Shell编程.  2. Java EE核心技术: Java语言核心.Java高级API.JVM及性能优化.Java Secu ...

  5. Strom topology 设计的演进

    场景:采集日志数据,日志数据有多个字段组成,需求是根据日志数据中的N个字段(维度),去统计指标数据(个数.平均值)等.

  6. Open Source BI Platform List

    资源入口: awesome-business-intelligence https://github.com/thenaturalist/awesome-business-intelligence h ...

  7. Java内部抽象类的匿名类初始化

    说在前面的话,以前写过一次这个变态代码,后来重构,把那个new的语法简化了,最近又要整,差点都想不起来了,留个文档把 1.下面这个案例更变态,抽象类还有一个个泛型类:首先内部抽象类的定义: /* * ...

  8. Java自学教程视频

    BAT大咖助力 全面升级Android面试 BAT大牛亲授 基于ElasticSearch的搜房网实战 从天气项目看Spring Cloud微服务治理 Java企业级电商项目架构演进之路  Tomca ...

  9. 快速开发框架,及库存管理系统,基于easyui框架和C#语言MVC、EntityFrameWork、T4模板技术。

    快速开发框架,及库存管理系统,基于easyui框架和C#语言MVC.EntityFrameWork.T4模板技术. 产品界面如下图所示: 源码结构: 开放全部源码,如有需要请联系,QQ:1107141 ...

  10. hive------ Group by、join、distinct等实现原理

    1. Hive 的 distribute by Order by 能够预期产生完全排序的结果,但是它是通过只用一个reduce来做到这点的.所以对于大规模的数据集它的效率非常低.在很多情况下,并不需要 ...