最近网上值机电子客票后台开发,其中的有个需求要求一次导入多个PNR(Passenger Name Record),并且判断这些导入的PNR是否满足同时导入的要求,并且完成相关PNR信息的提取和整理。开始我按照一般的思路完成了需求的功能,然而我发现导入一个PNR一般都在2秒钟以上,要是网络不好的话4、5秒都出现过,所以对自己开发的功能很不满意,这样的程序自己都接受不了,何况真正在生产线上工作的客服人员呢?于是,并发提取PNR的念头产生了。对于刚刚工作,又对Java多线程不熟悉的我这也许是个挑战,因为我只是知道有多线程这个概念,而没有真正的去使用过,去实践过。于是我到网上去找资料,查API文档,终于把这个难题放倒了。
GetPnrInfo.java类提取Pnr信息,实现了Ruable接口:
package com.feinar.cbs.ibe; import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.concurrent.CountDownLatch; import com.feinar.cbs.delivery.pojo.Deliveryorders; import com.feinar.cbs.flight.order.pojo.Passenger; import com.feinar.cbs.flight.order.pojo.Segment; import com.feinar.cbs.flight.order.pojo.SegmentId; import com.feinar.cbs.flight.order.pojo.Segmentprice; import com.feinar.cbs.flight.order.pojo.SegmentpriceId; import com.travelsky.ibe.client.FD; import com.travelsky.ibe.client.FDResult; import com.travelsky.ibe.client.FF; import com.travelsky.ibe.client.FFResult; import com.travelsky.ibe.client.pnr.PNRAirSeg; import com.travelsky.ibe.client.pnr.PNRContact; import com.travelsky.ibe.client.pnr.PNRInfant; import com.travelsky.ibe.client.pnr.PNRPassenger; import com.travelsky.ibe.client.pnr.PNRSSR; import com.travelsky.ibe.client.pnr.RT; import com.travelsky.ibe.client.pnr.RTResult; public class GetPnrInfo implements Runnable ... { private final String ptype[][] = ... { ... { "" , " CH " , " IN " } , ... { " 成人 " , " 儿童 " , " 婴儿 " } } ; private String pnr; private String errormsg; private List < Passenger > passengerlist[]; private List < Segment > segmentlist; private Deliveryorders deliveryorders; private CountDownLatch latch; public GetPnrInfo(String pnr, CountDownLatch latch) ... { this .pnr = pnr; this .latch = latch; this .errormsg = "" ; this .deliveryorders = new Deliveryorders(); this .segmentlist = new ArrayList < Segment > (); this .passengerlist = new ArrayList[ 3 ]; for ( int i = 0 ; i < 3 ; i ++ ) ... { this .passengerlist[i] = new ArrayList < Passenger > (); } } public GetPnrInfo(String pnr, String errormsg, List < Passenger > passengerlist[], List < Segment > segmentlist, Deliveryorders deliveryorders, CountDownLatch latch) ... { this .pnr = pnr; this .errormsg = errormsg; this .passengerlist = passengerlist; this .segmentlist = segmentlist; this .deliveryorders = deliveryorders; this .latch = latch; } public void run() ... { try ... { parsePnr(); } catch (Exception e) ... { this .setErrormsg(e.getMessage()); } latch.countDown(); } public void parsePnr() throws Exception ... { try ... { RT rt = new RT(); RTResult rtresult = new RTResult(); rtresult = rt.retrieve( this .getPnr()); // System.out.println("****** "+this.getPnr()); // 获取Pnr中的旅客信息 // 获得证件号组,常旅客号组 String foidcard[] = new String[rtresult.getPassengersCount()]; String fqtvcard[] = new String[rtresult.getPassengersCount()]; for ( int j = 0 ; j < rtresult.getSSRsCount(); j ++ ) ... { PNRSSR pnrssr = rtresult.getSSRAt(j); // System.out.println(pnrssr.getPsgrID().substring(1)+" card // "+pnrssr.getServeInfo().substring(12)); if (pnrssr.getSSRType().equalsIgnoreCase( " foid " )) foidcard[Integer.parseInt(pnrssr.getPsgrID().substring( 1 )) - 1 ] = pnrssr .getServeInfo().substring( 12 ); else if (pnrssr.getSSRType().equalsIgnoreCase( " fqtv " )) fqtvcard[Integer.parseInt(pnrssr.getPsgrID().substring( 1 )) - 1 ] = pnrssr .getServeInfo().substring( 12 ); } // 获取成人组,儿童组 getPassengers for ( int j = 0 ; j < rtresult.getPassengersCount(); j ++ ) ... { Passenger passenger = new Passenger(); PNRPassenger pnrp = rtresult.getPassengerAt(j); passenger.setPsgname(pnrp.getName()); passenger.setType(String.valueOf(pnrp.getType())); passenger.setPnrno(rtresult.getPnrcode()); // System.out.println("*** " + j + " **** " + foidcard[j]); passenger.setIdtype(foidcard[j].substring( 0 , 2 )); passenger.setIdcard(foidcard[j].substring( 2 , foidcard[j] .indexOf( " / " ))); passenger.setMembercardnumber(fqtvcard[j] == null ? null : fqtvcard[j].substring(2 , fqtvcard[j].indexOf( " / " ))); this .passengerlist[pnrp.getType()].add(passenger); passenger = null ; } foidcard = null ; fqtvcard = null ; // 获取婴儿组 getInfants for ( int j = 0 ; j < rtresult.getInfantsCount(); j ++ ) ... { Passenger passenger = new Passenger(); passenger.setType( " 2 " ); PNRInfant infant = rtresult.getInfantAt(j); passenger.setPsgname(infant.getName()); passenger.setBirthdate(infant.getBrith()); passenger.setCarriedby(rtresult.getPassengerAt( Integer.parseInt(infant.getCarrier().substring( 1 )) - 1 ) .getName()); this .passengerlist[ 2 ].add(passenger); passenger = null ; } // 获取Pnr 中的航班信息 // 提取航段信息 SimpleDateFormat format = new SimpleDateFormat( " yyyy-MM-dd HH:mm:ss " ); for ( int i = 0 ; i < rtresult.getAirSegsCount(); i ++ ) ... { Segment seg = new Segment(); PNRAirSeg pnrseg = rtresult.getAirSegAt(i); seg.setId( new SegmentId( null , new Long(i))); seg.setCarrier(pnrseg.getAirNo().substring( 0 , 2 )); seg.setFlightno(pnrseg.getAirNo().substring( 2 )); seg.setDepairport(pnrseg.getOrgCity()); seg.setArrairport(pnrseg.getDesCity()); seg.setDeptime( new Timestamp(format.parse( pnrseg.getDepartureTime().toLocaleString()).getTime())); seg.setArrtime( new Timestamp(format.parse( pnrseg.getArrivalTime().toLocaleString()).getTime())); seg.setCabin(String.valueOf(pnrseg.getFltClass())); // 查询机型 FFResult ffr = new FFResult(); try ... { ffr = new FF().flightTime(pnrseg.getAirNo(), pnrseg .getDepartureTime()); seg.setPlanetype(ffr.getPlaneModel()); } catch (Exception e) ... { System.out.println( " 系统在查询航班 " + pnrseg.getAirNo() + " 机型时出错; " ); throw new Exception( " 系统在查询航班 " + pnrseg.getAirNo() + " 机型时出错; " ); } seg.setSeatstatus(pnrseg.getActionCode());// 待定 Segmentprice segmentprice = new Segmentprice(); segmentprice.setCarrier(seg.getCarrier()); segmentprice.setFlightno(seg.getFlightno()); segmentprice.setCabin(seg.getCabin()); segmentprice.setDepairport(seg.getDepairport()); segmentprice.setArrairport(seg.getArrairport()); segmentprice.setArrtime(seg.getArrtime()); segmentprice.setFlightdate(seg.getDeptime()); segmentprice.setPlanetype(seg.getPlanetype()); segmentprice.setId( new SegmentpriceId( null , seg.getId() .getSegorder())); // 查询价格 FD fd = new FD(); FDResult rdresult; for ( int j = 0 ; j < 3 ; j ++ ) ... { try ... { SimpleDateFormat formater = new SimpleDateFormat( " ddMMMyy " , new Locale( " EN " )); rdresult = fd.findPrice(seg.getDepairport(), seg .getArrairport(), "" + formater.format(seg.getDeptime().getTime()) .toUpperCase(), seg.getCarrier(), seg .getPlanetype(), ptype[ 0 ][j]); for ( int k = 0 ; k < rdresult.getElementNum(); k ++ ) ... { if (rdresult.getCabinType(k).equals(seg.getCabin())) ... { switch (j) ... { case 0 : segmentprice .setAdultprice(Double .valueOf(rdresult .getSinglePrice(k))); break ; case 1 : segmentprice .setChildprice(Double .valueOf(rdresult .getSinglePrice(k))); break ; case 2 : segmentprice .setInfantprice(Double .valueOf(rdresult .getSinglePrice(k))); break ; default : break ; } } } } catch (Exception e1) ... { System.out.println( " 系统在查询航班 " + pnrseg.getAirNo() + " 的 " + ptype[ 1 ][j] + " 价时出错; " ); throw new Exception( " 系统在查询航班 " + pnrseg.getAirNo() + " 的 " + ptype[ 1 ][j] + " 价时出错; " ); } } seg.setSegmentprice(segmentprice); segmentlist.add(seg); segmentprice = null ; seg = null ; // 获取配送联系人 // System.out.println(rtresult.getContactsCount()); PNRContact contact = rtresult.getContactAt( 0 ); String contactinfo[] = contact.getContact().split( " / " ); String phone = "" ; if (contactinfo.length == 1 ) ... { phone = contactinfo[ 0 ]; } else ... { deliveryorders.setContactname(contactinfo[ 0 ]); phone = contactinfo[ 1 ]; } if (phone.startsWith( " 0 " )) deliveryorders.setContacttel(phone); else deliveryorders.setMobilephone(phone); } } catch (Exception e) ... { System.out.println( " 导入【 " + pnr + " 】出错; " ); throw new Exception( " 导入【 " + pnr + " 】出错; " ); } } public Deliveryorders getDeliveryorders() ... { return deliveryorders; } public void setDeliveryorders(Deliveryorders deliveryorders) ... { this .deliveryorders = deliveryorders; } public String getErrormsg() ... { return errormsg; } public void setErrormsg(String errormsg) ... { this .errormsg = errormsg; } public CountDownLatch getLatch() ... { return latch; } public void setLatch(CountDownLatch latch) ... { this .latch = latch; } public List < Passenger > [] getPassengerlist() ... { return passengerlist; } public void setPassengerlist(List < Passenger > [] passengerlist) ... { this .passengerlist = passengerlist; } public String getPnr() ... { return pnr; } public void setPnr(String pnr) ... { this .pnr = pnr; } public List < Segment > getSegmentlist() ... { return segmentlist; } public void setSegmentlist(List < Segment > segmentlist) ... { this .segmentlist = segmentlist; } }
LoadPnrAction.java多线程提取Pnr并判断是否满足条件,如果满足,则整合相关信息:
package com.feinar.cbs.flight.order.struts.action; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import com.feinar.cbs.delivery.pojo.Deliveryorders; import com.feinar.cbs.flight.order.pojo.Passenger; import com.feinar.cbs.flight.order.pojo.Segment; import com.feinar.cbs.ibe.GetPnrInfo; public class LoadPnrAction extends Action ... { @Override public ActionForward execute(ActionMapping mapping, ActionForm webForm, HttpServletRequest request, HttpServletResponse response) ... { Long l = System.currentTimeMillis(); String parseError = " 在导入PNR时发生了以下错误:/n " ; String forword = "" ; if (request.getParameter( " forword " ) != null && ! "" .equals(request.getParameter( " forword " ))) ... { forword = (String) request.getParameter( " forword " ); } if ( "" .equals( " forword " )) ... { parseError += " /n 非法请求来源; " ; request.getSession().setAttribute( " loadpnrError " , parseError); return mapping.findForward( " tomainjsp " ); } String pnrs = "" ; // DKD4L/DKD4Q/DKD50 // DKD56 if (request.getParameter( " pnrs " ) != null && ! "" .equals(request.getParameter( " pnrs " ))) ... { pnrs = (String) request.getParameter( " pnrs " ); } // "CSJB8/CSHMD/CSHMF/CSJBB/CSJB9"; // 获取pnr[] request.getSession().setAttribute( " pnrs " , pnrs); String pnr[] = pnrs.split( " / " ); System.out.println( " *** pnrs : " + pnrs); if (pnr.length < 1 ) ... { parseError += " /n 请输入要导入的PNR,并控制在5个以内; " ; request.getSession().setAttribute( " loadpnrError " , parseError); return mapping.findForward(forword); } if (pnr.length > 5 ) ... { parseError += " /n 你输入了 " + pnr.length + " 个PNR,请控制在5个以内; " ; System.out.println( " ***** parseError " + parseError); request.getSession().setAttribute( " loadpnrError " , parseError); return mapping.findForward(forword); } // 解析pnr List < Passenger > passengerList = new ArrayList < Passenger > (); List < Segment > segmentList = new ArrayList < Segment > (); Deliveryorders delivery = new Deliveryorders(); int pnrNum = pnr.length; Thread threads[] = new Thread[pnrNum]; GetPnrInfo pnrInfo[] = new GetPnrInfo[pnrNum]; CountDownLatch latch = new CountDownLatch(pnrNum); for ( int i = 0 ; i < pnrNum; i ++ ) ... { pnrInfo[i] = new GetPnrInfo(pnr[i], latch); threads[i] = new Thread(pnrInfo[i]); threads[i].start(); // 千万记住不能使用run()方法,否则就是简单的函数调用而不是多线程处理 } try ... { latch.await(); } catch (InterruptedException exc) ... { } System.out.println(" ******* " + pnrInfo[ 0 ].getSegmentlist()); boolean flag = false ; // 获取、解析PNR时是否发生错误标志 for ( int i = 0 ; i < pnrInfo.length; i ++ ) ... { if ( ! "" .equals(pnrInfo[i].getErrormsg())) ... { flag = true ; parseError += " /n " + pnrInfo[i].getErrormsg() + " " ; } } if (flag) ... { System.out.println( " 解析Pnr时有错误,退出 " + parseError); System.out.println( " *** PNR 解析耗时: " + (System.currentTimeMillis() - l)); request.getSession().setAttribute( " loadpnrError " , parseError); return mapping.findForward(forword); } // 检测是否同天、同航段、同窗位 List < Segment > seglist0 = pnrInfo[ 0 ].getSegmentlist(); for ( int i = 1 ; i < pnrInfo.length; i ++ ) ... { List < Segment > seglisti = pnrInfo[i].getSegmentlist(); if (seglisti.size() == seglist0.size()) ... { for ( int j = 0 ; j < seglist0.size(); j ++ ) ... { if ( ! seglisti.get(j).getCarrier().equals( seglist0.get(j).getCarrier()) || ! seglisti.get(j).getFlightno().equals( seglist0.get(j).getFlightno()) || ! seglisti.get(j).getPlanetype().equals( seglist0.get(j).getPlanetype()) || ! seglisti.get(j).getCabin().equals( seglist0.get(j).getCabin()) || ! seglisti.get(j).getDeptime().equals( seglist0.get(j).getDeptime())) ... { System.out.println( " 系统检测到【 " + pnr[i] + " 】与【 " + pnr[i - 1 ] + " 】不是同天、同航段、同窗位,不能同时导入; " ); parseError += " /n 系统已检测到【 " + pnr[i] + " 】与【 " + pnr[i - 1 ] + " 】不是同天、同航段、同窗位,不能同时导入; " ; request.getSession().setAttribute( " loadpnrError " , parseError); System.out.println( " *** PNR 解析已经耗时: " + (System.currentTimeMillis() - l)); return mapping.findForward(forword); } } } else ... { String samestr = "" ; for ( int j = 0 ; j < i; j ++ ) ... { samestr += " , " + pnr[j]; } samestr = samestr.substring( 2 ); System.out.println( " 系统检测到【 " + pnr[i] + " 】与【 " + samestr + " 】航段数不同,不能同时导入; " ); parseError += " /n 系统已检测到【 " + pnr[i] + " 】与【 " + samestr + " 】航段数不同,不能同时导入; " ; request.getSession().setAttribute( " loadpnrError " , parseError); System.out.println( " *** PNR 解析已经耗时: " + (System.currentTimeMillis() - l)); return mapping.findForward(forword); } } seglist0 = null ; // 航班信息 联系信息 segmentList = pnrInfo[ 0 ].getSegmentlist(); delivery = pnrInfo[ 0 ].getDeliveryorders(); // 整理旅客信息 for ( int i = 0 ; i < 3 ; i ++ ) ... { for ( int j = 0 ; j < pnrInfo.length; j ++ ) ... { for ( int k = 0 ; k < pnrInfo[j].getPassengerlist()[i].size(); k ++ ) ... { passengerList.add(pnrInfo[j].getPassengerlist()[i].get(k)); } } } request.getSession().setAttribute( " data " , new Object[] ... { null , segmentList, passengerList, null , null , null , delivery, pnrs } ); System.out.println( " *** PNR 解析耗时: " + (System.currentTimeMillis() - l)); return mapping.findForward( " input " ); } }
现在导入1个PNR大概2-3秒,导入5个需3-4秒导入10个也才3-4秒,由于需求只要求最多导入5个,所以我就没有测更多的数据,反正满足需求就好,完成功能就OK,哈哈。