jetty代理jetty_如何在Jetty中使用SPDY

jetty代理jetty

SPDY是Google提出的一种新协议,是针对网络的新协议。 SPDY与HTTP兼容,但尝试通过压缩,多路复用和优先级降低网页负载。准确地说,快速的目标是:( http://dev.chromium.org/spdy/spdy-whitepaper )。 SPDY项目为Web定义并实现了一个应用程序层协议,可大大减少延迟。

SPDY的高级目标是:

  • 旨在将页面加载时间减少50%。 我们的初步结果已经接近这个目标(见下文)。
  • 以最小化部署复杂性。 SPDY使用TCP作为基础传输层,因此不需要更改现有的网络基础结构。
  • 为了避免网站作者对内容进行任何更改。 支持SPDY的唯一更改是在客户端用户代理和Web服务器应用程序中。
  • 将对探索协议感兴趣的志趣相投的各方聚集在一起,以解决延迟问题。 我们希望与开源社区和行业专家合作开发此新协议

一些特定的技术目标是:

  • 允许多个并发HTTP请求在单个TCP会话中运行。
  • 通过压缩头并消除不必要的头来减少HTTP当前使用的带宽。
  • 定义易于实施且服务器效率高的协议。 我们希望通过减少边缘情况并定义易于解析的消息格式来降低HTTP的复杂性。
  • 使SSL成为基础传输协议,以提高安全性和与现有网络基础结构的兼容性。 尽管SSL确实会带来延迟损失,但我们认为网络的长期发展取决于安全的网络连接。 另外,必须使用SSL以确保跨现有代理的通信不中断。
  • 为了使服务器能够启动与客户端的通信并在可能的情况下将数据推送到客户端。

安装专家

在本文中,我们不会过多地研究此协议的技术实现,但将向您展示如何自己开始使用SPDY并对其进行试验。 为此,我们将使用在最新版本( http://wiki.eclipse.org/Jetty/Feature/SPDY )中提供SPDY实现的Jetty。

因此,让我们开始吧。 对于此示例,我们将让Maven处理依赖关系。 我们将使用以下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协议的扩展:TLS下一协议协商或简称NPN。 可以在googles技术说明( http://technotes.googlecode.com/git/nextprotoneg.html )上找到此扩展程序的详细信息,但总而言之可以解决此问题。 通过TLS与服务器建立连接时,如果我们想使用不同于HTTP的协议该怎么办? 我们不知道服务器是否支持该协议,并且由于SPDY专注于速度,因此我们不希望增加往返行程的延迟。 即使有几种不同的解决方案,但大多数解决方案都具有不可预测性,额外的往返次数或破坏现有代理的影响(有关更多信息,请参阅( http://www.ietf.org/proceedings/80/slides/tls-1.pdf )。
Google提出的解决方案是使用TLS的扩展机制来确定要使用的协议。 这称为“下一协议协商”或简称NPN。 使用此扩展,在TLS握手期间将执行以下步骤:

  1. 客户端显示对此扩展程序的支持
  2. 服务器响应此支持并包括支持的协议列表
  3. 客户端发送他要使用的协议,而不必由服务器提供。

这导致以下TLS握手:

客户端服务器

ClientHello(NP扩展)——–>
ServerHello(NP扩展和协议列表)
证书*
ServerKeyExchange *
证书申请*
<——– ServerHelloDone
证书*
ClientKeyExchange
证书验证*
[ChangeCipherSpec] NextProtocol
成品——–>
[ChangeCipherSpec] <——–已完成
应用数据<——->应用数据

有关TLS / SSL握手的更多信息,请参阅我以前的文章,该文章有关如何分析Java SSL错误: http : //www.smartjava.org/content/how-analyze-java-ssl-errors
因此,我们需要NPN来快速确定我们要使用的协议。 由于这不是标准的TLS,因此我们需要配置Java以使用NPN。 标准Java还不支持NPN,因此我们不能在标准JVM上运行SPDY。 为了解决这个问题,Jetty创建了一个可以与OpenJDK 7一起使用的NPN实现(有关更多详细信息,请参见http://wiki.eclipse.org/Jetty/Feature/NPN )。 您可以从此处下载此实现: http : //repo2.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/ ,您必须像这样将其添加到启动类路径中:

java -Xbootclasspath/p:<path_to_npn_boot_jar> ...

在SPDY中包装HTTP请求

现在,您可以开始使用Jetty的SPDY。 Jetty以两种不同方式支持此功能。 您可以使用它来将SPDY透明地转换为HTTP,然后再次返回,也可以使用它直接对话SPDY。 让我们创建一个简单的服务器配置,使用启用SPDY的连接托管一些静态内容。 为此,我们将使用以下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 start
  Server server = new Server();
 
  // the ssl context to use
  SslContextFactory sslFactory = new SslContextFactory();
  sslFactory.setKeyStorePath("src/main/resources/spdy.keystore");
  sslFactory.setKeyStorePassword("secret");
  sslFactory.setProtocol("TLSv1");
 
  // simple connector to add to serve content using spdy
  Connector connector = new HTTPSPDYServerConnector(sslFactory);
  connector.setPort(8443);
 
  // add connector to the server
  server.addConnector(connector);
 
  // add a handler to serve content
  ContextHandler 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配置执行相同的操作。

<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,我已经使用最新的chrome浏览器测试了此示例。 如果浏览到https:// localhost:8443 / dummy.html (我创建用来测试的文件),则将看到该文件的内容,就像您使用HTTPS请求该文件一样。 那么这里发生了什么? 首先,让我们看一下Chrome提供的SPDY会话视图,以确定我们是否真的在使用SPDY。 如果您导航到以下网址:chrome:// net-internals /#events&q = type:SPDY_SESSION%20is:active。 您会看到类似下图的内容。

Chrome中的SPDY

在此视图中,您可以看到所有当前的SPDY会话。 如果一切配置正确,您还可以看到连接到本地主机的SPDY会话。 额外检查以查看是否一切正常,以启用NPN扩展的调试功能。 您可以通过在用于启动服务器的Java代码中添加以下行来完成此操作:

NextProtoNego。 调试 = true ;

直接使用SPDY协议

现在我们已经可以使用HTTP over SPDY了,让我们看一下Jetty提供的另一个选项,它允许我们直接发送和接收SPDY消息。 对于此示例,我们将创建一个客户端,每5秒向服务器发送一条消息。 服务器以每秒接收的消息数发送响应到连接的客户端。 首先,我们创建服务器代码。

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
    */
   @Override
   public StreamFrameListener onSyn(final Stream stream, SynInfo synInfo) {
 
    // Send a reply to this message
    stream.reply(new ReplyInfo(false));
 
    // and start a timer that sends a request to this stream every 5 seconds
    ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
    Runnable periodicTask = new Runnable() {
      private int i = 0;
         public void run() {
          // send a request and don't close the stream
             stream.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 connector
  org.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 data
  StreamFrameListener streamListener = new StreamFrameListener.Adapter() {
 
      public void onData(Stream stream, DataInfo dataInfo)  {
          // Data received from server
          String content = dataInfo.asString("UTF-8", true);
          System.out.println("SPDY content: " + content);
      }
  };
 
  // Create client
  SPDYClient.Factory clientFactory = new SPDYClient.Factory();
  clientFactory.start();
  SPDYClient client = clientFactory.newSPDYClient(SPDY.V2);
 
  // Create a session to the server running on localhost port 8181
  Session session = client.connect(new InetSocketAddress("localhost", 8181), null).get(5, TimeUnit.SECONDS);
 
  // Start a new session, and configure the stream listener
  final Stream stream = session.syn(new SynInfo(false), streamListener).get(5, TimeUnit.SECONDS);
 
  //start a timer that sends a request to this stream every second
  ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
  Runnable periodicTask = new Runnable() {
    private int i = 0;
 
       public void run() {
        // send a request, don't close the stream
        stream.data(new StringDataInfo("Data from the client " + i++, false));
       }
   };
  executor.scheduleAtFixedRate(periodicTask, 0, 1, TimeUnit.SECONDS);
 }
}

这将在客户端和服务器上显示以下输出:

客户:
..
SPDY内容:来自服务器的数据3
SPDY内容:来自服务器的数据4
SPDY内容:来自服务器的数据5
SPDY内容:来自服务器的数据6
..

服务器:

收到以下客户端数据:来自客户端的数据2
收到以下客户端数据:来自客户端的数据3
收到以下客户端数据:来自客户端的数据4
收到以下客户端数据:来自客户端的数据5

从内联注释中,代码本身应该易于理解。 唯一要记住的是,当您想通过一个流发送多条数据消息时,要确保将StringDataInfo的构造函数的第二个参数设置为false。 如果设置为true,则在发送数据后将关闭流。

stream.data(new StringDataInfo("Data from the client " + i++, false));

这只是显示了一个简单的用例,说明如何直接使用SPDY协议。 可以在Jetty WikiSPDY API文档中找到更多信息和示例。

参考: Smart Java博客上的JCG合作伙伴 Jos Dirksen的如何将SPDY与Jetty一起使用


翻译自: https://www.javacodegeeks.com/2012/04/how-to-use-spdy-with-jetty.html

jetty代理jetty

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值