一直使用的阿里雲ECS,經典網絡類型。為什么不采用典型的專有網絡 VPC 呢?因為考慮到成本嘛,而且也沒有人來專門規划整個網絡的,所以就采用最簡單的經典網絡,每台主機各自為政,都有自己的公網、私網出口,互不影響。
通過這種方式,發現有的服務器根本不需要外網帶寬,比如負載均衡后面的服務器,因為負載均衡都是通過內網傳輸數據的,所以公網帶寬完全就閑置起來了,而且暴露在公網中也比較危險。所以我們就開始購入沒有公網IP的經典網絡ECS。然后就遇到了下面的各種問題。
阿里雲沒有購買公網的情況下,不分配公網IP,也不能訪問外網的資源,只能訪問在同一個機房里面的服務器內網IP。這可怎么辦呢?我們有些業務服務器是需要和外網進行交互的,比如進行資源加速的時候,是需要和CDN廠商服務器進行連接的,為了解決這個問題。我們在該機房中購買了一台公網帶寬比較大的服務器,作為訪問外網的代理服務器,安裝了HTTP代理服務軟件(記住哦,自己架設的代理服務器一定要設置密碼,而且必須是強密碼,不然一兩天之類就會被整崩潰的。)
現在就開始把以前老的應用程序遷移到這台只有內網訪問權限的服務器上,現在這台內網服務器上設置全局的HTTP代理信息,剛開始遷移PHP程序的時候,沒任何問題,因為會自動根據系統的代理信息進行連接,但是Java web程序貌似走不通哎。現在重點講 Java web如何使用代理程序進行連接。
前置條件:Tomcat VM 設置參數:
-Dhttp.proxyHost=10.0.0.1 -Dhttp.proxyPort=1234
我分析了以下幾種情況
- 使用jdk里面自帶的庫進行請求的時候,會自動根據VM參數進行連接。沒有使用第三方庫,詳細代碼參見:

1 private static String sendGetByDefault(String url, String param) { 2 String result = ""; 3 BufferedReader in = null; 4 try { 5 String urlNameString = url + "?" + param; 6 URL realUrl = new URL(urlNameString); 7 // 打開和URL之間的連接 8 URLConnection connection = realUrl.openConnection(); 9 // 設置通用的請求屬性 10 connection.setRequestProperty("accept", "*/*"); 11 connection.setRequestProperty("connection", "Keep-Alive"); 12 connection.setRequestProperty("user-agent", 13 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); 14 // 建立實際的連接 15 connection.connect(); 16 // 獲取所有響應頭字段 17 Map<String, List<String>> map = connection.getHeaderFields(); 18 // 遍歷所有的響應頭字段 19 for (String key : map.keySet()) { 20 System.out.println(key + "--->" + map.get(key)); 21 } 22 // 定義 BufferedReader輸入流來讀取URL的響應 23 in = new BufferedReader(new InputStreamReader( 24 connection.getInputStream())); 25 String line; 26 while ((line = in.readLine()) != null) { 27 result += line; 28 } 29 } catch (Exception e) { 30 System.out.println("發送GET請求出現異常!" + e); 31 e.printStackTrace(); 32 } 33 // 使用finally塊來關閉輸入流 34 finally { 35 try { 36 if (in != null) { 37 in.close(); 38 } 39 } catch (Exception e2) { 40 e2.printStackTrace(); 41 } 42 } 43 return result; 44 }
2. 使用Apache提供的第三方庫,不會根據VM參數進行連接,maven信息參見:
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.1</version> </dependency>
詳細代碼參見:

private static String sendGetByApache(String url, String param) { StringBuilder buffer = new StringBuilder(); DefaultHttpClient client = new DefaultHttpClient(); try { HttpGet get = new HttpGet(url); HttpResponse response = client.execute(get); HttpEntity entity = response.getEntity(); if (entity != null) { BufferedReader br = new BufferedReader(new InputStreamReader(entity.getContent())); String str; while ((str = br.readLine()) != null) { buffer.append(str); } br.close(); } } catch (Exception e) { e.printStackTrace(); } finally { client.getConnectionManager().shutdown(); } return buffer.toString(); }
3. 使用Unirest庫進行請求,不會根據VM代理信息進行連接,maven 信息參見:
<dependency> <groupId>com.mashape.unirest</groupId> <artifactId>unirest-java</artifactId> <version>1.4.7</version> </dependency>
詳細代碼參見:

private static String sendGetByUnirestClient(String url, String param) { com.mashape.unirest.http.HttpResponse<String> res; try { res = Unirest.get("http://baidu.com") .queryString("name", "Mark") .asString(); } catch (UnirestException e) { e.printStackTrace(); throw new RuntimeException(e); } return res.getBody(); }
綜上所述,基本上使用第三方庫的都不會自動使用系統代理,所以我們要對程序進行一下改造。我們建議使用Unirest,因為他提供多種語言的HTTP請求庫,並且風格一致,有利於不同語言之間的協作。
另外,如果在代碼中采用硬編碼的方式,代碼如下:

final String authUser = "proxy username"; final String authPassword = "proxy password"; Authenticator.setDefault( new Authenticator() { @Override public PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication( authUser, authPassword.toCharArray()); } } ); System.setProperty("http.proxyHost", "proxy host"); System.setProperty("http.proxyPort", "proxy password");
通過以上方式,可以使1,3兩種情況自動通過代理進行連接,2依然我行我素。所以在公司內部統一使用某種庫是非常有必要的。