项目
出行项目
需求
今日订单查询优化
场景描述
根据时间查询出今日订单,根据订单去mongo查询出规矩点,根据轨迹点去调用高德的地理/逆地理编码接口(https://lbs.amap.com/api/webservice/guide/api/georegeo 接口文档地址),获取经纬度对应的地址名称
问题:每一订单多会有6-7个坐标需要查询的,分页展示一页有10个订单,那么就要调用60多次的高德API,一次花销600毫秒,这里就需要花费4秒左右的时间,性能是非常低
解决方案
- 1.高德逆地理API支持批量查询,把前面的一个点查询一次改为一个订单查询一次,而且高德的批量查询一次消耗110毫秒左右快了很多
- 2.使用多线程同时处理10个订单,这样效率就很快 了
- 3.把查询出来的坐标名称:缓存起来,公司用的是memcache
代码
- 入口方法
private Map getOrderTodayListBeanList(Map<String, Object> pageParamMap) {
Long startTime = System.currentTimeMillis();
Map<String, Object> resultMap = orderService.getOrderTodayList(pageParamMap);
List<OrderTodayListBean> orderTodayListBeanList = (List<OrderTodayListBean>) resultMap.get("aaData");
int recordCount = (Integer) resultMap.get("iTotalDisplayRecords");
if (null != orderTodayListBeanList && orderTodayListBeanList.size() > 0) {
//创建线程池
ExecutorService threadPool = Executors.newFixedThreadPool(10);
OrderTodayThread orderTodayThread;
for (OrderTodayListBean orderTodayListBean : orderTodayListBeanList) {
orderTodayThread = new OrderTodayThread(cacheService, locationService, orderTodayListBean);
threadPool.execute(orderTodayThread);
}
try {
threadPool.shutdown();
if (!threadPool.awaitTermination(20, TimeUnit.SECONDS)) {
//到达指定时间,还有线程没执行完,不再等待,关闭线程池!--20S
threadPool.shutdownNow();
}
} catch (Throwable e) {
// TODO Auto-generated catch block
threadPool.shutdownNow();
e.printStackTrace();
}
}
- 2.线程处理类
package com.summersoft.ts.base;
import com.summersoft.framework.util.StringUtils;
import com.summersoft.ts.cache.CacheService;
import com.summersoft.ts.location.model.DriverLocation;
import com.summersoft.ts.location.model.OrderEventPoint;
import com.summersoft.ts.location.service.LocationService;
import com.summersoft.ts.order.bean.OrderTodayListBean;
import com.summersoft.utils.Constants;
import com.summersoft.utils.HttpRequest;
import java.util.List;
/**
* Description: 今日订单线程
*
* @author 兰平雄
* @date 2018/11/13
* @Version: 1.0
*/
public class OrderTodayThread implements Runnable{
private OrderTodayListBean orderTodayListBean;
private CacheService cacheService;
private LocationService locationService;
public OrderTodayThread() {
}
public OrderTodayThread(CacheService cacheService, LocationService locationService, OrderTodayListBean orderTodayListBean) {
this.orderTodayListBean = orderTodayListBean;
this.cacheService = cacheService;
this.locationService = locationService;
}
@Override
public void run() {
if ((Constants.ORDER_MAIN_STATUS_INITIAL+"").equals(orderTodayListBean.getMainStatus())) {
//待接单
orderTodayListBean.setStatusName("待接单");
} else if ((Constants.ORDER_MAIN_STATUS_DOING+"").equals(orderTodayListBean.getMainStatus())) {
//订单进行中
orderTodayListBean.setStatusName("进行中");
//先取缓存,缓存取不到在走查询逻辑
String mapOriginAddressNameKey = "map_originAddressName_"+orderTodayListBean.getOrderUuid()+"_" + orderTodayListBean.getMainStatus();
String mapOrderEventPointListKey = "map_orderEventPointList_"+orderTodayListBean.getOrderUuid()+"_" + orderTodayListBean.getMainStatus();
String originAddressNameCache = (String) cacheService.get(mapOriginAddressNameKey);
List<OrderEventPoint> orderEventPointListCache = (List<OrderEventPoint>) cacheService.get(mapOrderEventPointListKey);
if (StringUtils.isNotEmpty(originAddressNameCache) && null != orderEventPointListCache && orderEventPointListCache.size() > 0) {
orderTodayListBean.setOriginAddressName(originAddressNameCache);
orderTodayListBean.setOrderEventPointList(orderEventPointListCache);
} else {
List<OrderEventPoint> orderEventPointList = locationService.listOrderEventPoint(orderTodayListBean.getOrderUuid());
if (orderEventPointList != null && orderEventPointList.size() > 0) {
for (OrderEventPoint orderEventPoint : orderEventPointList) {
//位置信息类型 SJSB司机上班,SJXB司机下班,CKSC乘客上车,CKXC乘客下车,AUTO定时上传,DDPD派单
if ("SJCF".equals(orderEventPoint.getPositionType())) {
orderTodayListBean.setOriginAddressName(orderEventPoint.getOrderEventAddress().getAddress());
cacheService.set(mapOriginAddressNameKey, orderEventPoint.getOrderEventAddress().getAddress(), 5);
}
}
//保存订单状态点
orderTodayListBean.setOrderEventPointList(orderEventPointList);
cacheService.set(mapOrderEventPointListKey, orderEventPointList, 5);
}
}
String destAddressNameKey="map_destAddressName_"+orderTodayListBean.getOrderUuid()+"_" + orderTodayListBean.getMainStatus();
String driverLocationListKey="map_driverLocationList_"+orderTodayListBean.getOrderUuid()+"_" + orderTodayListBean.getMainStatus();
String destAddressNameCache = (String) cacheService.get(destAddressNameKey);
List<DriverLocation> driverLocationListCache = (List<DriverLocation>) cacheService.get(driverLocationListKey);
//先查询缓存
if (StringUtils.isNotEmpty(destAddressNameCache) && null != driverLocationListCache && driverLocationListCache.size() > 0) {
orderTodayListBean.setDestAddressName(destAddressNameCache);
orderTodayListBean.setOrderPointList(driverLocationListCache);
} else {
List<DriverLocation> driverLocationList = locationService.findOrderMapPoint(orderTodayListBean.getOrderUuid(), orderTodayListBean.getAppId());
if (driverLocationList != null) {
//保存订单全部点
orderTodayListBean.setOrderPointList(driverLocationList);
cacheService.set(driverLocationListKey, driverLocationList, 5);
//取得最后一个点来作为当前的点
if (driverLocationList != null && driverLocationList.size() != 0) {
DriverLocation lastLocation = driverLocationList.get(driverLocationList.size() - 1);
String addressName = HttpRequest.getAdressName(String.valueOf(lastLocation.getCoordinate().getLng()), String.valueOf(lastLocation.getCoordinate().getLat()));
orderTodayListBean.setDestAddressName(addressName);
cacheService.set(destAddressNameKey, addressName, 5);
}
}
}
//2018/11/10 测试要求 3-为已完成
} else if ((Constants.ORDER_MAIN_STATUS_DONE+"").equals(orderTodayListBean.getMainStatus())
|| (Constants.ORDER_MAIN_STATUS_PAYED+"").equals(orderTodayListBean.getMainStatus())) {
//已完成
orderTodayListBean.setStatusName("已完成");
//先取缓存,缓存取不到在走查询逻辑
String mapOriginAddressNameKey = "map_originAddressName_"+orderTodayListBean.getOrderUuid()+"_" + orderTodayListBean.getMainStatus();
String mapDestAddressNameKey = "map_destAddressName_"+orderTodayListBean.getOrderUuid()+"_" + orderTodayListBean.getMainStatus();
String mapOrderEventPointListKey = "map_orderEventPointList_"+orderTodayListBean.getOrderUuid()+"_" + orderTodayListBean.getMainStatus();
String originAddressNameCache = (String) cacheService.get(mapOriginAddressNameKey);
String destAddressNameKeyCache = (String) cacheService.get(mapDestAddressNameKey);
List<OrderEventPoint> orderEventPointListCache = (List<OrderEventPoint>) cacheService.get(mapOrderEventPointListKey);
if ((StringUtils.isNotEmpty(originAddressNameCache) || StringUtils.isNotEmpty(destAddressNameKeyCache))
&& null != orderEventPointListCache && orderEventPointListCache.size() > 0) {
if (StringUtils.isNotEmpty(originAddressNameCache)) {
orderTodayListBean.setOriginAddressName(originAddressNameCache);
}
if (StringUtils.isNotEmpty(destAddressNameKeyCache)) {
orderTodayListBean.setDestAddressName(destAddressNameKeyCache);
}
orderTodayListBean.setOrderEventPointList(orderEventPointListCache);
} else {
List<OrderEventPoint> orderEventPointList = locationService.listOrderEventPoint(orderTodayListBean.getOrderUuid());
if (orderEventPointList != null && orderEventPointList.size() > 0) {
for (OrderEventPoint orderEventPoint : orderEventPointList) {
if ("SJCF".equals(orderEventPoint.getPositionType())) {
orderTodayListBean.setOriginAddressName(orderEventPoint.getOrderEventAddress().getAddress());
cacheService.set(mapOriginAddressNameKey, orderEventPoint.getOrderEventAddress().getAddress(), 5);
}
if ("CKXC".equals(orderEventPoint.getPositionType())) {
orderTodayListBean.setDestAddressName(orderEventPoint.getOrderEventAddress().getAddress());
cacheService.set(mapDestAddressNameKey, orderEventPoint.getOrderEventAddress().getAddress(), 5);
}
}
orderTodayListBean.setOrderEventPointList(orderEventPointList);
cacheService.set(mapOrderEventPointListKey, orderEventPointList, 5);
}
}
String driverLocationListKey="map_driverLocationList_"+orderTodayListBean.getOrderUuid()+"_" + orderTodayListBean.getMainStatus();
List<DriverLocation> driverLocationListCache = (List<DriverLocation>) cacheService.get(driverLocationListKey);
//先查询缓存
if (null != driverLocationListCache && driverLocationListCache.size() > 0) {
orderTodayListBean.setOrderPointList(driverLocationListCache);
} else {
List<DriverLocation> driverLocationList = locationService.findOrderMapPoint(orderTodayListBean.getOrderUuid(), orderTodayListBean.getAppId());
if (driverLocationList != null) {
//保存订单全部点
orderTodayListBean.setOrderPointList(driverLocationList);
cacheService.set(driverLocationListKey, driverLocationList, 5);
}
}
} else if ((Constants.ORDER_MAIN_STATUS_CANCEL+"").equals(orderTodayListBean.getMainStatus())) {
//已取消
orderTodayListBean.setStatusName("已取消");
}
}
}
- 3.listOrderEventPoint()方法查询mongo和调用高德API方法
@Override
public List<OrderEventPoint> listOrderEventPoint(String orderUuid) {
Query query = new Query();
query.addCriteria(new Criteria("orderUuid").is(orderUuid));
query.addCriteria(new Criteria("positionType").ne("AUTO"));
query.with(new Sort(new Sort.Order(Sort.Direction.ASC, "uploadTime")));
//long startTime = System.currentTimeMillis();
List<DriverLocation> driverLocationList = mongoTemplate.find(query, DriverLocation.class);
//long endMongoTime = System.currentTimeMillis();
//System.out.println("****************************查询mongo时间"+(endMongoTime-startTime));
List<OrderEventPoint> orderEventPointList = new ArrayList<OrderEventPoint>();
StringBuilder locations = null;
List<OrderEventPoint> doGaodelist = null;
Map<String,List<OrderEventPoint>> orderEventPointListMap = new HashMap();
int index = 0;
for (int i=0;i< driverLocationList.size();i++) {
DriverLocation driverLocation = driverLocationList.get(i);
OrderEventPoint orderEventPoint = new OrderEventPoint();
orderEventPoint.setObjectId(driverLocation.get_id());
orderEventPoint.setDriverUuid(driverLocation.getDriverUuid());
orderEventPoint.setLng(driverLocation.getCoordinate().getLng());
orderEventPoint.setLat(driverLocation.getCoordinate().getLat());
orderEventPoint.setUploadTime(driverLocation.getUploadTime());
orderEventPoint.setPositionType(driverLocation.getPositionType());
//通过经纬度反向获取地址
Properties properties = PropertiesUtil.loadProperties("mongodb.properties");
String overseas = properties.getProperty("overseas");
//海外服务器暂时先不调用高德地图转换地址
if ( orderEventPoint.getLng() != null && orderEventPoint.getLat() != null) {
if (!"1".equals(overseas)) {
if (index % 20 == 0) {
//高德接口最多只能一次查询20条
if (index != 0) {
orderEventPointListMap.put(locations.toString(), doGaodelist);
}
doGaodelist = new ArrayList<>();
locations = new StringBuilder();
locations.append(orderEventPoint.getLng() + "," + orderEventPoint.getLat());
index++;
} else {
locations.append("|" + orderEventPoint.getLng() + "," + orderEventPoint.getLat());
}
doGaodelist.add(orderEventPoint);
} else {
LocationUtils.buildAddressGoogle(orderEventPoint.getLng(), orderEventPoint.getLat(), orderEventPoint);
}
}
if (i == (driverLocationList.size() - 1) && null != doGaodelist && doGaodelist.size() > 0) {
orderEventPointListMap.put(locations.toString(), doGaodelist);
}
orderEventPointList.add(orderEventPoint);
}
//高德批量查询接口
if (null != doGaodelist && doGaodelist.size() > 0) {
LocationUtils.batchBuildAddress(orderEventPointListMap);
}
//long endGaodeTime = System.currentTimeMillis();
// System.out.println("***************************高德获取地点名称时间"+(endGaodeTime-endMongoTime));
return orderEventPointList;
}
- 4.com.util.LocationUtils 类
/**
* 批量查询
* @param orderEventPointListMap
*/
public static void batchBuildAddress(Map<String, List<OrderEventPoint>> orderEventPointListMap){
for (Map.Entry<String, List<OrderEventPoint>> entry : orderEventPointListMap.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
if (StringUtils.isNotEmpty(entry.getKey())) {
buildBatchAddresses(getBatchPointsJson(entry.getKey()), entry.getValue());
}
}
}
public static JSONObject getBatchPointsJson(String locations){
Map<String, Object> paramsMap = new HashMap<String, Object>();
paramsMap.put("key", KEY);
paramsMap.put("location", locations);
// 开启批量查询
paramsMap.put("batch", true);
String result = HttpUtils.postResponseString("http://restapi.amap.com/v3/geocode/regeo", paramsMap);
return JSONObject.fromObject(result);
}
private static void buildBatchAddresses(JSONObject root, List<OrderEventPoint> orderEventPointList) {
String status = (String) root.get("status");
String info = (String) root.get("info");
if(status.equals("1") && info.equals("OK")){
JSONArray regeocodes = (JSONArray) root.get("regeocodes");
//判断回传的条数,有可能后面的会有查询不成功的时候
if (regeocodes.size()==orderEventPointList.size()) {
JSONObject regeocode = null;
for (int i=0;i<regeocodes.size();i++) {
regeocode=(JSONObject)regeocodes.get(i);
getOrderEventPoint(orderEventPointList.get(i), regeocode);
}
}
}
}
/**
* 从regeocode获取orderEventAddress
* @param orderEventPoint
* @param regeocode
*/
private static void getOrderEventPoint(OrderEventPoint orderEventPoint, JSONObject regeocode) {
OrderEventAddress orderEventAddress = new OrderEventAddress();
orderEventAddress.setDetailAddress((String) regeocode.get("formatted_address"));
//参数组件
JSONObject addressComponent = (JSONObject) regeocode.get("addressComponent");
String province = addressComponent.getString("province");
if("[]".equals(province)){
province = "";
}
String city = addressComponent.getString("city");
if("[]".equals(city)){
city = "";
}
String district = addressComponent.getString("district");
if("[]".equals(district)){
district = "";
}
String township = addressComponent.getString("township");
if("[]".equals(township)){
township = "";
}
orderEventAddress.setAddress(orderEventAddress.getDetailAddress().replaceAll(province, "").
replaceAll(city, "").replaceAll(district, "").replaceAll(township, ""));
orderEventPoint.setOrderEventAddress(orderEventAddress);
}
优化结果:
原来查询一次总耗时4.8秒,优化后600毫秒左右,走缓存就100毫秒左右