HTTP2 应用开发

最近更新时间: 2024-10-17 17:10:00

操作场景

本文以 provider-demo 和 consumer-demo 两个工程为例为你介绍 TSF 应用配置 http2 的操作方法。

前提条件

  • 【安装1.8或以上版本 JDK】

  • 【下载 TSF Demo 工程】 (springboot 版本2.0+,tomcat 版本8.5+)

注意:

  • 推荐使用 1.29.0-Finchley-RELEASE。
  • 本文 springboot-2.0.9.RELEASE 和 tomcat-8.5.56 为例。

操作步骤

步骤1. 制作 SSL 证书

通过 JDK 自带的 keytool 执行以下命令:

keytool -genkey -alias tomcat -keyalg RSA -keystore ./keystore.jks -storepass 123456

执行成功后当前目录下会生成 keystore.jks 证书文件。

步骤2. 改造 provider-demo 工程

  1. 复制 jks 证书文件到 provider-demo 工程的 resources 目录下。

  2. 修改 spring 配置文件,在bootstrap.yaml 文件中增加如下配置。

     server.http2.enabled=true
     server.ssl.key-store=classpath:keystore.jks
     server.ssl.key-store-password: 123456
  3. 启动 provider-demo,浏览器访问 https://127.0.0.1:18081/echo/1

Chrome 可能会提示“您的连接不是私密连接”。

只需单击任意空白处,键入“thisisunsafe”即可。

打开 Console,Protocol 的值为 h2 表示配置成功。

  1. 开启 http 访问端口(可选)。

由于此时只能通过 https 方式访问,可加入 Tomcat 配置开放新端口来支持 http 访问。

  1. 在启动类中增加 Bean。

java package com.tsf.demo.provider;

  import org.apache.catalina.connector.Connector;
  import org.springframework.beans.factory.annotation.Value;
  import org.springframework.boot.SpringApplication;
  import org.springframework.boot.autoconfigure.SpringBootApplication;
  import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
  import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
  import org.springframework.cloud.openfeign.EnableFeignClients;
  import org.springframework.context.annotation.Bean;
  import org.springframework.tsf.annotation.EnableTsf;

  @SpringBootApplication
  @EnableFeignClients // 使用Feign微服务调用时请启用
  @EnableTsf
  public class ProviderApplication {

      @Value("${http.port}")
      private Integer port;

      /**
       * Tomcat增加支持http访问
       **/
      @Bean
      public ServletWebServerFactory servletContainer() {
          TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
          Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
  //        connector.setSecure(false);
  //        connector.setScheme("http");
          connector.setPort(port);
          tomcat.addAdditionalTomcatConnectors(connector);
          return tomcat;
      }

      public static void main(String[] args) {
          SpringApplication.run(ProviderApplication.class, args);
      }
  }
  1. 在 Bootstrap.yml 配置文件中增加自定义配置。

   http.port=18082
  1. 重启 provider-demo,浏览器访问 http://127.0.0.1:18082/echo/1

此时 provider-demo 完成支持 https 和 http(通过不同端口访问),其中 https 访问时使用 http2 协议。

步骤3. 改造 consumer-demo 工程

  1. proxy 中的 @FeignClient 注解需指定 https 方式访问。

    @FeignClient(name = "https://provider-demo")
  1. RestTemplate 同理。

    restTemplate.getForObject("https://provider-demo/echo/" + str, String.class);
  1. 通过注入不同的 bean 选择是否使用SSL证书认证(以下步骤二选一):
  • 使用SSL证书认证访问方式
  1. 复制 jks 证书文件到 consumer-demo 工程的 resources 目录。

  2. spring 配置文件中增加自定义配置。

