1. 程式人生 > >效能優化-多執行緒-主執行緒等待子執行緒完成場景

效能優化-多執行緒-主執行緒等待子執行緒完成場景

專案

出行專案

需求

今日訂單查詢優化

場景描述

根據時間查詢出今日訂單,根據訂單去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毫秒左右