这周在项目中遇到这样一个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就好了。