分布式系統測試–使用HttpServer的一個并發問題

發表于:2014-02-27來源:豆瓣作者:@行知-追尋技術之美點擊數: 標簽:HttpServer
分布式系統測試–使用HttpServer的一個并發問題 上周發布的一個系統,出現了一個很詭異的現象。抽象一下描述,問題大概就是這樣的: 需求: 一次http請求,通過url的params來讀取服務器上的一個日志,并將日志內容返回給用戶。

  上周發布的一個系統,出現了一個很詭異的現象。抽象一下描述,問題大概就是這樣的:

  需求: 一次http請求,通過url的params來讀取服務器上的一個日志,并將日志內容返回給用戶。

  問題表現:存在一定的機率,一次請求返回的內容,與期望的內容不一致。也就是所謂的串日志問題。

  這個問題出現后,我們都認為后臺請求應該沒有問題,因為我們是直接用的JDK自帶的HttpServer來起的服務,心中對JDK還是有一些信任的。所以我們還是前端發送請求的時候發亂了,返回的結果和請求是能夠匹配的。

  為了定位問題,我們在后臺加了些日志,對比了每次請求和請求的返回內容。在1~2天的監控下,發現真的出現了請求和返回內容之間存在不一致的地方,而前端發送的請求沒有錯,問題直接定位到后臺處理的問題。

  簡單說來,大致就類似這樣:前端發送請求 http://127.0.0.1/getlog?id=123&path=testlog&type=r,期望是讀取的是testlog文件,但是實際確是把devlog的內容返回給前端了。由于定位到這個現象,問題也就可以定位到后臺讀文件拿參數是錯的。

  我們怎么獲取url中的參數呢,翻下源碼,如下:

  Map Params = (Map) httpExchange.getAttribute(“parameters”);

  JDK上是這么定義這個方法的,getAttribute:

  Filter modules may store arbitrary objects with HttpExchange instances as an out-of-band communication mechanism. Other Filters or the exchange handler may then access these objects.

  并沒有說他存在線程不安全,至于到底是不是這里出現的問題了,寫了一個測試程序:

  1000并發,下發10000個請求,看看出現多少次這樣的問題。結果很明顯,統計下來,發現存在這種問題的數據接近1000條左右,這問題就非常明顯了。

  httpExchange.getAttribute()在一定并發的情況下,存在線程不安全的問題。

  public class HttpServersPerfTest extends BaseCase {

  private static final Log logger = LogFactory.getLog(AlisaNodeHttpServersPerfTest.class);

  int maxThread = 1000;

  private int totalTask = 1000;

  @Test

  public void testRequestRunningLog() throws InterruptedException {

  execute();

  }

  private void execute() throws InterruptedException {

  ExecutorService pool = Executors.newFixedThreadPool(maxThread);

  final CountDownLatch countDownLatch = new CountDownLatch(totalTask);

  for (int n = 0; n < totalTask; n++) {

  pool.execute(new RequestHttpServerThread(countDownLatch));

  }

  try {

  countDownLatch.await();

  System.err.println(“==============>>>>> 下發結束” );

  } catch (InterruptedException e) {

  e.printStackTrace();

  }

  }

  private class RequestHttpServerThread implements Runnable {

  private CountDownLatch countDownLatch;

  public RequestHttpServerThread(CountDownLatch countDownLatch) {

  this.countDownLatch = countDownLatch;

  }

  /**

  * 任務執行,這里實際上在服務器上準備好一堆文件,文件內容按行將文件名寫入

  * 讀取到內容可以和文件名進行比較

  */

  public void run() {

  Random rd = new Random();

  int index = rd.nextInt(60);

  if (index < 10){

  index = index + 10;

  }

  String indexStr = Integer.toString(index);

  String path = indexStr+”-”+indexStr+”-”+indexStr+”/”;

  LogGetter logGetter = new LogGetter();

  logGetter.setPath(path);

  String runningLog = logGetter.getRunningLog(0L);

  if(runningLog.contains( “T3_000000000″ + indexStr)){

  System.out.println(“==============>”+”exit”);

  }else

  {

  String[] lines = runningLog.split(“\n”);

  System.out.println(“期望:” + indexStr + “\n” + “實際請求的:” + lines[0] + “\n”);

  }

  }

  }

  }

  定位到問題之后,我們就決定放棄使用這個方法,自己重寫一個parserQuery的方法,后臺拿到url之后重新對url進行參數的解析。

  Map params = HttpUtils.parseQuery(httpExchange.getRequestURI().getQuery());

  public static Map parserQuery(String query) throws UnsupportedEncodingException {

  Map parameters = new HashMap();

  if (query != null) {

  String pairs[] = query.split(“[&]“);

  for (String pair : pairs) {

  String param[] = pair.split(“[=]“);

  String key = null;

  String value = null;

原文轉自:http://www.anti-gravitydesign.com/deltestingadmindd/

国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97