效能優化-多執行緒-主執行緒等待子執行緒完成場景
阿新 • • 發佈:2018-12-21
專案
出行專案
需求
今日訂單查詢優化
場景描述
根據時間查詢出今日訂單,根據訂單去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毫秒左右