异步请求处理可以提高服务接口的吞吐量、响应能力等,这里介绍一个简单的实现参考:
ServiceController,服务接口调度中心,处理并分发所有访问/service/{name}的请求,
ServiceHandler,服务处理者接口;ServiceHandlerAbstract,服务处理者抽象类,供继承
ServiceHandlerDatetime,日期服务处理者,参考实现
ServiceRequester,服务访问工具类
配置web.xml支持异步请求处理,所有的filter和servlet都要配置支持async-supported
true
ServiceController,检查加密授权secret参数,通过后分发给服务名name对应的ServiceHandler处理
支持请求参数Map,可以get或post
支持请求正文为json或xml格式,转换为JSONObject和XMLObject
支持异步或同步处理,例如ServiceHandlerDatetime就声明为同步处理,而比较耗时的服务则最好声明为异步处理
@RestController
@RequestMapping("service")
public class ServiceController {
private Logger logger = LoggerFactory.getLogger(getClass());
private static HttpHeaders headers = new HttpHeaders();
private Map serviceHandlers = new HashMap<>();
public static String secret = GlobalConfig.getProperty("service.controller.secret", "vyj2mtc5i1r4");
public static JSONObject BadRequest = JSON.parseObject("{\"status\":\"200\", \"error\":\"bad request\"}");
public static String BadRequestXML = new XMLObject("response").add("status", "200").add("error", "bad request").toString();
static {
headers.set("Access-Control-Allow-Origin", "*");
}
@RequestMapping(value= {"{name}", "{name}/**"})
public Object service(final HttpServletRequest request, final @PathVariable String name) {
final Map params = RequestUtil.stringMap(request);
final String content = getContent(request);
final boolean isXml = request.getRequestURI().endsWith(".xml");
final JSONObject data = !isXml ? getJson(content) : null;
final XMLObject xml = isXml ? getXml(content) : null;
logger.info(request.getMethod()+request.getRequestURI());
if(params.size()>0) logger.info("params: "+JSON.toJSONString(params));
if(data!=null || xml!=null) logger.info("body: "+(data!=null ? data.toJSONString() : xml.toString()));
//检查加密授权
boolean legal = secret.equals(params.get("secret"));
if(!legal && data!=null) legal = secret.equals(data.getString("secret"));
if(!legal && xml!=null) legal = secret.equals(xml.get("secret"));
if(!legal) {
Object obj = isXml ? BadRequestXML : BadRequest;
logger.info("result: "+obj.toString());
return new HttpEntity(obj, headers);
}
//处理请求
final ServiceHandler serviceHandler = serviceHandlers.get(name);
if(serviceHandler==null) { //非法请求
Object obj = isXml ? BadRequestXML : BadRequest;
logger.info("result: "+obj.toString());
return new HttpEntity(obj, headers);
}
if(serviceHandler.async()) {
return new Callable() {
public Object call() throws Exception {
return handle(serviceHandler, request, params, isXml ? xml : data);
}
};
}else {
return handle(serviceHandler, request, params, isXml ? xml : data);
}
}
private Object handle(ServiceHandler handler, HttpServletRequest request, Map params, Object body) {
boolean isXml = request.getRequestURI().endsWith(".xml");
Object obj = isXml ? handler.handle(request, params, (XMLObject)body) : handler.handle(request, params, (JSONObject)body);
obj = obj!=null ? obj : (isXml ? BadRequestXML : BadRequest);
logger.info("result: "+obj.toString());
return new HttpEntity(isXml ? obj.toString() : obj, headers);
}
public boolean register(ServiceHandler serviceHandler) {
if(serviceHandler!=null && StringUtils.isNotBlank(serviceHandler.name())) {
ServiceHandler handler = serviceHandlers.get(serviceHandler.name());
if(handler!=null) logger.warn("service handler key already exist, replace it: "+handler+", using this: "+serviceHandler);
serviceHandlers.put(serviceHandler.name(), serviceHandler);
return true;
}
logger.warn("bad service handler to register: "+serviceHandler);
return false;
}
private JSONObject getJson(String content) {
try {
if(StringUtils.isNotBlank(content) && content.startsWith("{")) return JSON.parseObject(content);
} catch (Exception e) {
logger.warn("fail to parse content to json: "+e.getMessage(), e);
}
return null;
}
private XMLObject getXml(String content) {
try {
if(StringUtils.isNotBlank(content) && content.startsWith("
} catch (Exception e) {
logger.warn("fail to parse content to xml: "+e.getMessage(), e);
}
return null;
}
private String getContent(HttpServletRequest request) {
try {
return IOUtils.toString(request.getInputStream(), Charsets.UTF_8);
} catch (Exception e) {
logger.warn("fail to get content of request: "+e.getMessage(), e);
}
return null;
}
}
ServiceHandler
public interface ServiceHandler {
/** 服务名称*/
String name();
/** 是否异步,耗时任务请使用异步 */
boolean async();
/** key-value数据处理并响应json */
JSONObject handle(HttpServletRequest request, Map params, JSONObject body);
/** method rquestUri headers params body 都可以处理 */
XMLObject handle(HttpServletRequest request, Map params, XMLObject body);
}
ServiceHandlerAbstract
public abstract class ServiceHandlerAbstract implements ServiceHandler, InitializingBean {
protected @Autowired ServiceController serviceController;
protected Logger logger = LoggerFactory.getLogger(getClass());
public void afterPropertiesSet() throws Exception {
serviceController.register(this);
}
public boolean async() {
return false;
}
public JSONObject handle(HttpServletRequest request, Map params, JSONObject body) {
return null;
}
public XMLObject handle(HttpServletRequest request, Map params, XMLObject body) {
return null;
}
}
ServiceHandlerDatetime,注解@Component以便被spring扫描为bean
@Component
public class ServiceHandlerDatetime extends ServiceHandlerAbstract {
private String name = "datetime";
@Override
public String name() {
return name;
}
public JSONObject handle(HttpServletRequest request, Map params, JSONObject body) {
String format = params.get("format");
if(format==null && body!=null) format = body.getString("format");
String datetime = datetime(format);
JSONObject json = new JSONObject();
json.put(name, datetime);
return json;
}
public XMLObject handle(HttpServletRequest request, Map params, XMLObject body) {
String format = params.get("format");
if(format==null && body!=null) format = body.get("format");
String datetime = datetime(format);
XMLObject xml = new XMLObject("response");
xml.add(name, datetime);
return xml;
}
private String datetime(String format) {
FormatType formatType = FormatType.DAYTIME;
if(format!=null && format.length()>0) {
try {
formatType = FormatType.valueOf(format);
}catch (Exception e) {
logger.warn("bad datetime format: "+format);
}
}
return DateUtil.format(new Date(), formatType);
}
}
ServiceRequesterTester
String service = GlobalConfig.getProperty("front_url")+"/service/";
String datetime = service+"datetime.json";
String datetimeXML = service+"datetime.xml";
int requestCount = 10000;
@Test public void request() {
final CountDownLatch latch = new CountDownLatch(requestCount);
final IntHolder success = new IntHolder(0);
long s = System.currentTimeMillis();
for(int i=0;i
TaskUtil.submit(new Runnable() {
@Override
public void run() {
Object obj = null;
switch(success.value%3) {
case 0:
Map params = ServiceRequester.paramsWithSecret();
obj = ServiceRequester.request(datetime, params);
break;
case 1:
JSONObject data = ServiceRequester.jsonWithSecret();
data.put("format", "DAYSHORTTIME");
obj = ServiceRequester.request(datetime, data);
break;
case 2:
XMLObject xml = ServiceRequester.xmlWithSecret();
xml.add("format", "DAY");
obj = ServiceRequester.request(datetimeXML, xml);
break;
}
System.out.println(obj);
if(obj!=null) success.value++;
latch.countDown();
}
});
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//success: 9996/10000, total ms: 29701 异步处理
//success: 10000/10000, total ms: 12905 同步处理
System.out.println("success: "+success.value+"/"+requestCount+", total ms: "+(System.currentTimeMillis()-s));
}
本文介绍了一种利用异步请求处理提高服务接口性能的方法。通过ServiceController统一接收请求,并分发给具体的ServiceHandler进行处理。支持JSON和XML格式的数据交换,并可灵活配置同步或异步处理方式。
1万+

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



