具體配置以下java
pomgit
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency>
配置文件,配置鏈接數,重試次數,超時時間github
feign: hystrix: enabled: true httpclient: enabled: true #feign的最大鏈接數 max-connections: 200 #feign單個路徑的最大鏈接數 max-connections-per-route: 50
爲何要使用Apache的HTTPClient,由於JDK自帶的HTTP Client在JDK 9才支持HTTP 2.0,加上JDK 8 HTTP Client自己的各類缺陷。web
通常咱們在外網訪問會經過Nginx來進行數據的壓縮gzip,具體能夠參考Nginx開啓Gzip壓縮大幅提升頁面加載速度 spring
可是咱們微服務之間HTTP調用是不通過Nginx的。chrome
配置json
feign: hystrix: enabled: true httpclient: enabled: true #feign的最大鏈接數 max-connections: 200 #feign單個路徑的最大鏈接數 max-connections-per-route: 50 compression: request: enable: true mime-types: text/xml,application/xml,application/json #大小壓縮的限制,只有超過2M的請求數據纔會進行壓縮 min-request-size: 2048 response: enable: true
固然配置了這些,最重要的依然是HTTP 2.0的配置,HTTP 2.0是必需要配置成HTTPS的,有關HTTPS的解釋,能夠參考HTTP協議整理 bootstrap
HTPP 2.0的多路複用和HTTP 1.x的長鏈接的區別:tomcat
有關多路複用技術請參考Netty整理 安全
有關HTTP 2.0更多的解釋會在HTTP協議整理 中後續增長,它其實就是把HTTP的報文頭進行了一次傳輸和存儲在兩端的報文表中,只有變動的時候纔會重發,而且Frame的總體會打碎髮送,到目標機器會從新組裝的技術。
因此咱們須要生成證書,因爲是微服務之間內部調用,因此咱們不須要去公證機關申請證書。
爲Spring Boot項目配置HTTP 2.0
rabbitkeystore.jks
複製到項目的resource目錄下。application.yaml或者
bootstrap.yml中添加以下配置server: port: 8001 ssl: key-store: classpath:rabbitkeystore.jks key-store-password: rabbit key-password: rabbit http2: enabled: true
修改Springboot默認的WEB服務器Tomcat爲undertow,undertow在併發性能上自己要好過Tomcat。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency>
給undertow添加配置信息
server: port: 8001 undertow: # 設置IO線程數, 它主要執行非阻塞的任務,它們會負責多個鏈接, 默認設置每一個CPU核心一個線程 # 不要設置過大,若是過大,啓動項目會報錯:打開文件數過多 io-threads: 16 # 阻塞任務線程池, 當執行相似servlet請求阻塞IO操做, undertow會從這個線程池中取得線程 # 它的值設置取決於系統線程執行任務的阻塞係數,默認值是IO線程數*8 worker-threads: 256 # 如下的配置會影響buffer,這些buffer會用於服務器鏈接的IO操做,有點相似netty的池化內存管理 # 每塊buffer的空間大小,越小的空間被利用越充分,不要設置太大,以避免影響其餘應用,合適便可 buffer-size: 1024 # 是否分配的直接內存(NIO直接分配的堆外內存) direct-buffers: true
此時啓動配置項目
咱們必須使用https://來訪問咱們的API了,固然chrome會報不安全的鏈接,由於它的證書不是實際證書部門的證書。
在火狐中訪問,能夠很明顯的看到這個訪問是HTTP 2.0的
配置調用HTTPS的微服務的客戶端(注意,如下設置只能讓https生效,但還沒法使用http 2.0,有待研究)
rabbittruststore.jks
複製到項目的resource目錄下。注意是調用方的項目中pom
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency>
feign: hystrix: enabled: true httpclient: enabled: true #feign的最大鏈接數 max-connections: 200 #feign單個路徑的最大鏈接數 max-connections-per-route: 50 compression: request: enable: true mime-types: text/xml,application/xml,application/json #大小壓縮的限制,只有超過2M的請求數據纔會進行壓縮 min-request-size: 2048 response: enable: true
以上配置跟服務提供方同樣
設置ssl加密協議的配置類
public class TrustingSSLSocketFactory extends SSLSocketFactory implements X509TrustManager, X509KeyManager { @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } private static final Map<String, SSLSocketFactory> sslSocketFactories = new LinkedHashMap<String, SSLSocketFactory>(); private static final char[] KEYSTORE_PASSWORD = "rabbit".toCharArray(); private final static String[] ENABLED_CIPHER_SUITES = {"TLS_RSA_WITH_AES_256_CBC_SHA"}; private final SSLSocketFactory delegate; private final String serverAlias; private final PrivateKey privateKey; private final X509Certificate[] certificateChain; private TrustingSSLSocketFactory(String serverAlias) { try { SSLContext sc = SSLContext.getInstance("SSL"); sc.init(new KeyManager[] {this}, new TrustManager[] {this}, new SecureRandom()); this.delegate = sc.getSocketFactory(); } catch (Exception e) { throw new RuntimeException(e); } this.serverAlias = serverAlias; if (serverAlias.isEmpty()) { this.privateKey = null; this.certificateChain = null; } else { try { KeyStore keyStore = loadKeyStore(TrustingSSLSocketFactory.class.getResourceAsStream("/rabbittruststore.jks")); this.privateKey = (PrivateKey) keyStore.getKey(serverAlias, KEYSTORE_PASSWORD); java.security.cert.Certificate[] rawChain = keyStore.getCertificateChain(serverAlias); this.certificateChain = Arrays.copyOf(rawChain, rawChain.length, X509Certificate[].class); } catch (Exception e) { throw new RuntimeException(e); } } } public static SSLSocketFactory get() { return get(""); } public synchronized static SSLSocketFactory get(String serverAlias) { if (!sslSocketFactories.containsKey(serverAlias)) { sslSocketFactories.put(serverAlias, new TrustingSSLSocketFactory(serverAlias)); } return sslSocketFactories.get(serverAlias); } static Socket setEnabledCipherSuites(Socket socket) { SSLSocket.class.cast(socket).setEnabledCipherSuites(ENABLED_CIPHER_SUITES); return socket; } private static KeyStore loadKeyStore(InputStream inputStream) throws IOException { try { KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(inputStream, KEYSTORE_PASSWORD); return keyStore; } catch (Exception e) { throw new RuntimeException(e); } finally { inputStream.close(); } } @Override public String[] getDefaultCipherSuites() { return ENABLED_CIPHER_SUITES; } @Override public String[] getSupportedCipherSuites() { return ENABLED_CIPHER_SUITES; } @Override public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { return setEnabledCipherSuites(delegate.createSocket(s, host, port, autoClose)); } @Override public Socket createSocket(String host, int port) throws IOException { return setEnabledCipherSuites(delegate.createSocket(host, port)); } @Override public Socket createSocket(InetAddress host, int port) throws IOException { return setEnabledCipherSuites(delegate.createSocket(host, port)); } @Override public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { return setEnabledCipherSuites(delegate.createSocket(host, port, localHost, localPort)); } @Override public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { return setEnabledCipherSuites(delegate.createSocket(address, port, localAddress, localPort)); } @Override public X509Certificate[] getAcceptedIssuers() { return null; } @Override public String[] getClientAliases(String keyType, Principal[] issuers) { return null; } @Override public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { return null; } @Override public String[] getServerAliases(String keyType, Principal[] issuers) { return null; } @Override public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { return serverAlias; } @Override public X509Certificate[] getCertificateChain(String s) { return certificateChain; } @Override public PrivateKey getPrivateKey(String alias) { return privateKey; } }
FeignHTTP的配置
@Configuration public class FeignConfig { @Bean public Feign.Builder feignBuilder() { final Client trustSSLSockets = client(); return Feign.builder().client(trustSSLSockets); } @Bean public Client client(){ return new Client.Default( TrustingSSLSocketFactory.get(), new NoopHostnameVerifier()); } @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }
最後是寫一個接口,配置成@FeignClient,url必須設置爲https協議的。
@FeignClient(value = "user-center",configuration = FeignConfig.class,url = "https://127.0.0.1:8001") public interface UserClient { @GetMapping("/users-anon/showopencity") Result<List<City>> showOpenCity(); }
最後是測試調用
@RestController public class TestController { @Autowired private UserClient userClient; @GetMapping("/showcity") public Result<List<City>> showCity() { return userClient.showOpenCity(); } }
結果以下