mosn 中的context、变量 和 内存复用机制


在梳理变量机制中,发现与context、对象复用等机制都有一定关联,而context是贯穿始终的关键,所以本来只想记录变量机制,结果对context、对象复用封装都进行了review:

(review 相关经验:

  1. 查看对外接口,传参和返回值;思考使用 环境,与什么有关;
  2. 跳出细节,找一条线,思考:从 0 到 生命周期结束, 如何使用 以及怎么与接口关联;
  3. 源码解析文档、issue、历史提交记录、单元测试使用用例;
  4. 如果没有这个细节会怎么样?
  5. 结合过往经验,从抽象的层面去理解作者的用途;

Context 篇

mosnCtx: 是对context 库的wrapper,操作统一使用mosnCtx 库,而不使用标准的context库,为什么要加一个wrapper 呢?

context 两大功能:cancel (通知)、WithValue (request 级别的上下文);

缺点:WithValue 易被滥用,会返回一个新的context,后续查找时造成链表递归查询,效率低下

mosnCtx 封装WithValue的目的是:减少返回新的context、改进Value查询:将value 存储控制在本层,由于标准的context 只支持key,value,如果存储多个只能套娃,查询的时候也要遍历。所以改进的方式是:在本层使用数组(带 index 的 数组效率优于map),查询的时候也不套娃。所以使用的时候要注意这点:

实现了一个 context 接口
type valueCtx struct {context.Contextbuiltin [ContextKeyEnd]interface{}
}// 这里定义了 context 内置数组 只能存储下面 index 的value 上下文
// Context key types(built-in)
const (ContextKeyStreamID ContextKey = iotaContextKeyConnectionContextKeyConnectionIDContextKeyConnectionPoolIndexContextKeyListenerPortContextKeyListenerNameContextKeyListenerTypeContextKeyListenerStatsNameSpaceContextKeyNetworkFilterChainFactoriesContextKeyBufferPoolCtxContextKeyAccessLogsContextOriRemoteAddrContextKeyAcceptChanContextKeyAcceptBufferContextKeyConnectionFdContextSubProtocolContextKeyTraceSpanKeyContextKeyActiveSpanContextKeyTraceIdContextKeyVariablesContextKeyProxyGeneralConfigContextKeyDownStreamProtocolContextKeyConfigDownStreamProtocolContextKeyConfigUpStreamProtocolContextKeyDownStreamHeadersContextKeyDownStreamRespHeadersContextKeyEnd
)

查询值 WithValue:往内置数组存储 value, 尽量返回一个 valueCtx 类型的context,所以内容一定是存储在数组中的。

WithValue 封装这里强调 不能和 context.WithValue 混用:为什么呢,一旦 使用了 context.WithValue 会创建一个新的context,初始的context 的数组丢失了,而mosnCtx 在做值查询Value 的时候是(特意)没有做递归查询的,那么初始的数组内容会丢失。

// WithValue add the given key-value pair into the existed value context, or create a new value context which contains the pair.
// This Function should not be used along with the official context.WithValue !!// The following context topology will leads to existed pair {'foo':'bar'} NOT FOUND, because recursive lookup for
// key-type=ContextKey is not supported by mosn.valueCtx.
//
// topology: context.Background -> mosn.valueCtx{'foo':'bar'} -> context.valueCtx -> mosn.valueCtx{'hmm':'haa'}
func WithValue(parent context.Context, key ContextKey, value interface{}) context.Context {if mosnCtx, ok := parent.(*valueCtx); ok {mosnCtx.builtin[key] = valuereturn mosnCtx}// create new valueCtxmosnCtx := &valueCtx{Context: parent}mosnCtx.builtin[key] = valuereturn mosnCtx
}

获取值:如果是valueCtx 则从数组获取 否则 兼容原生的语义

没有做递归查询 除了效率问题外,还有一点很重要 避免值查询滥用,使用者需清晰知道生命周期和边界 以减少 错误的产生 和代码逻辑的混乱。

封装的同时还保留了原有context的语义,但是我们尽量不要混用。

// ContextKey type
type ContextKey int
// 封装 value  是为了兼容原生Context
// 原生的value key必须时string 类型的 ,所以不用担心获取int 类型数组和原生的int 冲突问题
func (c *valueCtx) Value(key interface{}) interface{} {if contextKey, ok := key.(ContextKey); ok {return c.builtin[contextKey]}return c.Context.Value(key)
}func Get(ctx context.Context, key ContextKey) interface{} {if mosnCtx, ok := ctx.(*valueCtx); ok {return mosnCtx.builtin[key]}return ctx.Value(key)
}

以上context 看似够用了,发现mosnCtx 还额外封装了一个Clone: 如果是原生的Context 就返回原生的 否则返回一个新的valueCtx,注意这里是数组拷贝:会返回一个新的数组 而不是指向 旧数组

// Clone copy the origin mosn value context(if it is), and return new one
func Clone(parent context.Context) context.Context {if mosnCtx, ok := parent.(*valueCtx); ok {clone := &valueCtx{Context: mosnCtx}// array copy assignclone.builtin = mosnCtx.builtinreturn clone}return parent
}

我们查看 调用点:发现只有内存复用机制有使用到,下面先分析内存复用机制,再回过来分析 context 其他细节:

func (cm *ContextManager) Next() {// buffer contextcm.curr = buffer.NewBufferPoolContext(mosnctx.Clone(cm.base))// variable contextcm.curr = variable.NewVariableContext(cm.curr)
}

内存对象复用

内存对象复用主要分为:通用的内存对象 管理(分配 和 重置) 以及 对象结构体引用(mosn 结合context 特有的管理)。

BufferPoolCtx (接口)是通用对象的管理方法,比如http/buffer.go 、 proxy/buffer.go 中的对象 proxyBuffer 和 httpBuffer 都是由 该接口 申请释放。httpBuffer、proxyBuffer 等对象 都继承了 tempBufferCtx 对象需要实现自己的 BufferPoolCtx方法。

const maxBufferPool = 16var (index int32bPool = bufferPoolArray[:]vPool = new(valuePool)bufferPoolArray [maxBufferPool]bufferPoolnullBufferValue [maxBufferPool]interface{}
)// bufferPool is buffer pool
type bufferPool struct {ctx types.BufferPoolCtxsync.Pool
}type valuePool struct {sync.Pool
}被分配的对象 都继承了 tempBufferCtx
// BufferPoolCtx is the bufferpool's context
type BufferPoolCtx interface {// Index returns the bufferpool's IndexIndex() int// New returns the bufferNew() interface{}Reset 如果直接放弃了原先的成员指针,交由gc 处理了,否则如果需要复用成员指针指向的内存 则对成员指针指向的内容进行reset,内存指针继续保留// Reset resets the bufferReset(interface{})
}type TempBufferCtx struct {index int
}

下面看一下 buffer/buffer.go 对外 方法:

RegisterBuffer 在 http/buffer.go 、proxy/buffer.go 中 init即会注册变量,为什么会有一个index 机制呢?因为要区分 不同对象 ,所以 每种对象都有自己一个单独的 sync.pool 大池子。

func RegisterBuffer(poolCtx types.BufferPoolCtx) {// frist index is 1i := atomic.AddInt32(&index, 1)if i >= maxBufferPool {panic("bufferSize over full")}bPool[i].ctx = poolCtxsetIndex(poolCtx, int(i))
}
setIndex 这个函数也很有意思。将接口转换成具体的对象,并设置具体对象的值。所以所有的内存对象都需要继承 tempBufferCtx 结构体
可以对比下 将setIndex 变成接口的方法,index 变成每个对象的成员变量的写法// ifaceWords is interface internal representation.
type ifaceWords struct {typ  unsafe.Pointerdata unsafe.Pointer
}// setIdex sets index, poolCtx must embedded TempBufferCtx
func setIndex(poolCtx types.BufferPoolCtx, i int) {p := (*ifaceWords)(unsafe.Pointer(&poolCtx))temp := (*TempBufferCtx)(p.data)temp.index = i
}

bufferPool 是某一类对象的 sync pool 池子 ,返回 bufferPoolCtx 接口 new 的对象

// Take returns a buffer from buffer pool
func (p *bufferPool) take() (value interface{}) {value = p.Get()if value == nil {value = p.ctx.New()}return
}// Give returns a buffer to buffer pool
func (p *bufferPool) give(value interface{}) {p.ctx.Reset(value)p.Put(value)
}

以上已经够用,但是mosn 又单独封装了一个bufferValue 的 概念, bufferPool 的 take 和 give 并没有对外暴露, 对外暴露的是bufferValue的接口:

// bufferValue is buffer pool's Value
type bufferValue struct {value    [maxBufferPool]interface{}transmit [maxBufferPool]interface{}
}// newBufferValue returns bufferValue
func newBufferValue() (value *bufferValue) {v := vPool.Get()if v == nil {value = new(bufferValue)} else {value = v.(*bufferValue)}return
}Take 仅在find中调用?
// Take returns buffer from buffer pools
func (bv *bufferValue) Take(poolCtx types.BufferPoolCtx) (value interface{}) {i := poolCtx.Index()value = bPool[i].take()bv.value[i] = valuereturn
}// Give returns buffer to buffer pools
func (bv *bufferValue) Give() {if index <= 0 {return}// first index is 1for i := 1; i <= int(index); i++ {value := bv.value[i]if value != nil {bPool[i].give(value)}value = bv.transmit[i]if value != nil {bPool[i].give(value)}}bv.value = nullBufferValuebv.transmit = nullBufferValue// Give bufferValue to PoolvPool.Put(bv)
}

我们 看看 bufferValue 的对外方法:

NewBufferPoolContext (ctx context.Context) 将某个 context 的内置数组 types.ContextKeyBufferPoolCtx 下标 设置为newBufferValue bufferValue 新建结构体。

// NewBufferPoolContext returns a context with bufferValue
func NewBufferPoolContext(ctx context.Context) context.Context {return mosnctx.WithValue(ctx, types.ContextKeyBufferPoolCtx, newBufferValue())
}

PoolContext(ctx context.Context) 从某个 context 中获取 bufferValue 对象,如果内置数组有 则返回内置数组中的bufferValue 指针,否则 新建一个

// PoolContext returns bufferValue by context
func PoolContext(ctx context.Context) *bufferValue {if ctx != nil {if val := mosnctx.Get(ctx, types.ContextKeyBufferPoolCtx); val != nil {return val.(*bufferValue)}}return newBufferValue()
}

PoolContext 经常与Find 一起使用:从上一步的 bufferValue 对象中,找到value 数组中的对象index 下标

// Find returns buffer from bufferValue
func (bv *bufferValue) Find(poolCtx types.BufferPoolCtx, x interface{}) interface{} {i := poolCtx.Index()if i <= 0 || i > int(index) {panic("buffer should call buffer.RegisterBuffer()")}if bv.value[i] != nil {return bv.value[i]}return bv.Take(poolCtx)
}
func proxyBuffersByContext(ctx context.Context) *proxyBuffers {poolCtx := buffer.PoolContext(ctx)return poolCtx.Find(&ins, nil).(*proxyBuffers)
}

从调用代码可以看出,bufferValue 经常被用来 结合context 入参 实现某个具体对象的获取。在变量机制中经常被用到。 一个完整的流程是:在 contextManager 中 为context 内置数组 types.ContextKeyBufferPoolCtx 分配一个 bufferValue对象,但是此时bufferValue 对象中val 是一个 各注册对象的数组,此时还没有具体分配具体对象。

func NewContextManager(base context.Context) *ContextManager {return &ContextManager{base: base,}
}
func (cm *ContextManager) Next() {// buffer contextcm.curr = buffer.NewBufferPoolContext(mosnctx.Clone(cm.base))// variable contextcm.curr = variable.NewVariableContext(cm.curr)
}

通过 proxyBuffersByContext 此时在 bufferValue的 value 数组中填充一个具体的对象,并开始初始化一个对象

newActiveStreamproxyBuffers := proxyBuffersByContext(ctx)stream := &proxyBuffers.streamstream.ID = atomic.AddUint32(&currProxyID, 1)stream.proxy = proxystream.requestInfo = &proxyBuffers.info

后续再次调用 proxyBuffersByContext 时 获取到的对象是已经初始化过内容的对象指针了。

以上如何理解bufferValue 呢?bufferValue 总是伴随context一起,如果没有bufferValue , 我们只能实现一个通用的对象池 获取一个对象壳子 以及归还一个对象壳子,有了bufferValue 以及 context, 不仅能够对壳子进行复用,还能获取到具体的对象指针:

  1. 有点类似连接池概念:不仅复用conn 结构体(壳子),在填充内容后,还能复用哈希池中的具体连接。可以直接调用conn 的connect 方法;
  2. bufferValue 相当于对bufferPoolCtx 的一层缓冲。如果有则从bufferValue 中的 value数组 获取内容,没有则从bufferPool中获取一个对象 并放入bufferValue 的value 数组中。

我们再来看看:context 中的clone, 如果不clone 的话,cm.base中的 context 内置数组 中的 bufferValue 将会被覆盖。bufferValue 中的对象都是请求生命周期的,如果之前的context 已经new过一次,新的context 再new一次,则旧的context的bufferValue 下的 value 数组中的对象都会丢失。clone 之后不仅可以让原生context 中的bufferValue 对象不会丢失,也可以实现对象继承 或者覆盖(不太可能)

cm.curr = buffer.NewBufferPoolContext(mosnctx.Clone(cm.base))
除非一个context 走天下 且对象不会再分配,可以只new一次吧,否则一个新的context 最好new 一个新的 bufferValue

还有一个疑点是: bufferValue 中的 transmit 域:我们可以看到 transmit 域仅仅在释放的时候有使用到:把前一个context的value 拷贝到本 context 再一起归还给内存池。对于本身有clone的应该是没有必要的,但是对于那些不是clone的context 由于最后归还的context 只有一个,如果没有transmit 可能造成内存没有归还内存池?

// TransmitBufferPoolContext copy a context
func TransmitBufferPoolContext(dst context.Context, src context.Context) {sValue := PoolContext(src)if sValue.value == nullBufferValue {return}dValue := PoolContext(dst)dValue.transmit = sValue.valuesValue.value = nullBufferValue
}

最后bufferValue 本身也是在vpool 池子里复用的。

变量

变量注册、变量赋值、变量获取、变量使用

变量必须在启动时先注册 后,才有后续的 赋值 和 获取等;

变量对象的描述:采用接口的模式 抽象 变量的设置 获取;

// Variable provides a flexible and convenient way to pass information
type Variable interface {// variable nameName() string// variable data, which is useful for getter/setterData() interface{}// variable flagsFlags() uint32// value getterGetter() GetterFunc// value setterSetter() SetterFunc
}// Indexer indicates that variable needs to be cached by using pre-allocated IndexedValue
type Indexer interface {// variable indexGetIndex() uint32// set index to variableSetIndex(index uint32)
}type GetterFunc func(ctx context.Context, value *IndexedValue, data interface{}) (string, error)// SetterFunc used to set the value of variable
type SetterFunc func(ctx context.Context, variableValue *IndexedValue, value string) error

具体的变量结构体:有BasicVariable 、IndexedVariable, 在 mosn 里面还有prefix_ | protocol variable 的 概念,后两者都是由 BasicVariable 承载;下面我们一步一步拆解这四个涵义,并比对异同点

// variable.Variable
type BasicVariable struct {getter GetterFuncsetter SetterFuncname  stringdata  interface{}flags uint32
}func (bv *BasicVariable) Name() string {return bv.name
}func (bv *BasicVariable) Data() interface{} {return bv.data
}func (bv *BasicVariable) Flags() uint32 {return bv.flags
}func (bv *BasicVariable) Setter() SetterFunc {return bv.setter
}func (bv *BasicVariable) Getter() GetterFunc {return bv.getter
}// variable.Variable
// variable.VariableIndexer
type IndexedVariable struct {BasicVariableindex uint32
}func (iv *IndexedVariable) SetIndex(index uint32) {iv.index = index
}func (iv *IndexedVariable) GetIndex() uint32 {return iv.index
}// BasicSetter used for variable value setting only, and would not affect any real data structure, like headers.
func BasicSetter(ctx context.Context, variableValue *IndexedValue, value string) error {variableValue.data = valuevariableValue.Valid = truereturn nil
}

我们先看看工厂中的全局存储结构体:factory.go

 mux              sync.RWMutexvariables        = make(map[string]Variable, 32) // all built-in variable definitionsprefixVariables  = make(map[string]Variable, 32) // all prefix getter definitionsindexedVariables = make([]Variable, 0, 32)       // indexed variablesfunc RegisterVariable(variable Variable) error {mux.Lock()defer mux.Unlock()name := variable.Name()// check conflictif _, ok := variables[name]; ok {return errors.New(errVariableDuplicated + name)}// registervariables[name] = variable// check indexif indexer, ok := variable.(Indexer); ok {index := len(indexedVariables)indexer.SetIndex(uint32(index))indexedVariables = append(indexedVariables, variable)}return nil
}

对象的操作api在 api.go:

func GetVariableValue(ctx context.Context, name string) (string, error)
func SetVariableValue(ctx context.Context, name, value string) error

我们以proxy/var.go 为例:

BasicVariable

初始化:
builtinVariables = []variable.Variable{variable.NewBasicVariable(types.VarStartTime, nil, startTimeGetter, nil, 0),variable.NewBasicVariable(types.VarRequestReceivedDuration, nil, receivedDurationGetter, nil, 0),variable.NewBasicVariable(types.VarResponseReceivedDuration, nil, responseReceivedDurationGetter, nil, 0),注册:for idx := range builtinVariables {variable.RegisterVariable(builtinVariables[idx])}

我们发现BasicVariable 没有设置 setter 函数,只有getter 函数: 通过context 获取出具体请求相关的结构体(见上节内存复用分析),然后 从请求中直接获取相关的信息,所以没有setter 只有getter

// StartTimeGetter
// get request's arriving time
func startTimeGetter(ctx context.Context, value *variable.IndexedValue, data interface{}) (string, error) {proxyBuffers := proxyBuffersByContext(ctx)info := proxyBuffers.inforeturn info.StartTime().Format("2006/01/02 15:04:05.000"), nil
}

非index variable 直接从全局variables 接口map 获取变量接口的getter 方法进行调用

func GetVariableValue(ctx context.Context, name string) (string, error) {// 1. find built-in variablesif variable, ok := variables[name]; ok {// 1.1 check indexed valueif indexer, ok := variable.(Indexer); ok {return getFlushedVariableValue(ctx, indexer.GetIndex())}// 1.2 use variable.Getter() to get valuegetter := variable.Getter()if getter == nil {return "", errors.New(errGetterNotFound + name)}return getter(ctx, nil, variable.Data())}

IndexedVariable

从初始化可以看出,对比 BasicVariable 普遍没有设置 variable.Getter(), 且需要显式调用api

func GetVariableValue(ctx context.Context, name string) (string, error)
func SetVariableValue(ctx context.Context, name, value string) error

还有多了一个 BasicSetter

// BasicSetter used for variable value setting only, and would not affect any real data structure, like headers.
func BasicSetter(ctx context.Context, variableValue *IndexedValue, value string) error {variableValue.data = valuevariableValue.Valid = truereturn nil
}  type IndexedValue struct {Valid       boolNotFound    boolnoCacheable bool//escape      booldata string
}
     variable.NewIndexedVariable(types.VarProxyTryTimeout, nil, nil, variable.BasicSetter, 0),variable.NewIndexedVariable(types.VarProxyGlobalTimeout, nil, nil, variable.BasicSetter, 0),variable.NewIndexedVariable(types.VarProxyHijackStatus, nil, nil, variable.BasicSetter, 0),variable.NewIndexedVariable(types.VarProxyGzipSwitch, nil, nil, variable.BasicSetter, 0),variable.NewIndexedVariable(types.VarProxyIsDirectResponse, nil, nil, variable.BasicSetter, 0),variable.NewIndexedVariable(types.VarHeaderStatus, nil, nil, variable.BasicSetter, 0),variable.NewIndexedVariable(types.VarHeaderRPCMethod, nil, nil, variable.BasicSetter, 0),variable.NewIndexedVariable(types.VarHeaderRPCService, nil, nil, variable.BasicSetter, 0),

下面 我们重点 捋一下 api:

注册变量时, 除了 把 indexVariable 放到全局的variable map 以外,还要放入一个variable 数组
// registervariables[name] = variable// check indexif indexer, ok := variable.(Indexer); ok {index := len(indexedVariables)indexer.SetIndex(uint32(index))indexedVariables = append(indexedVariables, variable)}先找到注册的变量对象,查询是否有实现index 接口。调用
setFlushedVariableValue(ctx context.Context, index uint32, value string) func SetVariableValue(ctx context.Context, name, value string) error {// find built-in & indexed variables, prefix and non-indexed are not supportedif variable, ok := variables[name]; ok {// 1.1 check indexed valueif indexer, ok := variable.(Indexer); ok {return setFlushedVariableValue(ctx, indexer.GetIndex(), value)}}
}从context的 内置数组 下标 types.ContextKeyVariables 获取IndexedValue 数组,context 中的 IndexedValue 数组包含了所有indexed Variables 变量具体描述。在contextManager 的Next 中new,在内存复用时已经clone过一次
func NewVariableContext(ctx context.Context) context.Context {// TODO: sync.Pool reusevalues := make([]IndexedValue, len(indexedVariables)) // TODO: pre-alloc buffer for runtime variablereturn mosnctx.WithValue(ctx, types.ContextKeyVariables, values)
}此时 variable.Setter() 就是 basicSetter,将indexedValue 的data 设置为 value,并置为validfunc setFlushedVariableValue(ctx context.Context, index uint32, value string) error {if variables := ctx.Value(types.ContextKeyVariables); variables != nil {if values, ok := variables.([]IndexedValue); ok {variable := indexedVariables[index]variableValue := &values[index]// should check variable.Flagsif (variable.Flags() & MOSN_VAR_FLAG_NOCACHEABLE) == MOSN_VAR_FLAG_NOCACHEABLE {variableValue.noCacheable = true}setter := variable.Setter()if setter == nil {return errors.New(errSetterNotFound + variable.Name())}return setter(ctx, variableValue, value)}}}func GetVariableValue(ctx context.Context, name string) (string, error) {// 1. find built-in variablesif variable, ok := variables[name]; ok {// 1.1 check indexed valueif indexer, ok := variable.(Indexer); ok {return getFlushedVariableValue(ctx, indexer.GetIndex())}从contex 的内置数组中,获取 index 对应的 indexdValue 如果是可缓存的 直接获取 data值
// TODO: provide direct access to this function, so the cost of variable name finding could be optimized
func getFlushedVariableValue(ctx context.Context, index uint32) (string, error) {if variables := ctx.Value(types.ContextKeyVariables); variables != nil {if values, ok := variables.([]IndexedValue); ok {value := &values[index]if value.Valid || value.NotFound {if !value.noCacheable {return value.data, nil}// clear flags//value.Valid = false//value.NotFound = false}return getIndexedVariableValue(ctx, value, index)}}}从getter 函数获取值,并赋值到context的index 下标
func getIndexedVariableValue(ctx context.Context, value *IndexedValue, index uint32) (string, error) {variable := indexedVariables[index]//if value.NotFound || value.Valid {// return value.data, nil//}getter := variable.Getter()if getter == nil {return "", errors.New(errGetterNotFound + variable.Name())}vdata, err := getter(ctx, value, variable.Data())if err != nil {value.Valid = falsevalue.NotFound = truereturn vdata, err}value.data = vdataif (variable.Flags() & MOSN_VAR_FLAG_NOCACHEABLE) == MOSN_VAR_FLAG_NOCACHEABLE {value.noCacheable = true}return value.data, nil
}

以上 indexed value 相当于contex 内置数组 对应下标的一个缓存,是对无法直接从context 转换对象 中getter 信息场景 的一个补充,

prefixVariables

 VarPrefixReqHeader string = "request_header_"// RespHeaderPrefix is the prefix of response header's formatterVarPrefixRespHeader string = "response_header_"prefixVariables = []variable.Variable{variable.NewBasicVariable(types.VarPrefixReqHeader, nil, requestHeaderMapGetter, nil, 0),variable.NewBasicVariable(types.VarPrefixRespHeader, nil, responseHeaderMapGetter, nil, 0),
}

变量注册 :

prefixVariables  = make(map[string]Variable, 32) // all func RegisterPrefixVariable(prefix string, variable Variable) error {mux.Lock()defer mux.Unlock()// check conflictif _, ok := prefixVariables[prefix]; ok {return errors.New(errPrefixDuplicated + prefix)}// registerprefixVariables[prefix] = variablereturn nil
}

变量获取: 遍历prefix map 调用getter

 // 2. find prefix variablesfor prefix, variable := range prefixVariables {if strings.HasPrefix(name, prefix) {getter := variable.Getter()if getter == nil {return "", errors.New(errGetterNotFound + name)}return getter(ctx, nil, name)}}

prefix 变量不支持 setter,看看getter 函数:

func requestHeaderMapGetter(ctx context.Context, value *variable.IndexedValue, data interface{}) (string, error) {proxyBuffers := proxyBuffersByContext(ctx)headers := proxyBuffers.stream.downstreamReqHeadersheaderName := data.(string)headerValue, ok := headers.Get(headerName[reqHeaderIndex:])if !ok {return variable.ValueNotFound, nil}return string(headerValue), nil
}func responseHeaderMapGetter(ctx context.Context, value *variable.IndexedValue, data interface{}) (string, error) {proxyBuffers := proxyBuffersByContext(ctx)headers := proxyBuffers.request.upstreamRespHeadersheaderName := data.(string)headerValue, ok := headers.Get(headerName[respHeaderIndex:])if !ok {return variable.ValueNotFound, nil}return string(headerValue), nil
}

ProtocolVariables

 // register protocol resourcevariable.RegisterProtocolResource(protocol.HTTP1, api.SCHEME, types.VarProtocolRequestScheme)variable.RegisterProtocolResource(protocol.HTTP1, api.PATH, types.VarProtocolRequestPath)variable.RegisterProtocolResource(protocol.HTTP1, api.URI, types.VarProtocolRequestUri)variable.RegisterProtocolResource(protocol.HTTP1, api.ARG, types.VarProtocolRequestArg)variable.RegisterProtocolResource(protocol.HTTP1, api.COOKIE, types.VarProtocolCookie)variable.RegisterProtocolResource(protocol.HTTP1, api.HEADER, types.VarProtocolRequestHeader)
var (errUnregisterProtocolResource = "unregister Protocol resource, Protocol: "protocolVar                   map[string]string
)func init() {protocolVar = make(map[string]string)
}// RegisterProtocolResource registers the resource as ProtocolResourceName
// forexample protocolVar[Http1+api.URI] = http_request_uri var
func RegisterProtocolResource(protocol api.ProtocolName, resource api.ProtocolResourceName, varname string) error {pr := convert(protocol, resource)protocolVar[pr] = fmt.Sprintf("%s_%s", protocol, varname)
}// GetProtocolResource get URI,PATH,ARG var depends on ProtocolResourceName
func GetProtocolResource(ctx context.Context, name api.ProtocolResourceName, data ...interface{}) (string, error) {p, ok := mosnctx.Get(ctx, types.ContextKeyDownStreamProtocol).(api.ProtocolName)if v, ok := protocolVar[convert(p, name)]; ok {// apend data behind if data existsif len(data) == 1 {v = fmt.Sprintf("%s%s", v, data[0])}return GetVariableValue(ctx, v)} else {return "", errors.New(errUnregisterProtocolResource + string(p))}
}func convert(p api.ProtocolName, name api.ProtocolResourceName) string {return string(p) + string(name)
}
NewActiveStream:ctx = mosnctx.WithValue(ctx, types.ContextKeyDownStreamProtocol, proto)

contextManager

针对 内存复用 变量 进行上下文 环境管理;

stream 中的contextManager

// contextManager
type ContextManager struct {base context.Contextcurr context.Context
}func (cm *ContextManager) Get() context.Context {return cm.curr
}func (cm *ContextManager) Next() {// buffer contextcm.curr = buffer.NewBufferPoolContext(mosnctx.Clone(cm.base))// variable contextcm.curr = variable.NewVariableContext(cm.curr)
}func (cm *ContextManager) InjectTrace(ctx context.Context, span api.Span) context.Context {if span != nil {return mosnctx.WithValue(ctx, mosnctx.ContextKeyTraceId, span.TraceId())}// generate traceIdreturn mosnctx.WithValue(ctx, mosnctx.ContextKeyTraceId, trace.IdGen().GenerateTraceId())
}func NewContextManager(base context.Context) *ContextManager {return &ContextManager{base: base,}
}

访问日志

先看看访问日志中对变量的使用 以及 变量包:

const DefaultAccessLogFormat = "%start_time% %request_received_duration% %response_received_duration% %bytes_sent%" + " " +"%bytes_received% %protocol% %response_code% %duration% %response_flag% %response_code% %upstream_local_address%" + " " +"%downstream_local_address% %downstream_remote_address% %upstream_host%"
%start_time% %ID% %topic% %dst_psm% %dst_address% %proto% %http_req_method% %http_req_uri% %http_req_arg_xxx% %http_req_hdr_xxx% %body_bytes_sent% %body_sent% %http_resp_code%
%http_resp_hdr_xxx% %body_bytes_recieved% %http_resp_hdr_time %http_resp_all_time backend

​ 传统的方式 是 定义一个结构体:然后将各种info在适当的时机赋值,最后打日志的时候 使用 switch case 根据每个logformat 获取对应info值;

而mosn 里面没有使用上述方式,好处 是:

  1. 把switch case 转成map func 方式,func 需要一个context,context 转换成 结构体 获取信息。或者 set 在 context的内置数组中 方便后续获取;
  2. 如果换取其他协议需要新增一套结构体表示方式,但是使用context 作为结构体抽象 或者 存储变量值 形式上比较统一;

mosn 中的context、变量 和 内存复用机制相关推荐

  1. java 单例 内存释放_周小抒 – 梦想仗剑走天涯 | Android中关于Context单例模式引起的内存泄漏以及解决方案...

    内存溢出与内存泄漏的区别: 内存溢出是由于应用所消耗的内存或者应用申请的内存超出了虚拟机分配的内存,也就是内存不够用了. 内存泄漏是某个不再使用对象由于被其他实例引用,导致不能被GC回收,而导致的内存 ...

  2. 【Android 内存优化】Bitmap 内存缓存 ( Bitmap 内存复用 | 弱引用 | 引用队列 | 针对不同 Android 版本开发不同的 Bitmap 复用策略 | 工具类代码 )

    文章目录 一.Bitmap 复用池 二.弱引用 Bitmap 内存释放 三.从 Bitmap 复用池中获取对应可以被复用的 Bitmap 对象 1.Android 2.3.3(API 级别 10)及以 ...

  3. java 数据保存内存_java中的各种数据类型在内存中存储的方式 一

    1.java是如何管理内存的 java的内存管理就是对象的分配和释放问题.(其中包括两部分) 分配:内存的分配是由程序完成的,程序员需要通过关键字new为每个对象申请内存空间(基本类型除外),所有的对 ...

  4. iOS内存管理机制解析

    软件运行时会分配和使用设备的内存资源,因此,在软件开发的过程中,需要进行内存管理,以保证高效.快速的分配内存,并且在适当的时候释放和回收内存资源. 一.Objective-C内存管理的对象 IOS开发 ...

  5. java内存管理机制-转载保存有价值的东西

    JAVA 内存管理总结 1. java是如何管理内存的 Java的内存管理就是对象的分配和释放问题.(两部分) 分配 :内存的分配是由程序完成的,程序员需要通过关键字new 为每个对象申请内存空间 ( ...

  6. python内存池机制,python基础—12python内存管理机制

    一.内存池机制 1.对象池 面试题目: python内存管理机制是什么? 1). 由于python中万物皆对象,内存管理机制就是对象的存储问题,Python会分 配一块内存空间去存储对象. 2) 对于 ...

  7. Go的内存回收机制(三色屏障)

    参考原文 垃圾回收(Garbage Collection,简称GC)是编程语言中提供的自动的内存管理机制,自动释放不需要的内存对象,让出存储器资源.GC过程中无需程序员手动执行.GC机制在现代很多编程 ...

  8. vs2008、vs2010、vs2012中查看变量在内存中的内容的方法

    在vs中调试程序的,有时候需要设置断点看某个变量的内容,如果变量是一个指针.数组或结构体,则可以通过查看该变量所指向的内存来看变量的内容. 1.在vs2008中打开内存窗口 在vs2008调试界面中, ...

  9. PHP变量在内存中的存储方式

    原文:http://www.phppan.com/tag/refcount/ 每门计算机语言都需要一些容器来保存变量数据.在一些语言当中,变量都有特定的类型,如字符串,数组,对象等等.比如C和Pasc ...

最新文章

  1. 零基础入门学习Python(36) 类和对象:继承
  2. 国内人脸识别第一案,我们来谈谈国外法规和隐私保护技术
  3. 利用数据存储技术实现数据安全合理备份
  4. Axure中推动拉动元件不生效_单线图标元件库SVG矢量图标 纯净无干扰的Axure元件库...
  5. 【强化学习】Policy Gradient原理
  6. go中使用sqlite
  7. 修改windows功能要开启哪个服务器,win7更改windows功能提示出现错误并非所有的功能被成功更改怎么办...
  8. 易语言最大化控件跟随变化_庄子:利己最大化与博弈圈套
  9. [转+]C语言复杂声明
  10. mysql 新建用户并赋予远程访问权限
  11. 孔浩java web视频教程_孔浩老师webservice视频教程(含CXF视频教程)
  12. C语言写PCAP文件
  13. 类似宝塔linux面板,类似宝塔面板的软件有没有呢?
  14. linux下find命令的详细说明
  15. filezilla下载文件,如何使用filezilla下载文件
  16. android 盒子 关闭电视,电视盒子正确使用方法,速速来取!
  17. 消息队列相关使用方式
  18. 实习日记——Day24
  19. 《Python深度学习》3.4电影评论分类:二分类问题
  20. Word章节自动编号+题注自动编号+公式编号+交叉引用

热门文章

  1. [转]Git忽略提交规则 - .gitignore配置运维总结
  2. RMAN异机复制数据库(不同路径)
  3. 接口测试--version2
  4. 09、HTLM中直接写get请求和模板标签分片功能
  5. FuncT,TResult的使用方法(转载)
  6. Linux系统内存管理之伙伴系统分析 - 旭东的博客 - 博客园
  7. mysql text 最大长度 报错 Row size too large. The maximum row size for the used table type
  8. 内网渗透测试:内网信息收集与上传下载
  9. 绕过CDN查找真实IP 方法简介
  10. centos7 安装 xfce桌面