test-ssl-config.key-store=classpath:keystore.jks
test-ssl-config.key-store-password: 123456
  1. 修改 bean(restTemplate、feignClient)

   package com.tsf.demo.consumer;

   import feign.Client;
   import org.apache.http.conn.ssl.NoopHostnameVerifier;
   import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
   import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
   import org.apache.http.impl.client.CloseableHttpClient;
   import org.apache.http.impl.client.HttpClients;
   import org.apache.http.ssl.SSLContexts;
   import org.springframework.beans.factory.annotation.Value;
   import org.springframework.boot.SpringApplication;
   import org.springframework.boot.autoconfigure.SpringBootApplication;
   import org.springframework.cloud.client.loadbalancer.LoadBalanced;
   import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
   import org.springframework.cloud.openfeign.EnableFeignClients;
   import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
   import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
   import org.springframework.context.annotation.Bean;
   import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
   import org.springframework.tsf.annotation.EnableTsf;
   import org.springframework.util.ResourceUtils;
   import org.springframework.web.client.AsyncRestTemplate;
   import org.springframework.web.client.RestTemplate;

   import javax.net.ssl.*;
   import java.io.FileInputStream;
   import java.io.IOException;
   import java.io.InputStream;
   import java.security.*;
   import java.security.cert.CertificateException;

   @SpringBootApplication
   @EnableFeignClients // 使用Feign微服务调用时请启用
   @EnableTsf
   public class ConsumerApplication {

       @Value("${test-ssl-config.key-store}")
       private String file;
       @Value("${test-ssl-config.key-store-password}")
       private String password;

       @LoadBalanced
       @Bean
       public RestTemplate restTemplate() {
           SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(getSSLSocket(file, password),
                   new String[]{"TLSv1"},
                   null,
                   NoopHostnameVerifier.INSTANCE);
           CloseableHttpClient httpClient = HttpClients.custom()
                   .setSSLSocketFactory(csf)
                   .build();
           HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
           requestFactory.setHttpClient(httpClient);
           return new RestTemplate(requestFactory);
       }

       @Bean
       public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) {
           return new LoadBalancerFeignClient(
               new Client.Default(getSSLSocket(file, password).getSocketFactory(), (hostname, session) -> true), cachingFactory, clientFactory
           );
       }

       public static SSLContext getSSLSocket(String file, String password) {
           SSLContext sslContext = null;
           try {
               KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
               InputStream keyStoreInput = new FileInputStream(ResourceUtils.getFile(file));
               keyStore.load(keyStoreInput, password.toCharArray());

               KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
               InputStream trustStoreInput = new FileInputStream(ResourceUtils.getFile(file));
               trustStore.load(trustStoreInput, null);
               sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, password.toCharArray())
                       .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build();
           } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | CertificateException | IOException | UnrecoverableKeyException e) {
               e.printStackTrace();
           }
           return sslContext;
       }

       @LoadBalanced
       @Bean
       public AsyncRestTemplate asyncRestTemplate() {
           return new AsyncRestTemplate();
       }

       public static void main(String[] args) {
           SpringApplication.run(ConsumerApplication.class, args);
       }
   }
  • 忽略SSL认证方式 只需要修改 bean(restTemplate、feignClient)

与第一种方式的差异在 getSSLSocket() 方法。

java

package com.tsf.demo.consumer;</dx-codeblock>

import feign.Client;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.tsf.annotation.EnableTsf;
import org.springframework.web.client.AsyncRestTemplate;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.*;
import java.security.*;
import java.security.cert.X509Certificate;

@SpringBootApplication
@EnableFeignClients // 使用Feign微服务调用时请启用
@EnableTsf
public class ConsumerApplication {

        @LoadBalanced
        @Bean
        public RestTemplate restTemplate() {
                SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(getSSLSocket(),
                                new String[]{"TLSv1"},
                                null,
                                NoopHostnameVerifier.INSTANCE);
                CloseableHttpClient httpClient = HttpClients.custom()
                                .setSSLSocketFactory(csf)
                                .build();
                HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
                requestFactory.setHttpClient(httpClient);
                return new RestTemplate(requestFactory);
        }

        @Bean
        public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) {
                return new LoadBalancerFeignClient(
                        new Client.Default(getSSLSocket().getSocketFactory(), (hostname, session) -> true), cachingFactory, clientFactory
                );
        }

        public static SSLContext getSSLSocket() {
                SSLContext sslContext = null;
                try {
                        sslContext = SSLContext.getInstance("TLS");
                        X509TrustManager tm = new X509TrustManager() {
                                @Override
                                public void checkClientTrusted(X509Certificate[] chain, String authType) {}

                                @Override
                                public void checkServerTrusted(X509Certificate[] chain, String authType) {}

                                @Override
                                public X509Certificate[] getAcceptedIssuers() {
                                        return null;
                                }
                        };
                        sslContext.init(null, new TrustManager[]{tm}, null);
                } catch (NoSuchAlgorithmException | KeyManagementException e) {
                        e.printStackTrace();
                }
                return sslContext;
        }

        @LoadBalanced
        @Bean
        public AsyncRestTemplate asyncRestTemplate() {
                return new AsyncRestTemplate();
        }

        public static void main(String[] args) {
                SpringApplication.run(ConsumerApplication.class, args);
        }

}
  1. 验证结果。

启动 consumer-demo,浏览器访问 http://127.0.0.1:18083/echo-feign/123

浏览器访问 http://127.0.0.1:18083/echo-rest/123