这周在项目中遇到这样一个Bug,代码大致是这样的,有一个LogEntity日志类,里面有一个InnerLog负责存储每次请求的RPCInfo相关信息,

每次请求的时候会把RPC相关信息加入到InnerLog中。

public class LogEntity {

    public LogEntity() {
} private InnerLog inner = new InnerLog();
   public void addRpcInfo(RpcInfo rpcInfo) {
  if (gene == null) {
  initRpcGene();
  }
  if (rpcInfo.getRpctype() != null) {
   if (StringUtils.isBlank(rpcInfo.getRpcid())) {
   rpcInfo.setRpcid(gene.genRpcId());
   }
   inner.getInnerinfo().add(rpcInfo);
   }
  }
}

然后在Controller中,注入LogEntity,记录日志信息。

@RestController
@RequestMapping("/v")
public class VideoController extends BaseController { @Autowired
public VideoService videoService; @Autowired
LogEntity logEntity;
  @Override
  @RequestMapping(value = "/search", method = RequestMethod.GET)
  public Response search(Request request) {
  long start = System.currentTimeMillis();
  logEntity.put(LogConst.STARTTIMESTAMP, String.valueOf(start));
     ......
  ......
     logEntity.logging(logger);
  }
}

然后在压测的时候发现日志很快就到几十个G了。排查发现第二次请求时候的InnerLog中也包括第一次的InnerLog信息。这说明第二次请求和第一次请求

的时候用的是一个LogEntity实例。因此,想到这应该是注入的问题。因为对SpringBoot研究不是很深入,只是会简单的实用。因此,对一些性质还不是很

了解。查阅资料发现,注入的Bean是有范围可选的,默认的范围是Singleton,也就是容器中只有一个Bean实例。接下来,详细的看看Bean都有那几类范

围:

(1)singleton: 表示在spring容器中的单例,通过spring容器获得该bean时总是返回唯一的实例

(2)prototype:表示每次获得bean都会生成一个新的对象

(3)request:表示在一次http请求内有效(只适用于web应用)

(4)session:表示在一个用户会话内有效(只适用于web应用)

(5)globalSession:表示在全局会话内有效(只适用于web应用)

在多数情况,我们只会使用singleton和prototype两种scope,如果未指定scope属性,默认为singleton。

因此,针对这个问题,我们可以再LogEntity类上加上@Scope("prototype")注解,问题就解决了。也可以不改变LogEntity的范围,不过每次请求的时候新建

一个InnerLog就好了。

05-23 18:15