性能优化-多线程-主线程等待子线程完成场景

项目

出行项目

需求

今日订单查询优化

场景描述

根据时间查询出今日订单,根据订单去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毫秒左右

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值