Merge pull request #603 from weaveworks/602-app-cpu

Don't keep stacking filters onto renderers, close idle http connections when we close a http publisher.
This commit is contained in:
Peter Bourgon
2015-10-29 13:45:05 +01:00
3 changed files with 30 additions and 28 deletions

View File

@@ -120,7 +120,7 @@ type APITopologyDesc struct {
URL string `json:"url"`
SubTopologies []APITopologyDesc `json:"sub_topologies,omitempty"`
Stats *topologyStats `json:"stats,omitempty"`
Stats topologyStats `json:"stats,omitempty"`
}
type byName []APITopologyDesc
@@ -193,11 +193,11 @@ func (r *registry) makeTopologyList(rep xfer.Reporter) func(w http.ResponseWrite
topologies = []APITopologyDesc{}
)
r.walk(func(desc APITopologyDesc) {
decorateTopologyForRequest(req, &desc)
decorateWithStats(&desc, rpt)
renderer := renderedForRequest(req, desc)
desc.Stats = decorateWithStats(rpt, renderer)
for i := range desc.SubTopologies {
decorateTopologyForRequest(req, &desc.SubTopologies[i])
decorateWithStats(&desc.SubTopologies[i], rpt)
renderer := renderedForRequest(req, desc.SubTopologies[i])
desc.Stats = decorateWithStats(rpt, renderer)
}
topologies = append(topologies, desc)
})
@@ -205,21 +205,21 @@ func (r *registry) makeTopologyList(rep xfer.Reporter) func(w http.ResponseWrite
}
}
func decorateWithStats(desc *APITopologyDesc, rpt report.Report) {
func decorateWithStats(rpt report.Report, renderer render.Renderer) topologyStats {
var (
nodes int
realNodes int
edges int
)
for _, n := range desc.renderer.Render(rpt) {
for _, n := range renderer.Render(rpt) {
nodes++
if !n.Pseudo {
realNodes++
}
edges += len(n.Adjacency)
}
renderStats := desc.renderer.Stats(rpt)
desc.Stats = &topologyStats{
renderStats := renderer.Stats(rpt)
return topologyStats{
NodeCount: nodes,
NonpseudoNodeCount: realNodes,
EdgeCount: edges,
@@ -233,25 +233,27 @@ func (r *registry) enableKubernetesTopologies() {
r.add(kubernetesTopologies...)
}
func decorateTopologyForRequest(r *http.Request, topology *APITopologyDesc) {
func renderedForRequest(r *http.Request, topology APITopologyDesc) render.Renderer {
renderer := topology.renderer
for param, opts := range topology.Options {
value := r.FormValue(param)
for _, opt := range opts {
if (value == "" && opt.Default) || (opt.Value != "" && opt.Value == value) {
topology.renderer = opt.decorator(topology.renderer)
renderer = opt.decorator(renderer)
}
}
}
return renderer
}
func (r *registry) captureTopology(rep xfer.Reporter, f func(xfer.Reporter, APITopologyDesc, http.ResponseWriter, *http.Request)) http.HandlerFunc {
func (r *registry) captureRenderer(rep xfer.Reporter, f func(xfer.Reporter, render.Renderer, http.ResponseWriter, *http.Request)) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
topology, ok := r.get(mux.Vars(req)["topology"])
if !ok {
http.NotFound(w, req)
return
}
decorateTopologyForRequest(req, &topology)
f(rep, topology, w, req)
renderer := renderedForRequest(req, topology)
f(rep, renderer, w, req)
}
}

View File

@@ -33,14 +33,14 @@ type APIEdge struct {
}
// Full topology.
func handleTopology(rep xfer.Reporter, t APITopologyDesc, w http.ResponseWriter, r *http.Request) {
func handleTopology(rep xfer.Reporter, renderer render.Renderer, w http.ResponseWriter, r *http.Request) {
respondWith(w, http.StatusOK, APITopology{
Nodes: t.renderer.Render(rep.Report()).Prune(),
Nodes: renderer.Render(rep.Report()).Prune(),
})
}
// Websocket for the full topology. This route overlaps with the next.
func handleWs(rep xfer.Reporter, t APITopologyDesc, w http.ResponseWriter, r *http.Request) {
func handleWs(rep xfer.Reporter, renderer render.Renderer, w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
respondWith(w, http.StatusInternalServerError, err.Error())
return
@@ -53,16 +53,16 @@ func handleWs(rep xfer.Reporter, t APITopologyDesc, w http.ResponseWriter, r *ht
return
}
}
handleWebsocket(w, r, rep, t, loop)
handleWebsocket(w, r, rep, renderer, loop)
}
// Individual nodes.
func handleNode(rep xfer.Reporter, t APITopologyDesc, w http.ResponseWriter, r *http.Request) {
func handleNode(rep xfer.Reporter, renderer render.Renderer, w http.ResponseWriter, r *http.Request) {
var (
vars = mux.Vars(r)
nodeID = vars["id"]
rpt = rep.Report()
node, ok = t.renderer.Render(rep.Report())[nodeID]
node, ok = renderer.Render(rep.Report())[nodeID]
)
if !ok {
http.NotFound(w, r)
@@ -72,13 +72,13 @@ func handleNode(rep xfer.Reporter, t APITopologyDesc, w http.ResponseWriter, r *
}
// Individual edges.
func handleEdge(rep xfer.Reporter, t APITopologyDesc, w http.ResponseWriter, r *http.Request) {
func handleEdge(rep xfer.Reporter, renderer render.Renderer, w http.ResponseWriter, r *http.Request) {
var (
vars = mux.Vars(r)
localID = vars["local"]
remoteID = vars["remote"]
rpt = rep.Report()
metadata = t.renderer.EdgeMetadata(rpt, localID, remoteID)
metadata = renderer.EdgeMetadata(rpt, localID, remoteID)
)
respondWith(w, http.StatusOK, APIEdge{Metadata: metadata})
@@ -92,7 +92,7 @@ func handleWebsocket(
w http.ResponseWriter,
r *http.Request,
rep xfer.Reporter,
t APITopologyDesc,
renderer render.Renderer,
loop time.Duration,
) {
conn, err := upgrader.Upgrade(w, r, nil)
@@ -117,7 +117,7 @@ func handleWebsocket(
tick = time.Tick(loop)
)
for {
newTopo := t.renderer.Render(rep.Report()).Prune()
newTopo := renderer.Render(rep.Report()).Prune()
diff := render.TopoDiff(previousTopo, newTopo)
previousTopo = newTopo

View File

@@ -65,13 +65,13 @@ func Router(c collector) *mux.Router {
get.HandleFunc("/api", gzipHandler(apiHandler))
get.HandleFunc("/api/topology", gzipHandler(topologyRegistry.makeTopologyList(c)))
get.HandleFunc("/api/topology/{topology}",
gzipHandler(topologyRegistry.captureTopology(c, handleTopology)))
gzipHandler(topologyRegistry.captureRenderer(c, handleTopology)))
get.HandleFunc("/api/topology/{topology}/ws",
topologyRegistry.captureTopology(c, handleWs)) // NB not gzip!
topologyRegistry.captureRenderer(c, handleWs)) // NB not gzip!
get.MatcherFunc(URLMatcher("/api/topology/{topology}/{id}")).HandlerFunc(
gzipHandler(topologyRegistry.captureTopology(c, handleNode)))
gzipHandler(topologyRegistry.captureRenderer(c, handleNode)))
get.MatcherFunc(URLMatcher("/api/topology/{topology}/{local}/{remote}")).HandlerFunc(
gzipHandler(topologyRegistry.captureTopology(c, handleEdge)))
gzipHandler(topologyRegistry.captureRenderer(c, handleEdge)))
get.MatcherFunc(URLMatcher("/api/origin/host/{id}")).HandlerFunc(
gzipHandler(makeOriginHostHandler(c)))
get.HandleFunc("/api/report", gzipHandler(makeRawReportHandler(c)))