Целью реализации http-сервера Go является обработка одновременных соединений, поэтому я сомневаюсь, что вы столкнетесь с проблемами параллелизма в самой реализации.
То, что здесь происходит, заключается в том, что при печати всего r.Context()
вы получаете доступ к внутреннему полю объекта Go * Server
без синхронизации доступа к нему.
Это вызывает ошибку concurrent map read and map write
, которую вы в итоге видите.
Самое простое решение было бы заменить это:
fmt.Fprintf(w, "r.ctx: %#v, %+v", r.Context(), r.Context())
С помощью некоторой пользовательской функции, которую вы пишете, она получает этот объект Context
и извлекает значения, которые вам подходят (например, любой пользовательский ключ / значение, которое вы добавили в контекст).
Более подробное объяснение этого поля activeConn
activeConn
, который вы видите, когда печатаете весь r.Context()
, как это происходит, происходит от типа Server
Го.
При подготовке к прослушиванию соединений Server
создает базовый контекст, в который он добавляет ссылку на сам Server
:
https://github.com/golang/go/blob/master/src/net/http/server.go#L2894
func (srv *Server) Serve(l net.Listener) error {
....
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
....
}
Таким образом, при печати всего контекста, вы в конечном итоге печатаете это поле activeConn
:
https://github.com/golang/go/blob/master/src/net/http/server.go#L2582
activeConn map[*conn]struct{}
Реализация Server
синхронизирует доступ к этой карте, когда она должна использоваться, например, здесь:
https://github.com/golang/go/blob/master/src/net/http/server.go#L2997
...
s.mu.Lock()
defer s.mu.Unlock()
if s.activeConn == nil {
s.activeConn = make(map[*conn]struct{})
}
if add {
s.activeConn[c] = struct{}{}
} else {
delete(s.activeConn, c)
}
....