beego-Context模块简介

         项目组件用beego开发,最近在完成一项需求时遇到一个坑,需求的内容大概是这样的,在一次请求内需要先对用户请求进行响应,然后异步goroute中去处理用户请求中的额外业务(向另一个服务(storage-manager)发送请求,不断查询资源的状态变化,待状态合适时需要删除资源),由于访问另一个服务需要证书认证,证书是在过滤器中加进去的,放在input.data中,如果需要在另一个goroute中使用证书首先就得拿到input.data;然而,正好项目中封装好了一个方法获取访问sm服务的通用方法,传入的一个参数就是input对象, 方法实现大体如下:

func  sendRequestToStorageManager(intput *context.BeegoInput,url string) error {
   tsp := input.GetData("X-Cluster-IP")
   client := http.Client{
              TranSport:tsp,
   }
   resp, err := client.GET(url)
   if err != nil {
      return err
   }
   if resp.Code != http.StatusOk {
      return fmt.Errorf("not expect response result!")
   }

}

// 调用处代码样例
func (ctl MyController)Create(){

   .....
   go func(){
       for {
           err := sendRequestToStorageManager(ctl.Ctx.Input)
           if err == nil {
              return
          }
       }
   }()
  .....

}

       于是我就按部就班的在gorouter中循环的调用这个方法访问sm服务,在测试的过程中意外发生了,gorouter的日中中出现了“401 Auth Failed!”,查看sm服务的相关日志得知我的客户端证书不正确,额额额! 证书为什么会不对呢?明明gorouter启动时还是好着的啊,于是我就展开了一番探索,我一开始就怀疑是input对象的问题,因为在访问sm服务的方法中引用的是input对象的指针,一单有别的goroute最input中的值进行改变,那么我启动的goroute中的input指针对应的值也会发生改变,导致最终我拿去访问sm的证书发生变化,认证失败;

      为了证实我的观点对beego的代码大体进行了浏览,终于在Context的模块中找到了蛛丝马迹,代码如下:

    github.com/astaxie/beego/router.go

// NewControllerRegister returns a new ControllerRegister.
func NewControllerRegister() *ControllerRegister {
	cr := &ControllerRegister{
		routers:  make(map[string]*Tree),
		policies: make(map[string]*Tree),
	}
	cr.pool.New = func() interface{} {
		return beecontext.NewContext()
	}
	return cr
}



// Implement http.Handler interface.
func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	startTime := time.Now()
	var (
		runRouter    reflect.Type
		findRouter   bool
		runMethod    string
		methodParams []*param.MethodParam
		routerInfo   *ControllerInfo
		isRunnable   bool
	)
	context := p.pool.Get().(*beecontext.Context)
	context.Reset(rw, r)

	defer p.pool.Put(context)
	if BConfig.RecoverFunc != nil {
		defer BConfig.RecoverFunc(context)
	}
......

}

关键代码有两行:context := p.pool.Get().(*beecontext.Context);defer p.pool.Put(context)  可以看出Context的实现使用了资源池的概念,每次有请求需要处理时首先在资源池中捞取一个Context对象,如果资源池没有就new一个,方法如下:

cr.pool.New = func() interface{} {
        return beecontext.NewContext()
} 

       处理完成以后归还到Context的资源池中,看到这里我大体明白了,我们在goroute中对input对象的引用在请求响应完成后就会被改变,因为它有可能已经作用于另外一次回话了,所以我们引用的input对象对应的指针的值也会被改变,这就是为什么,在goroute启动后会一直出现认证失败的原因了,因为证书已经被别的请求覆盖了。。。。

       用图像总结一下:

       Context的结构图:

      

    Context pool的工作原理:

       

 

 

 

你可能感兴趣的:(beego-Context模块简介)