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 工程
复制 jks 证书文件到 provider-demo 工程的 resources 目录下。
修改 spring 配置文件,在bootstrap.yaml 文件中增加如下配置。
server.http2.enabled=true server.ssl.key-store=classpath:keystore.jks server.ssl.key-store-password: 123456
启动 provider-demo,浏览器访问
https://127.0.0.1:18081/echo/1
。
Chrome 可能会提示“您的连接不是私密连接”。
只需单击任意空白处,键入“thisisunsafe”即可。
打开 Console,Protocol 的值为 h2 表示配置成功。
- 开启 http 访问端口(可选)。
由于此时只能通过 https 方式访问,可加入 Tomcat 配置开放新端口来支持 http 访问。
- 在启动类中增加 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);
}
}
- 在 Bootstrap.yml 配置文件中增加自定义配置。
http.port=18082
- 重启 provider-demo,浏览器访问
http://127.0.0.1:18082/echo/1
。
此时 provider-demo 完成支持 https 和 http(通过不同端口访问),其中 https 访问时使用 http2 协议。
步骤3. 改造 consumer-demo 工程
- proxy 中的 @FeignClient 注解需指定 https 方式访问。
@FeignClient(name = "https://provider-demo")
- RestTemplate 同理。
restTemplate.getForObject("https://provider-demo/echo/" + str, String.class);
- 通过注入不同的 bean 选择是否使用SSL证书认证(以下步骤二选一):
- 使用SSL证书认证访问方式
复制 jks 证书文件到 consumer-demo 工程的 resources 目录。
spring 配置文件中增加自定义配置。
test-ssl-config.key-store=classpath:keystore.jks
test-ssl-config.key-store-password: 123456
- 修改 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);
}
}
- 验证结果。
启动 consumer-demo,浏览器访问 http://127.0.0.1:18083/echo-feign/123
。
浏览器访问 http://127.0.0.1:18083/echo-rest/123
。