go语言nsq源码解读八 http.go、http_server.go
这篇讲另两个文件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的更多相关文章
- (转)go语言nsq源码解读二 nsqlookupd、nsqd与nsqadmin
转自:http://www.baiyuxiong.com/?p=886 ---------------------------------------------------------------- ...
- go语言 nsq源码解读三 nsqlookupd源码nsqlookupd.go
从本节开始,将逐步阅读nsq各模块的代码. 读一份代码,我的思路一般是: 1.了解用法,知道了怎么使用,对理解代码有宏观上有很大帮助. 2.了解各大模块的功能特点,同时再想想,如果让自己来实现这些模块 ...
- go语言nsq源码解读七 lookup_protocol_v1.go
本篇将解读nsqlookup处理tcp请求的核心代码文件lookup_protocol_v1.go. 1234567891011121314151617181920212223242526272829 ...
- go语言nsq源码解读一-基本介绍
简单介绍一下nsq. 参考 http://feilong.me/2013/05/nsq-realtime-message-processing-system 的介绍:NSQ是由知名短链接服务商bitl ...
- go语言nsq源码解读二 nsqlookupd、nsqd与nsqadmin
nsqlookupd: 官方文档解释见:http://bitly.github.io/nsq/components/nsqlookupd.html 用官方话来讲是:nsqlookupd管理拓扑信息,客 ...
- go语言nsq源码解读九 tcp和http中channel、topic的增删
通过前面多篇文章,nsqlookupd基本已经解读完毕了,不过在关于channel和topic的增删上还比较模糊,所以本篇将站在宏观的角度来总结一下,tcp.go和http.go两个文件中关于chan ...
- go语言nsq源码解读六 tcp.go、tcp_server.go
本篇讲nsqlookupd中tcp.go.tcp_server.go tcp_server.go位于util目录下. 12345678910111213141516171819202122232425 ...
- go语言 nsq源码解读四 nsqlookupd源码options.go、context.go和wait_group_wrapper.go
本节会解读nsqlookupd.go文件中涉及到的其中三个文件:options.go.context.go和wait_group_wrapper.go. options.go 123456789101 ...
- go语言nsq源码解读五 nsqlookupd源码registration_db.go
本篇将讲解registration_db.go文件. 1234567891011121314151617181920212223242526272829303132333435363738394041 ...
随机推荐
- Course1-Python数据类型总结
一. 开始正式学习前的tips: Python和其他语言有很多类似, 也有一些差异, 下面先总结了一些基本语法上的注意事项 1. 注意缩进 2. 一行语句分为多行显示: \ 3. 注释: 单行注释#, ...
- Mac电脑配置Apache服务器详细说明
Mac电脑服务器配置过程,无论是个人学习,还是公司测试都非常实用,流程精简易懂,用于让Mac电脑做服务器方便做网络数据请求的测试. 第一步:定位到 Apache2 目录 $ cd /etc/Apach ...
- RunTime运行时在iOS中的应用之UITextField占位符placeholder
RunTime运行时机制 runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API. 在我们平时编写的Objective-C代码中, 程序运行过程时, 其实最终 ...
- CKEditor5 + ckfinder3(php)
CKEditor5资源下载,这里我们选择ckeditor5-build-classic下载: https://ckeditor.com/ckeditor-5-builds/download/ ckfi ...
- VueJs(8)---组件(注册组件)
组件(注册组件) 一.介绍 组件系统是Vue.js其中一个重要的概念,它提供了一种抽象,让我们可以使用独立可复用的小组件来构建大型应用,任意类型的应用界面都可以抽象为一个组件树 那么什么是组件呢? 组 ...
- spring mvc和spring的区别
springmvc只是spring其中的一部分. spring 可以 支持 hibernate ,ibatis ,JMS,JDBC 支持事务管理, 注解功能,表达式语言,测试 springmvc 就是 ...
- 北京一家JAVA开发公司面试题(留给后人)
1.jsp有哪些内置对象?作用分别是什么? 2.描述一下servlet的生命周期和基本架构. 3.多线程有几种实现方法,都是什么? 同步有几种实现方法,都是什么? 4.作用域public priv ...
- git merge 与 rebase
git merge git rebase merge V.S. rebase 参考材料 写在开始: 对merge和rebase的用法总有疑惑,好像两个都能完成"获取别的branch的comm ...
- Python-网站页面代码获取
Python3.6 库:urllib3, bs4 主程序是抓取亚马逊图书销售排名数据,但是亚马逊应该是加了反爬虫,拒绝疑似机器人的请求,这部分暂时以百度代替. 其实简单的页面抓取,常用的urllib. ...
- 使用 GNU profiler 来提高代码运行速度
各种软件对于性能的需求可能会有很大的区别,但是很多应用程序都有非常严格的性能需求,这一点并不奇怪.电影播放器就是一个很好的例子:如果一个电影播放器只能以所需要速度的 75% 来播放电影,那么它几乎就没 ...