问题描述
给一个系统写服务端api,采用的spring+jersey的代码架构如下图

定义了一个InfoResource,其中使用@autowired 来注入对应的InfoQueryService,调用方式如下。测试时发现,如果单线程调用接口则一切正常,如果多线程并发调用这个接口,则部分请求返回内容是不完整的,然而程序运行并没有任何报错。
// InfoResource.java
@Path("/info")
@Component
public class InfoResource {
@Autowired
private InfoQueryService infoQueryService;
.......
@POST
@Path("/json/upload")
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_FORM_URLENCODED })
@Produces(MediaType.APPLICATION_JSON)
public Response uploadJson(@Context final HttpHeaders headers, final RequestBodyBean requestBodybean) {
NormalResponseBean normalResponseBean = null;
try {
normalResponseBean = infoQueryService.doResponseService(headers, requestBodybean);
} catch (Exception e) {
e.printStackTrace();
}
......
}
}
// InfoQueryService.java
@Component
public class InfoQueryService {
@Autowired
private ConfigProperty configProperty;
@Autowired
private OrgDAO orgDAO;
@Autowired
private InfoDAO infoDAO;
/**
* 查询请求接口service 主函数
*
* @param headers
* @param bodyBean
* @return
*/
public NormalResponseBean doResponseService(HttpHeaders inHeaders, RequestBodyBean bodyBean) {
......
}
}
问题分析
在service上添加synchronized,发现问题解决了。但是接口速度明显降低,公网调用从80ms降低到130ms,这显然不能接受。删除synchronized。
继续测试,程序不报错说明service注入是成功的,那么哪一层错了呢?
在service层打日志,查看每次接口拼装的返回结果都是对的。
在resource层打日志,发现部分返回结果是错误的。也就是service处理是正确的,返回给resource层就错误了。
判定是service注入到resource的过程有问题。
为什么呢?
spring对组建的注入(@autowired)默认用的单例模式,在上面代码中,spring使用了一个infoQueryService实例来处理多个接口请求。高并发情况下就可能导致service实例在内存层面出现冲突。
有问题的代码:
@Autowired
private InfoQueryService infoQueryService;
举例子,service实例是一个服务员,服务员一次服务一个客户是没问题的,但是同时服务多个客户时,服务员思维就不能保持清晰了。
怎么解决?
多配置服务员!
把单例注入修改为多例注入,每次请求新建一个infoQueryService实例。
代码实现
在spring的单例中注入多例由多种实现方式,笔者偏好于使用@Lookup注解,实现如下。
// InfoResource.java
@Path("/info")
@Component
public class InfoResource {
// @Autowired // 不再使用autowired
private InfoQueryService infoQueryService;
.......
// 使用Lookup实现多例注入
@Lookup
public InfoQueryService getPrototypeBean() {
// 返回null即可,spring会自动重写这个方法,为你返回正确的InfoQueryService实例
return null;
}
@POST
@Path("/json/upload")
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_FORM_URLENCODED })
@Produces(MediaType.APPLICATION_JSON)
public Response uploadJson(@Context final HttpHeaders headers, final RequestBodyBean requestBodybean) {
NormalResponseBean normalResponseBean = null;
try {
infoQueryService = getPrototypeBean(); // 每次请求会新建一个实例
normalResponseBean = infoQueryService.doResponseService(headers, requestBodybean);
} catch (Exception e) {
e.printStackTrace();
}
......
}
}
最后,多例注入显然会比单例注入慢,测试结果看会慢2到5个毫秒。
更多多例注入的实现方法:
Java编程中的Spring多例

本文介绍了在使用Spring+Jersey编写API服务端时遇到的并发请求问题,问题源于@Autowired注解导致的服务单例模式在高并发下引发的内容不完整。通过在Service层添加synchronized暂时解决问题,但牺牲了性能。进一步分析发现,问题出在Service到Resource的注入环节。解决方案是将单例注入改为多例注入,避免并发冲突。文中提供了注解方式实现多例注入的代码示例,并提及多例注入对性能的影响。

9546

被折叠的 条评论
为什么被折叠?



