func (fe *T) AppendBeSrvRTMTo(aggregate *memmetrics.RTMetrics, beSrvURLKey backend.SrvURLKey) {
	fe.mu.Lock()
	rtmCollect := fe.rtmCollect
	fe.mu.Unlock()
	if rtmCollect == nil {
		return
	}
	rtmCollect.AppendBeSrvRTMTo(aggregate, beSrvURLKey)
}
AppendBeSrvRTMTo appends round-trip metrics of a backend server to aggregate.
It does nothing if a server if the specified URL key does not exist.
package frontend
import (
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"sync"
	"time"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
	"github.com/vulcand/oxy/buffer"
	"github.com/vulcand/oxy/forward"
	"github.com/vulcand/oxy/memmetrics"
	"github.com/vulcand/oxy/roundrobin"
	"github.com/vulcand/oxy/stream"
	"github.com/vulcand/vulcand/engine"
	"github.com/vulcand/vulcand/plugin"
	"github.com/vulcand/vulcand/proxy"
	"github.com/vulcand/vulcand/proxy/backend"
	"github.com/vulcand/vulcand/proxy/rtmcollect"
)
type T struct {
	mu         sync.Mutex
	ready      bool
	trustXFDH  bool
	cfg        engine.Frontend
	mwCfgs     map[engine.MiddlewareKey]engine.Middleware
	backend    *backend.T
	handler    http.Handler
	rtmCollect *rtmcollect.T
	listeners  plugin.FrontendListeners
}
func New(cfg engine.Frontend, be *backend.T, opts proxy.Options,
	mwCfgs map[engine.MiddlewareKey]engine.Middleware,
	listeners plugin.FrontendListeners,
) *T {
	if mwCfgs == nil {
		mwCfgs = make(map[engine.MiddlewareKey]engine.Middleware)
	}
	fe := T{
		cfg:       cfg,
		trustXFDH: opts.TrustForwardHeader,
		mwCfgs:    mwCfgs,
		backend:   be,
		listeners: listeners,
	}
	return &fe
}
func (fe *T) Key() engine.FrontendKey {
	return fe.cfg.Key()
}
func (fe *T) BackendKey() engine.BackendKey {
	fe.mu.Lock()
	beKey := fe.backend.Key()
	fe.mu.Unlock()
	return beKey
}
func (fe *T) Route() string {
	fe.mu.Lock()
	route := fe.cfg.Route
	fe.mu.Unlock()
	return route
}
func (fe *T) String() string {
	return fmt.Sprintf("frontend(%v)", fe.cfg.Id)
}
func (fe *T) Update(feCfg engine.Frontend, be *backend.T) error {
	fe.mu.Lock()
	defer fe.mu.Unlock()
	if feCfg.Key() != fe.Key() {
		return errors.Errorf("invalid key, want=%v, got=%v", fe.Key(), feCfg.Key())
	}
	if !feCfg.Equals(fe.cfg) {
		fe.cfg = feCfg
		fe.ready = false
	}
	if be != fe.backend {
		fe.backend = be
		fe.ready = false
	}
	return nil
}
func (fe *T) UpsertMiddleware(mwCfg engine.Middleware) {
	fe.mu.Lock()
	defer fe.mu.Unlock()
	mwKey := engine.MiddlewareKey{FrontendKey: engine.FrontendKey{Id: fe.cfg.Id}, Id: mwCfg.Id}
	fe.mwCfgs[mwKey] = mwCfg
	fe.ready = false
}
func (fe *T) DeleteMiddleware(mwKey engine.MiddlewareKey) {
	fe.mu.Lock()
	defer fe.mu.Unlock()
	if _, ok := fe.mwCfgs[mwKey]; !ok {
		return
	}
	delete(fe.mwCfgs, mwKey)
	fe.ready = false
}
func (fe *T) OnBackendMutated() {
	fe.mu.Lock()
	fe.ready = false
	fe.mu.Unlock()
}
func (fe *T) CfgWithStats() (engine.Frontend, bool, error) {
	fe.mu.Lock()
	rtmCollect := fe.rtmCollect
	feCfg := fe.cfg
	fe.mu.Unlock()
	if rtmCollect == nil {
		return engine.Frontend{}, false, nil
	}
	var err error
	if feCfg.Stats, err = rtmCollect.RTStats(); err != nil {
		return engine.Frontend{}, false, errors.Wrap(err, "failed to get stats")
	}
	return feCfg, true, nil
}
func (fe *T) AppendRTMTo(aggregate *memmetrics.RTMetrics) {
	fe.mu.Lock()
	rtmCollect := fe.rtmCollect
	fe.mu.Unlock()
	if rtmCollect == nil {
		return
	}
	rtmCollect.AppendFeRTMTo(aggregate)
}