welcome to xlongwei.com

欢迎大家一起学习、交流、分享


QQ:9167702333 邮箱:admin@xlongwei.com

ServiceController 服务接口异步请求处理


分类 Java   关键字 分享   标签 java   web   发布 hongwei  1428937948957
注意 转载须保留原文链接,译文链接,作者译者等信息。  
异步请求处理可以提高服务接口的吞吐量、响应能力等,这里介绍一个简单的实现参考:
  • ServiceController,服务接口调度中心,处理并分发所有访问/service/{name}的请求,
  • ServiceHandler,服务处理者接口;ServiceHandlerAbstract,服务处理者抽象类,供继承
  • ServiceHandlerDatetime,日期服务处理者,参考实现
  • ServiceRequester,服务访问工具类

配置web.xml支持异步请求处理,所有的filter和servlet都要配置支持async-supported
<async-supported>true</async-supported>

ServiceController,检查加密授权secret参数,通过后分发给服务名name对应的ServiceHandler处理
支持请求参数Map<String,String>,可以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<String, ServiceHandler> 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<String, String> 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<Object>(obj, headers);
}

//处理请求
final ServiceHandler serviceHandler = serviceHandlers.get(name);
if(serviceHandler==null) { //非法请求
Object obj = isXml ? BadRequestXML : BadRequest;
logger.info("result: "+obj.toString());
return new HttpEntity<Object>(obj, headers);
}

if(serviceHandler.async()) {
return new Callable<Object>() {
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<String, String> 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<Object>(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("<")) return XMLObject.fromXML(content);
} 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<String, String> params, JSONObject body);
/** method rquestUri headers params body 都可以处理 */
XMLObject handle(HttpServletRequest request, Map<String, String> 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<String, String> params, JSONObject body) {
return null;
}
public XMLObject handle(HttpServletRequest request, Map<String, String> 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<String, String> 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<String, String> 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<requestCount;i++) {
		TaskUtil.submit(new Runnable() {
			@Override
			public void run() {
				Object obj = null;
				switch(success.value%3) {
				case 0:
					Map<String, String> 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));
}