jetty代理jetty_如何在Jetty中使用SPDY
jetty代理jetty
SPDY是Google提出的一種新協(xié)議,是針對網(wǎng)絡(luò)的新協(xié)議。 SPDY與HTTP兼容,但嘗試通過壓縮,多路復(fù)用和優(yōu)先級降低網(wǎng)頁負載。準確地說,快速的目標是:( http://dev.chromium.org/spdy/spdy-whitepaper )。 SPDY項目為Web定義并實現(xiàn)了一個應(yīng)用程序?qū)訁f(xié)議,可大大減少延遲。SPDY的高級目標是:
- 旨在將頁面加載時間減少50%。 我們的初步結(jié)果已經(jīng)接近這個目標(見下文)。
- 以最小化部署復(fù)雜性。 SPDY使用TCP作為基礎(chǔ)傳輸層,因此不需要更改現(xiàn)有的網(wǎng)絡(luò)基礎(chǔ)結(jié)構(gòu)。
- 為了避免網(wǎng)站作者對內(nèi)容進行任何更改。 支持SPDY的唯一更改是在客戶端用戶代理和Web服務(wù)器應(yīng)用程序中。
- 將對探索協(xié)議感興趣的志趣相投的各方聚集在一起,以解決延遲問題。 我們希望與開源社區(qū)和行業(yè)專家合作開發(fā)此新協(xié)議
一些特定的技術(shù)目標是:
- 允許多個并發(fā)HTTP請求在單個TCP會話中運行。
- 通過壓縮頭并消除不必要的頭來減少HTTP當前使用的帶寬。
- 定義易于實施且服務(wù)器效率高的協(xié)議。 我們希望通過減少邊緣情況并定義易于解析的消息格式來降低HTTP的復(fù)雜性。
- 使SSL成為基礎(chǔ)傳輸協(xié)議,以提高安全性和與現(xiàn)有網(wǎng)絡(luò)基礎(chǔ)結(jié)構(gòu)的兼容性。 盡管SSL確實會帶來延遲損失,但我們認為網(wǎng)絡(luò)的長期發(fā)展取決于安全的網(wǎng)絡(luò)連接。 另外,必須使用SSL以確保跨現(xiàn)有代理的通信不中斷。
- 為了使服務(wù)器能夠啟動與客戶端的通信并在可能的情況下將數(shù)據(jù)推送到客戶端。
安裝專家
在本文中,我們不會過多地研究此協(xié)議的技術(shù)實現(xiàn),但將向您展示如何自己開始使用SPDY并對其進行試驗。 為此,我們將使用在最新版本( http://wiki.eclipse.org/Jetty/Feature/SPDY )中提供SPDY實現(xiàn)的Jetty。
因此,讓我們開始吧。 對于此示例,我們將讓Maven處理依賴關(guān)系。 我們將使用以下POM。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>smartjava.jetty.spdy</groupId><artifactId>SPDY-Example</artifactId><version>0.0.1-SNAPSHOT</version><dependencies><dependency><groupId>org.eclipse.jetty.aggregate</groupId><artifactId>jetty-all-server</artifactId><version>8.1.2.v20120308</version><type>jar</type><scope>compile</scope><exclusions><exclusion><artifactId>mail</artifactId><groupId>javax.mail</groupId></exclusion></exclusions></dependency><dependency><groupId>org.eclipse.jetty.spdy</groupId><artifactId>spdy-jetty</artifactId><version>8.1.2.v20120308</version></dependency><dependency><groupId>org.eclipse.jetty.spdy</groupId><artifactId>spdy-core</artifactId><version>8.1.2.v20120308</version></dependency><dependency><groupId>org.eclipse.jetty.spdy</groupId><artifactId>spdy-jetty-http</artifactId><version>8.1.2.v20120308</version></dependency><dependency><groupId>org.eclipse.jetty.npn</groupId><artifactId>npn-api</artifactId><version>8.1.2.v20120308</version><scope>provided</scope></dependency></dependencies> </project>NTP TLS擴展
通過此POM,將加載正確的庫,因此我們可以開始在Jetty中使用特定的SPDY類。 但是,在真正使用SPDY之前,我們還需要配置Java以使用TLS協(xié)議的擴展:TLS下一協(xié)議協(xié)商或簡稱NPN。 可以在googles技術(shù)說明( http://technotes.googlecode.com/git/nextprotoneg.html )上找到此擴展程序的詳細信息,但總而言之可以解決此問題。 通過TLS與服務(wù)器建立連接時,如果我們想使用不同于HTTP的協(xié)議該怎么辦? 我們不知道服務(wù)器是否支持該協(xié)議,并且由于SPDY專注于速度,因此我們不希望增加往返行程的延遲。 即使有幾種不同的解決方案,但大多數(shù)解決方案都具有不可預(yù)測性,額外的往返次數(shù)或破壞現(xiàn)有代理的影響(有關(guān)更多信息,請參閱( http://www.ietf.org/proceedings/80/slides/tls-1.pdf )。
Google提出的解決方案是使用TLS的擴展機制來確定要使用的協(xié)議。 這稱為“下一協(xié)議協(xié)商”或簡稱NPN。 使用此擴展,在TLS握手期間將執(zhí)行以下步驟:
這導(dǎo)致以下TLS握手:
客戶端服務(wù)器ClientHello(NP擴展)——–>
ServerHello(NP擴展和協(xié)議列表)
證書*
ServerKeyExchange *
證書申請*
<——– ServerHelloDone
證書*
ClientKeyExchange
證書驗證*
[ChangeCipherSpec] NextProtocol
成品——–>
[ChangeCipherSpec] <——–已完成
應(yīng)用數(shù)據(jù)<——->應(yīng)用數(shù)據(jù)
有關(guān)TLS / SSL握手的更多信息,請參閱我以前的文章,該文章有關(guān)如何分析Java SSL錯誤: http : //www.smartjava.org/content/how-analyze-java-ssl-errors 。
因此,我們需要NPN來快速確定我們要使用的協(xié)議。 由于這不是標準的TLS,因此我們需要配置Java以使用NPN。 標準Java還不支持NPN,因此我們不能在標準JVM上運行SPDY。 為了解決這個問題,Jetty創(chuàng)建了一個可以與OpenJDK 7一起使用的NPN實現(xiàn)(有關(guān)更多詳細信息,請參見http://wiki.eclipse.org/Jetty/Feature/NPN )。 您可以從此處下載此實現(xiàn): http : //repo2.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/ ,您必須像這樣將其添加到啟動類路徑中:
在SPDY中包裝HTTP請求
現(xiàn)在,您可以開始使用Jetty的SPDY。 Jetty以兩種不同方式支持此功能。 您可以使用它來將SPDY透明地轉(zhuǎn)換為HTTP,然后再次返回,也可以使用它直接對話SPDY。 讓我們創(chuàng)建一個簡單的服務(wù)器配置,使用啟用SPDY的連接托管一些靜態(tài)內(nèi)容。 為此,我們將使用以下Jetty配置:
import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ResourceHandler; import org.eclipse.jetty.spdy.http.HTTPSPDYServerConnector; import org.eclipse.jetty.util.ssl.SslContextFactory;public class SPDYServerLauncher {public static void main(String[] args) throws Exception {// the server to startServer server = new Server();// the ssl context to useSslContextFactory sslFactory = new SslContextFactory();sslFactory.setKeyStorePath("src/main/resources/spdy.keystore");sslFactory.setKeyStorePassword("secret");sslFactory.setProtocol("TLSv1");// simple connector to add to serve content using spdyConnector connector = new HTTPSPDYServerConnector(sslFactory);connector.setPort(8443);// add connector to the serverserver.addConnector(connector);// add a handler to serve contentContextHandler handler = new ContextHandler();handler.setContextPath("/content");handler.setResourceBase("src/main/resources/webcontent");handler.setHandler(new ResourceHandler());server.setHandler(handler);server.start();server.join();} }由于Jetty還具有非常靈活的XML配置語言,因此您可以使用以下XML配置執(zhí)行相同的操作。
<Configure id="Server" class="org.eclipse.jetty.server.Server"><New id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory"><Set name="keyStorePath">src/main/resources/spdy.keystore</Set><Set name="keyStorePassword">secret</Set><Set name="protocol">TLSv1</Set></New><Call name="addConnector"><Arg><New class="org.eclipse.jetty.spdy.http.HTTPSPDYServerConnector"><Arg><Ref id="sslContextFactory" /></Arg><Set name="Port">8443</Set></New></Arg></Call>// Use standard XML configuration for the other handlers and other// stuff you want to add</Configure>如您所見,我們指定了SSL上下文。 這是必需的,因為SPDY可在TLS上運行。 當我們運行此配置時,Jetty將開始在端口8443上偵聽SPDY連接。 并非所有瀏覽器都支持SPDY,我已經(jīng)使用最新的chrome瀏覽器測試了此示例。 如果瀏覽到https:// localhost:8443 / dummy.html (我創(chuàng)建用來測試的文件),則將看到該文件的內(nèi)容,就像您使用HTTPS請求該文件一樣。 那么這里發(fā)生了什么? 首先,讓我們看一下Chrome提供的SPDY會話視圖,以確定我們是否真的在使用SPDY。 如果您導(dǎo)航到以下網(wǎng)址:chrome:// net-internals /#events&q = type:SPDY_SESSION%20is:active。 您會看到類似下圖的內(nèi)容。
在此視圖中,您可以看到所有當前的SPDY會話。 如果一切配置正確,您還可以看到連接到本地主機的SPDY會話。 額外檢查以查看是否一切正常,以啟用NPN擴展的調(diào)試功能。 您可以通過在用于啟動服務(wù)器的Java代碼中添加以下行來完成此操作:
NextProtoNego。 調(diào)試 = true ;直接使用SPDY協(xié)議
現(xiàn)在我們已經(jīng)可以使用HTTP over SPDY了,讓我們看一下Jetty提供的另一個選項,它允許我們直接發(fā)送和接收SPDY消息。 對于此示例,我們將創(chuàng)建一個客戶端,每5秒向服務(wù)器發(fā)送一條消息。 服務(wù)器以每秒接收的消息數(shù)發(fā)送響應(yīng)到連接的客戶端。 首先,我們創(chuàng)建服務(wù)器代碼。
import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit;import org.eclipse.jetty.server.Server; import org.eclipse.jetty.spdy.SPDYServerConnector; import org.eclipse.jetty.spdy.api.DataInfo; import org.eclipse.jetty.spdy.api.ReplyInfo; import org.eclipse.jetty.spdy.api.Stream; import org.eclipse.jetty.spdy.api.StreamFrameListener; import org.eclipse.jetty.spdy.api.StringDataInfo; import org.eclipse.jetty.spdy.api.SynInfo; import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;public class SPDYListener {public static void main(String[] args) throws Exception {// Frame listener that handles the communication over speedy ServerSessionFrameListener frameListener = new ServerSessionFrameListener.Adapter() {/*** As soon as we receive a syninfo we return the handler for the stream on * this session*/@Overridepublic StreamFrameListener onSyn(final Stream stream, SynInfo synInfo) {// Send a reply to this messagestream.reply(new ReplyInfo(false));// and start a timer that sends a request to this stream every 5 secondsScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();Runnable periodicTask = new Runnable() {private int i = 0;public void run() {// send a request and don't close the streamstream.data(new StringDataInfo("Data from the server " + i++, false));}};executor.scheduleAtFixedRate(periodicTask, 0, 1, TimeUnit.SECONDS);// Next create an adapter to further handle the client input from specific stream.return new StreamFrameListener.Adapter() {/*** We're only interested in the data, not the headers in this* example*/public void onData(Stream stream, DataInfo dataInfo) {String clientData = dataInfo.asString("UTF-8", true);System.out.println("Received the following client data: " + clientData);}};}};// Wire up and start the connectororg.eclipse.jetty.server.Server server = new Server();SPDYServerConnector connector = new SPDYServerConnector(frameListener);connector.setPort(8181);server.addConnector(connector);server.start();server.join();} }客戶端代碼如下所示:
import java.net.InetSocketAddress; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit;import org.eclipse.jetty.spdy.SPDYClient; import org.eclipse.jetty.spdy.api.DataInfo; import org.eclipse.jetty.spdy.api.SPDY; import org.eclipse.jetty.spdy.api.Session; import org.eclipse.jetty.spdy.api.Stream; import org.eclipse.jetty.spdy.api.StreamFrameListener; import org.eclipse.jetty.spdy.api.StringDataInfo; import org.eclipse.jetty.spdy.api.SynInfo;/*** Calls the server every couple of seconds.* * @author jos*/ public class SPDYCaller {public static void main(String[] args) throws Exception {// this listener receives data from the server. It then prints out the dataStreamFrameListener streamListener = new StreamFrameListener.Adapter() {public void onData(Stream stream, DataInfo dataInfo) {// Data received from serverString content = dataInfo.asString("UTF-8", true);System.out.println("SPDY content: " + content);}};// Create clientSPDYClient.Factory clientFactory = new SPDYClient.Factory();clientFactory.start();SPDYClient client = clientFactory.newSPDYClient(SPDY.V2);// Create a session to the server running on localhost port 8181Session session = client.connect(new InetSocketAddress("localhost", 8181), null).get(5, TimeUnit.SECONDS);// Start a new session, and configure the stream listenerfinal Stream stream = session.syn(new SynInfo(false), streamListener).get(5, TimeUnit.SECONDS);//start a timer that sends a request to this stream every secondScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();Runnable periodicTask = new Runnable() {private int i = 0;public void run() {// send a request, don't close the streamstream.data(new StringDataInfo("Data from the client " + i++, false));}};executor.scheduleAtFixedRate(periodicTask, 0, 1, TimeUnit.SECONDS);} }這將在客戶端和服務(wù)器上顯示以下輸出:
客戶:..
SPDY內(nèi)容:來自服務(wù)器的數(shù)據(jù)3
SPDY內(nèi)容:來自服務(wù)器的數(shù)據(jù)4
SPDY內(nèi)容:來自服務(wù)器的數(shù)據(jù)5
SPDY內(nèi)容:來自服務(wù)器的數(shù)據(jù)6
..
服務(wù)器:
…
收到以下客戶端數(shù)據(jù):來自客戶端的數(shù)據(jù)2
收到以下客戶端數(shù)據(jù):來自客戶端的數(shù)據(jù)3
收到以下客戶端數(shù)據(jù):來自客戶端的數(shù)據(jù)4
收到以下客戶端數(shù)據(jù):來自客戶端的數(shù)據(jù)5
…
從內(nèi)聯(lián)注釋中,代碼本身應(yīng)該易于理解。 唯一要記住的是,當您想通過一個流發(fā)送多條數(shù)據(jù)消息時,要確保將StringDataInfo的構(gòu)造函數(shù)的第二個參數(shù)設(shè)置為false。 如果設(shè)置為true,則在發(fā)送數(shù)據(jù)后將關(guān)閉流。
stream.data(new StringDataInfo("Data from the client " + i++, false));這只是顯示了一個簡單的用例,說明如何直接使用SPDY協(xié)議。 可以在Jetty Wiki和SPDY API文檔中找到更多信息和示例。
參考: Smart Java博客上的JCG合作伙伴 Jos Dirksen的如何將SPDY與Jetty一起使用 。
翻譯自: https://www.javacodegeeks.com/2012/04/how-to-use-spdy-with-jetty.html
jetty代理jetty
總結(jié)
以上是生活随笔為你收集整理的jetty代理jetty_如何在Jetty中使用SPDY的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 购买余额佳要注意哪些?
- 下一篇: Apache Camel 2.16发布–