1. 程式人生 > >SpringCloud 原始碼系列(5)—— 負載均衡 Ribbon(下)

SpringCloud 原始碼系列(5)—— 負載均衡 Ribbon(下)

SpringCloud 原始碼系列(4)—— 負載均衡 Ribbon(上)

SpringCloud 原始碼系列(5)—— 負載均衡 Ribbon(下)

五、Ribbon 核心介面

前面已經瞭解到 Ribbon 核心介面以及預設實現如何協作來查詢要呼叫的一個例項,這節再來看下各個核心介面的一些特性及其它實現類。

1、客戶端配置 — IClientConfig

IClientConfig 就是管理客戶端配置的核心介面,它的預設實現類是 DefaultClientConfigImpl。可以看到在建立 IClientConfig 時,設定了 Ribbon 客戶端預設的連線和讀取超時時間為 1 秒,例如讀取如果超過1秒,就會返回超時,這兩個一般需要根據實際情況來調整。、

 1 @Bean
 2 @ConditionalOnMissingBean
 3 public IClientConfig ribbonClientConfig() {
 4     DefaultClientConfigImpl config = new DefaultClientConfigImpl();
 5     // 載入配置
 6     config.loadProperties(this.name);
 7     // 連線超時預設 1 秒
 8     config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);
 9     // 讀取超時預設 1 秒
10     config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);
11     config.set(CommonClientConfigKey.GZipPayload, DEFAULT_GZIP_PAYLOAD);
12     return config;
13 }

CommonClientConfigKey 這個類定義了 Ribbon 客戶端相關的所有配置的鍵常量,可以通過這個類來看有哪些配置。

  1 public abstract class CommonClientConfigKey<T> implements IClientConfigKey<T> {
  2 
  3     public static final IClientConfigKey<String> AppName = new CommonClientConfigKey<String>("AppName"){};
  4 
  5     public static final IClientConfigKey<String> Version = new CommonClientConfigKey<String>("Version"){};
  6 
  7     public static final IClientConfigKey<Integer> Port = new CommonClientConfigKey<Integer>("Port"){};
  8 
  9     public static final IClientConfigKey<Integer> SecurePort = new CommonClientConfigKey<Integer>("SecurePort"){};
 10 
 11     public static final IClientConfigKey<String> VipAddress = new CommonClientConfigKey<String>("VipAddress"){};
 12 
 13     public static final IClientConfigKey<Boolean> ForceClientPortConfiguration = new CommonClientConfigKey<Boolean>("ForceClientPortConfiguration"){}; // use client defined port regardless of server advert
 14 
 15     public static final IClientConfigKey<String> DeploymentContextBasedVipAddresses = new CommonClientConfigKey<String>("DeploymentContextBasedVipAddresses"){};
 16 
 17     public static final IClientConfigKey<Integer> MaxAutoRetries = new CommonClientConfigKey<Integer>("MaxAutoRetries"){};
 18 
 19     public static final IClientConfigKey<Integer> MaxAutoRetriesNextServer = new CommonClientConfigKey<Integer>("MaxAutoRetriesNextServer"){};
 20 
 21     public static final IClientConfigKey<Boolean> OkToRetryOnAllOperations = new CommonClientConfigKey<Boolean>("OkToRetryOnAllOperations"){};
 22 
 23     public static final IClientConfigKey<Boolean> RequestSpecificRetryOn = new CommonClientConfigKey<Boolean>("RequestSpecificRetryOn"){};
 24 
 25     public static final IClientConfigKey<Integer> ReceiveBufferSize = new CommonClientConfigKey<Integer>("ReceiveBufferSize"){};
 26 
 27     public static final IClientConfigKey<Boolean> EnablePrimeConnections = new CommonClientConfigKey<Boolean>("EnablePrimeConnections"){};
 28 
 29     public static final IClientConfigKey<String> PrimeConnectionsClassName = new CommonClientConfigKey<String>("PrimeConnectionsClassName"){};
 30 
 31     public static final IClientConfigKey<Integer> MaxRetriesPerServerPrimeConnection = new CommonClientConfigKey<Integer>("MaxRetriesPerServerPrimeConnection"){};
 32 
 33     public static final IClientConfigKey<Integer> MaxTotalTimeToPrimeConnections = new CommonClientConfigKey<Integer>("MaxTotalTimeToPrimeConnections"){};
 34 
 35     public static final IClientConfigKey<Float> MinPrimeConnectionsRatio = new CommonClientConfigKey<Float>("MinPrimeConnectionsRatio"){};
 36 
 37     public static final IClientConfigKey<String> PrimeConnectionsURI = new CommonClientConfigKey<String>("PrimeConnectionsURI"){};
 38 
 39     public static final IClientConfigKey<Integer> PoolMaxThreads = new CommonClientConfigKey<Integer>("PoolMaxThreads"){};
 40 
 41     public static final IClientConfigKey<Integer> PoolMinThreads = new CommonClientConfigKey<Integer>("PoolMinThreads"){};
 42 
 43     public static final IClientConfigKey<Integer> PoolKeepAliveTime = new CommonClientConfigKey<Integer>("PoolKeepAliveTime"){};
 44 
 45     public static final IClientConfigKey<String> PoolKeepAliveTimeUnits = new CommonClientConfigKey<String>("PoolKeepAliveTimeUnits"){};
 46 
 47     public static final IClientConfigKey<Boolean> EnableConnectionPool = new CommonClientConfigKey<Boolean>("EnableConnectionPool") {};
 48 
 49     /**
 50      * Use {@link #MaxConnectionsPerHost}
 51      */
 52     @Deprecated
 53     public static final IClientConfigKey<Integer> MaxHttpConnectionsPerHost = new CommonClientConfigKey<Integer>("MaxHttpConnectionsPerHost"){};
 54 
 55     /**
 56      * Use {@link #MaxTotalConnections}
 57      */
 58     @Deprecated
 59     public static final IClientConfigKey<Integer> MaxTotalHttpConnections = new CommonClientConfigKey<Integer>("MaxTotalHttpConnections"){};
 60 
 61     public static final IClientConfigKey<Integer> MaxConnectionsPerHost = new CommonClientConfigKey<Integer>("MaxConnectionsPerHost"){};
 62 
 63     public static final IClientConfigKey<Integer> MaxTotalConnections = new CommonClientConfigKey<Integer>("MaxTotalConnections"){};
 64 
 65     public static final IClientConfigKey<Boolean> IsSecure = new CommonClientConfigKey<Boolean>("IsSecure"){};
 66 
 67     public static final IClientConfigKey<Boolean> GZipPayload = new CommonClientConfigKey<Boolean>("GZipPayload"){};
 68 
 69     public static final IClientConfigKey<Integer> ConnectTimeout = new CommonClientConfigKey<Integer>("ConnectTimeout"){};
 70 
 71     public static final IClientConfigKey<Integer> BackoffInterval = new CommonClientConfigKey<Integer>("BackoffTimeout"){};
 72 
 73     public static final IClientConfigKey<Integer> ReadTimeout = new CommonClientConfigKey<Integer>("ReadTimeout"){};
 74 
 75     public static final IClientConfigKey<Integer> SendBufferSize = new CommonClientConfigKey<Integer>("SendBufferSize"){};
 76 
 77     public static final IClientConfigKey<Boolean> StaleCheckingEnabled = new CommonClientConfigKey<Boolean>("StaleCheckingEnabled"){};
 78 
 79     public static final IClientConfigKey<Integer> Linger = new CommonClientConfigKey<Integer>("Linger"){};
 80 
 81     public static final IClientConfigKey<Integer> ConnectionManagerTimeout = new CommonClientConfigKey<Integer>("ConnectionManagerTimeout"){};
 82 
 83     public static final IClientConfigKey<Boolean> FollowRedirects = new CommonClientConfigKey<Boolean>("FollowRedirects"){};
 84 
 85     public static final IClientConfigKey<Boolean> ConnectionPoolCleanerTaskEnabled = new CommonClientConfigKey<Boolean>("ConnectionPoolCleanerTaskEnabled"){};
 86 
 87     public static final IClientConfigKey<Integer> ConnIdleEvictTimeMilliSeconds = new CommonClientConfigKey<Integer>("ConnIdleEvictTimeMilliSeconds"){};
 88 
 89     public static final IClientConfigKey<Integer> ConnectionCleanerRepeatInterval = new CommonClientConfigKey<Integer>("ConnectionCleanerRepeatInterval"){};
 90 
 91     public static final IClientConfigKey<Boolean> EnableGZIPContentEncodingFilter = new CommonClientConfigKey<Boolean>("EnableGZIPContentEncodingFilter"){};
 92 
 93     public static final IClientConfigKey<String> ProxyHost = new CommonClientConfigKey<String>("ProxyHost"){};
 94 
 95     public static final IClientConfigKey<Integer> ProxyPort = new CommonClientConfigKey<Integer>("ProxyPort"){};
 96 
 97     public static final IClientConfigKey<String> KeyStore = new CommonClientConfigKey<String>("KeyStore"){};
 98 
 99     public static final IClientConfigKey<String> KeyStorePassword = new CommonClientConfigKey<String>("KeyStorePassword"){};
100 
101     public static final IClientConfigKey<String> TrustStore = new CommonClientConfigKey<String>("TrustStore"){};
102 
103     public static final IClientConfigKey<String> TrustStorePassword = new CommonClientConfigKey<String>("TrustStorePassword"){};
104 
105     // if this is a secure rest client, must we use client auth too?
106     public static final IClientConfigKey<Boolean> IsClientAuthRequired = new CommonClientConfigKey<Boolean>("IsClientAuthRequired"){};
107 
108     public static final IClientConfigKey<String> CustomSSLSocketFactoryClassName = new CommonClientConfigKey<String>("CustomSSLSocketFactoryClassName"){};
109      // must host name match name in certificate?
110     public static final IClientConfigKey<Boolean> IsHostnameValidationRequired = new CommonClientConfigKey<Boolean>("IsHostnameValidationRequired"){};
111 
112     // see also http://hc.apache.org/httpcomponents-client-ga/tutorial/html/advanced.html
113     public static final IClientConfigKey<Boolean> IgnoreUserTokenInConnectionPoolForSecureClient = new CommonClientConfigKey<Boolean>("IgnoreUserTokenInConnectionPoolForSecureClient"){};
114 
115     // Client implementation
116     public static final IClientConfigKey<String> ClientClassName = new CommonClientConfigKey<String>("ClientClassName"){};
117 
118     //LoadBalancer Related
119     public static final IClientConfigKey<Boolean> InitializeNFLoadBalancer = new CommonClientConfigKey<Boolean>("InitializeNFLoadBalancer"){};
120 
121     public static final IClientConfigKey<String> NFLoadBalancerClassName = new CommonClientConfigKey<String>("NFLoadBalancerClassName"){};
122 
123     public static final IClientConfigKey<String> NFLoadBalancerRuleClassName = new CommonClientConfigKey<String>("NFLoadBalancerRuleClassName"){};
124 
125     public static final IClientConfigKey<String> NFLoadBalancerPingClassName = new CommonClientConfigKey<String>("NFLoadBalancerPingClassName"){};
126 
127     public static final IClientConfigKey<Integer> NFLoadBalancerPingInterval = new CommonClientConfigKey<Integer>("NFLoadBalancerPingInterval"){};
128 
129     public static final IClientConfigKey<Integer> NFLoadBalancerMaxTotalPingTime = new CommonClientConfigKey<Integer>("NFLoadBalancerMaxTotalPingTime"){};
130 
131     public static final IClientConfigKey<String> NFLoadBalancerStatsClassName = new CommonClientConfigKey<String>("NFLoadBalancerStatsClassName"){};
132 
133     public static final IClientConfigKey<String> NIWSServerListClassName = new CommonClientConfigKey<String>("NIWSServerListClassName"){};
134 
135     public static final IClientConfigKey<String> ServerListUpdaterClassName = new CommonClientConfigKey<String>("ServerListUpdaterClassName"){};
136 
137     public static final IClientConfigKey<String> NIWSServerListFilterClassName = new CommonClientConfigKey<String>("NIWSServerListFilterClassName"){};
138 
139     public static final IClientConfigKey<Integer> ServerListRefreshInterval = new CommonClientConfigKey<Integer>("ServerListRefreshInterval"){};
140 
141     public static final IClientConfigKey<Boolean> EnableMarkingServerDownOnReachingFailureLimit = new CommonClientConfigKey<Boolean>("EnableMarkingServerDownOnReachingFailureLimit"){};
142 
143     public static final IClientConfigKey<Integer> ServerDownFailureLimit = new CommonClientConfigKey<Integer>("ServerDownFailureLimit"){};
144 
145     public static final IClientConfigKey<Integer> ServerDownStatWindowInMillis = new CommonClientConfigKey<Integer>("ServerDownStatWindowInMillis"){};
146 
147     public static final IClientConfigKey<Boolean> EnableZoneAffinity = new CommonClientConfigKey<Boolean>("EnableZoneAffinity"){};
148 
149     public static final IClientConfigKey<Boolean> EnableZoneExclusivity = new CommonClientConfigKey<Boolean>("EnableZoneExclusivity"){};
150 
151     public static final IClientConfigKey<Boolean> PrioritizeVipAddressBasedServers = new CommonClientConfigKey<Boolean>("PrioritizeVipAddressBasedServers"){};
152 
153     public static final IClientConfigKey<String> VipAddressResolverClassName = new CommonClientConfigKey<String>("VipAddressResolverClassName"){};
154 
155     public static final IClientConfigKey<String> TargetRegion = new CommonClientConfigKey<String>("TargetRegion"){};
156 
157     public static final IClientConfigKey<String> RulePredicateClasses = new CommonClientConfigKey<String>("RulePredicateClasses"){};
158 
159     public static final IClientConfigKey<String> RequestIdHeaderName = new CommonClientConfigKey<String>("RequestIdHeaderName") {};
160 
161     public static final IClientConfigKey<Boolean> UseIPAddrForServer = new CommonClientConfigKey<Boolean>("UseIPAddrForServer") {};
162 
163     public static final IClientConfigKey<String> ListOfServers = new CommonClientConfigKey<String>("listOfServers") {};
164 
165     private static final Set<IClientConfigKey> keys = new HashSet<IClientConfigKey>();
166 
167     // ...
168 }
View Code

進入到 DefaultClientConfigImpl,可以看到 CommonClientConfigKey 中的每個配置都對應了一個預設值。在載入配置的時候,如果使用者沒有定製配置,就會使用預設的配置。

  1 public class DefaultClientConfigImpl implements IClientConfig {
  2 
  3     public static final Boolean DEFAULT_PRIORITIZE_VIP_ADDRESS_BASED_SERVERS = Boolean.TRUE;
  4 
  5     public static final String DEFAULT_NFLOADBALANCER_PING_CLASSNAME = "com.netflix.loadbalancer.DummyPing"; // DummyPing.class.getName();
  6 
  7     public static final String DEFAULT_NFLOADBALANCER_RULE_CLASSNAME = "com.netflix.loadbalancer.AvailabilityFilteringRule";
  8 
  9     public static final String DEFAULT_NFLOADBALANCER_CLASSNAME = "com.netflix.loadbalancer.ZoneAwareLoadBalancer";
 10 
 11     public static final boolean DEFAULT_USEIPADDRESS_FOR_SERVER = Boolean.FALSE;
 12 
 13     public static final String DEFAULT_CLIENT_CLASSNAME = "com.netflix.niws.client.http.RestClient";
 14 
 15     public static final String DEFAULT_VIPADDRESS_RESOLVER_CLASSNAME = "com.netflix.client.SimpleVipAddressResolver";
 16 
 17     public static final String DEFAULT_PRIME_CONNECTIONS_URI = "/";
 18 
 19     public static final int DEFAULT_MAX_TOTAL_TIME_TO_PRIME_CONNECTIONS = 30000;
 20 
 21     public static final int DEFAULT_MAX_RETRIES_PER_SERVER_PRIME_CONNECTION = 9;
 22 
 23     public static final Boolean DEFAULT_ENABLE_PRIME_CONNECTIONS = Boolean.FALSE;
 24 
 25     public static final int DEFAULT_MAX_REQUESTS_ALLOWED_PER_WINDOW = Integer.MAX_VALUE;
 26 
 27     public static final int DEFAULT_REQUEST_THROTTLING_WINDOW_IN_MILLIS = 60000;
 28 
 29     public static final Boolean DEFAULT_ENABLE_REQUEST_THROTTLING = Boolean.FALSE;
 30 
 31     public static final Boolean DEFAULT_ENABLE_GZIP_CONTENT_ENCODING_FILTER = Boolean.FALSE;
 32 
 33     public static final Boolean DEFAULT_CONNECTION_POOL_CLEANER_TASK_ENABLED = Boolean.TRUE;
 34 
 35     public static final Boolean DEFAULT_FOLLOW_REDIRECTS = Boolean.FALSE;
 36 
 37     public static final float DEFAULT_PERCENTAGE_NIWS_EVENT_LOGGED = 0.0f;
 38 
 39     public static final int DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER = 1;
 40 
 41     public static final int DEFAULT_MAX_AUTO_RETRIES = 0;
 42 
 43     public static final int DEFAULT_BACKOFF_INTERVAL = 0;
 44 
 45     public static final int DEFAULT_READ_TIMEOUT = 5000;
 46 
 47     public static final int DEFAULT_CONNECTION_MANAGER_TIMEOUT = 2000;
 48 
 49     public static final int DEFAULT_CONNECT_TIMEOUT = 2000;
 50 
 51     public static final Boolean DEFAULT_ENABLE_CONNECTION_POOL = Boolean.TRUE;
 52 
 53     @Deprecated
 54     public static final int DEFAULT_MAX_HTTP_CONNECTIONS_PER_HOST = 50;
 55 
 56     @Deprecated
 57     public static final int DEFAULT_MAX_TOTAL_HTTP_CONNECTIONS = 200;
 58 
 59     public static final int DEFAULT_MAX_CONNECTIONS_PER_HOST = 50;
 60 
 61     public static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 200;
 62 
 63     public static final float DEFAULT_MIN_PRIME_CONNECTIONS_RATIO = 1.0f;
 64 
 65     public static final String DEFAULT_PRIME_CONNECTIONS_CLASS = "com.netflix.niws.client.http.HttpPrimeConnection";
 66 
 67     public static final String DEFAULT_SEVER_LIST_CLASS = "com.netflix.loadbalancer.ConfigurationBasedServerList";
 68 
 69     public static final String DEFAULT_SERVER_LIST_UPDATER_CLASS = "com.netflix.loadbalancer.PollingServerListUpdater";
 70 
 71     public static final int DEFAULT_CONNECTION_IDLE_TIMERTASK_REPEAT_IN_MSECS = 30000; // every half minute (30 secs)
 72 
 73     public static final int DEFAULT_CONNECTIONIDLE_TIME_IN_MSECS = 30000; // all connections idle for 30 secs
 74 
 75     protected volatile Map<String, Object> properties = new ConcurrentHashMap<String, Object>();
 76 
 77     protected Map<IClientConfigKey<?>, Object> typedProperties = new ConcurrentHashMap<IClientConfigKey<?>, Object>();
 78 
 79     private static final Logger LOG = LoggerFactory.getLogger(DefaultClientConfigImpl.class);
 80 
 81     private String clientName = null;
 82 
 83     private VipAddressResolver resolver = null;
 84 
 85     private boolean enableDynamicProperties = true;
 86     /**
 87      * Defaults for the parameters for the thread pool used by batchParallel
 88      * calls
 89      */
 90     public static final int DEFAULT_POOL_MAX_THREADS = DEFAULT_MAX_TOTAL_HTTP_CONNECTIONS;
 91     public static final int DEFAULT_POOL_MIN_THREADS = 1;
 92     public static final long DEFAULT_POOL_KEEP_ALIVE_TIME = 15 * 60L;
 93     public static final TimeUnit DEFAULT_POOL_KEEP_ALIVE_TIME_UNITS = TimeUnit.SECONDS;
 94     public static final Boolean DEFAULT_ENABLE_ZONE_AFFINITY = Boolean.FALSE;
 95     public static final Boolean DEFAULT_ENABLE_ZONE_EXCLUSIVITY = Boolean.FALSE;
 96     public static final int DEFAULT_PORT = 7001;
 97     public static final Boolean DEFAULT_ENABLE_LOADBALANCER = Boolean.TRUE;
 98 
 99     public static final String DEFAULT_PROPERTY_NAME_SPACE = "ribbon";
100 
101     private String propertyNameSpace = DEFAULT_PROPERTY_NAME_SPACE;
102 
103     public static final Boolean DEFAULT_OK_TO_RETRY_ON_ALL_OPERATIONS = Boolean.FALSE;
104 
105     public static final Boolean DEFAULT_ENABLE_NIWS_EVENT_LOGGING = Boolean.TRUE;
106 
107     public static final Boolean DEFAULT_IS_CLIENT_AUTH_REQUIRED = Boolean.FALSE;
108 
109     private final Map<String, DynamicStringProperty> dynamicProperties = new ConcurrentHashMap<String, DynamicStringProperty>();
110 
111     public Boolean getDefaultPrioritizeVipAddressBasedServers() {
112         return DEFAULT_PRIORITIZE_VIP_ADDRESS_BASED_SERVERS;
113     }
114 
115     public String getDefaultNfloadbalancerPingClassname() {
116         return DEFAULT_NFLOADBALANCER_PING_CLASSNAME;
117     }
118 
119     public String getDefaultNfloadbalancerRuleClassname() {
120         return DEFAULT_NFLOADBALANCER_RULE_CLASSNAME;
121     }
122 
123     public String getDefaultNfloadbalancerClassname() {
124         return DEFAULT_NFLOADBALANCER_CLASSNAME;
125     }
126 
127     public boolean getDefaultUseIpAddressForServer() {
128         return DEFAULT_USEIPADDRESS_FOR_SERVER;
129     }
130 
131     public String getDefaultClientClassname() {
132         return DEFAULT_CLIENT_CLASSNAME;
133     }
134 
135     public String getDefaultVipaddressResolverClassname() {
136         return DEFAULT_VIPADDRESS_RESOLVER_CLASSNAME;
137     }
138 
139     public String getDefaultPrimeConnectionsUri() {
140         return DEFAULT_PRIME_CONNECTIONS_URI;
141     }
142 
143     public int getDefaultMaxTotalTimeToPrimeConnections() {
144         return DEFAULT_MAX_TOTAL_TIME_TO_PRIME_CONNECTIONS;
145     }
146 
147     public int getDefaultMaxRetriesPerServerPrimeConnection() {
148         return DEFAULT_MAX_RETRIES_PER_SERVER_PRIME_CONNECTION;
149     }
150 
151     public Boolean getDefaultEnablePrimeConnections() {
152         return DEFAULT_ENABLE_PRIME_CONNECTIONS;
153     }
154 
155     public int getDefaultMaxRequestsAllowedPerWindow() {
156         return DEFAULT_MAX_REQUESTS_ALLOWED_PER_WINDOW;
157     }
158 
159     public int getDefaultRequestThrottlingWindowInMillis() {
160         return DEFAULT_REQUEST_THROTTLING_WINDOW_IN_MILLIS;
161     }
162 
163     public Boolean getDefaultEnableRequestThrottling() {
164         return DEFAULT_ENABLE_REQUEST_THROTTLING;
165     }
166 
167     public Boolean getDefaultEnableGzipContentEncodingFilter() {
168         return DEFAULT_ENABLE_GZIP_CONTENT_ENCODING_FILTER;
169     }
170 
171     public Boolean getDefaultConnectionPoolCleanerTaskEnabled() {
172         return DEFAULT_CONNECTION_POOL_CLEANER_TASK_ENABLED;
173     }
174 
175     public Boolean getDefaultFollowRedirects() {
176         return DEFAULT_FOLLOW_REDIRECTS;
177     }
178 
179     public float getDefaultPercentageNiwsEventLogged() {
180         return DEFAULT_PERCENTAGE_NIWS_EVENT_LOGGED;
181     }
182 
183     public int getDefaultMaxAutoRetriesNextServer() {
184         return DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER;
185     }
186 
187     public int getDefaultMaxAutoRetries() {
188         return DEFAULT_MAX_AUTO_RETRIES;
189     }
190 
191     public int getDefaultReadTimeout() {
192         return DEFAULT_READ_TIMEOUT;
193     }
194 
195     public int getDefaultConnectionManagerTimeout() {
196         return DEFAULT_CONNECTION_MANAGER_TIMEOUT;
197     }
198 
199     public int getDefaultConnectTimeout() {
200         return DEFAULT_CONNECT_TIMEOUT;
201     }
202 
203     @Deprecated
204     public int getDefaultMaxHttpConnectionsPerHost() {
205         return DEFAULT_MAX_HTTP_CONNECTIONS_PER_HOST;
206     }
207 
208     @Deprecated
209     public int getDefaultMaxTotalHttpConnections() {
210         return DEFAULT_MAX_TOTAL_HTTP_CONNECTIONS;
211     }
212 
213     public int getDefaultMaxConnectionsPerHost() {
214         return DEFAULT_MAX_CONNECTIONS_PER_HOST;
215     }
216 
217     public int getDefaultMaxTotalConnections() {
218         return DEFAULT_MAX_TOTAL_CONNECTIONS;
219     }
220 
221     public float getDefaultMinPrimeConnectionsRatio() {
222         return DEFAULT_MIN_PRIME_CONNECTIONS_RATIO;
223     }
224 
225     public String getDefaultPrimeConnectionsClass() {
226         return DEFAULT_PRIME_CONNECTIONS_CLASS;
227     }
228 
229     public String getDefaultSeverListClass() {
230         return DEFAULT_SEVER_LIST_CLASS;
231     }
232 
233     public int getDefaultConnectionIdleTimertaskRepeatInMsecs() {
234         return DEFAULT_CONNECTION_IDLE_TIMERTASK_REPEAT_IN_MSECS;
235     }
236 
237     public int getDefaultConnectionidleTimeInMsecs() {
238         return DEFAULT_CONNECTIONIDLE_TIME_IN_MSECS;
239     }
240 
241     public VipAddressResolver getResolver() {
242         return resolver;
243     }
244 
245     public boolean isEnableDynamicProperties() {
246         return enableDynamicProperties;
247     }
248 
249     public int getDefaultPoolMaxThreads() {
250         return DEFAULT_POOL_MAX_THREADS;
251     }
252 
253     public int getDefaultPoolMinThreads() {
254         return DEFAULT_POOL_MIN_THREADS;
255     }
256 
257     public long getDefaultPoolKeepAliveTime() {
258         return DEFAULT_POOL_KEEP_ALIVE_TIME;
259     }
260 
261     public TimeUnit getDefaultPoolKeepAliveTimeUnits() {
262         return DEFAULT_POOL_KEEP_ALIVE_TIME_UNITS;
263     }
264 
265     public Boolean getDefaultEnableZoneAffinity() {
266         return DEFAULT_ENABLE_ZONE_AFFINITY;
267     }
268 
269     public Boolean getDefaultEnableZoneExclusivity() {
270         return DEFAULT_ENABLE_ZONE_EXCLUSIVITY;
271     }
272 
273     public int getDefaultPort() {
274         return DEFAULT_PORT;
275     }
276 
277     public Boolean getDefaultEnableLoadbalancer() {
278         return DEFAULT_ENABLE_LOADBALANCER;
279     }
280 
281 
282     public Boolean getDefaultOkToRetryOnAllOperations() {
283         return DEFAULT_OK_TO_RETRY_ON_ALL_OPERATIONS;
284     }
285 
286     public Boolean getDefaultIsClientAuthRequired(){
287         return DEFAULT_IS_CLIENT_AUTH_REQUIRED;
288     }
289 
290 
291     /**
292      * Create instance with no properties in default name space {@link #DEFAULT_PROPERTY_NAME_SPACE}
293      */
294     public DefaultClientConfigImpl() {
295         this.dynamicProperties.clear();
296         this.enableDynamicProperties = false;
297     }
298 
299     /**
300      * Create instance with no properties in the specified name space
301      */
302     public DefaultClientConfigImpl(String nameSpace) {
303         this();
304         this.propertyNameSpace = nameSpace;
305     }
306 
307     public void loadDefaultValues() {
308         putDefaultIntegerProperty(CommonClientConfigKey.MaxHttpConnectionsPerHost, getDefaultMaxHttpConnectionsPerHost());
309         putDefaultIntegerProperty(CommonClientConfigKey.MaxTotalHttpConnections, getDefaultMaxTotalHttpConnections());
310         putDefaultBooleanProperty(CommonClientConfigKey.EnableConnectionPool, getDefaultEnableConnectionPool());
311         putDefaultIntegerProperty(CommonClientConfigKey.MaxConnectionsPerHost, getDefaultMaxConnectionsPerHost());
312         putDefaultIntegerProperty(CommonClientConfigKey.MaxTotalConnections, getDefaultMaxTotalConnections());
313         putDefaultIntegerProperty(CommonClientConfigKey.ConnectTimeout, getDefaultConnectTimeout());
314         putDefaultIntegerProperty(CommonClientConfigKey.ConnectionManagerTimeout, getDefaultConnectionManagerTimeout());
315         putDefaultIntegerProperty(CommonClientConfigKey.ReadTimeout, getDefaultReadTimeout());
316         putDefaultIntegerProperty(CommonClientConfigKey.MaxAutoRetries, getDefaultMaxAutoRetries());
317         putDefaultIntegerProperty(CommonClientConfigKey.MaxAutoRetriesNextServer, getDefaultMaxAutoRetriesNextServer());
318         putDefaultBooleanProperty(CommonClientConfigKey.OkToRetryOnAllOperations, getDefaultOkToRetryOnAllOperations());
319         putDefaultBooleanProperty(CommonClientConfigKey.FollowRedirects, getDefaultFollowRedirects());
320         putDefaultBooleanProperty(CommonClientConfigKey.ConnectionPoolCleanerTaskEnabled, getDefaultConnectionPoolCleanerTaskEnabled());
321         putDefaultIntegerProperty(CommonClientConfigKey.ConnIdleEvictTimeMilliSeconds, getDefaultConnectionidleTimeInMsecs());
322         putDefaultIntegerProperty(CommonClientConfigKey.ConnectionCleanerRepeatInterval, getDefaultConnectionIdleTimertaskRepeatInMsecs());
323         putDefaultBooleanProperty(CommonClientConfigKey.EnableGZIPContentEncodingFilter, getDefaultEnableGzipContentEncodingFilter());
324         String proxyHost = ConfigurationManager.getConfigInstance().getString(getDefaultPropName(CommonClientConfigKey.ProxyHost.key()));
325         if (proxyHost != null && proxyHost.length() > 0) {
326             setProperty(CommonClientConfigKey.ProxyHost, proxyHost);
327         }
328         Integer proxyPort = ConfigurationManager
329                 .getConfigInstance()
330                 .getInteger(
331                         getDefaultPropName(CommonClientConfigKey.ProxyPort),
332                         (Integer.MIN_VALUE + 1)); // + 1 just to avoid potential clash with user setting
333         if (proxyPort != (Integer.MIN_VALUE + 1)) {
334             setProperty(CommonClientConfigKey.ProxyPort, proxyPort);
335         }
336         putDefaultIntegerProperty(CommonClientConfigKey.Port, getDefaultPort());
337         putDefaultBooleanProperty(CommonClientConfigKey.EnablePrimeConnections, getDefaultEnablePrimeConnections());
338         putDefaultIntegerProperty(CommonClientConfigKey.MaxRetriesPerServerPrimeConnection, getDefaultMaxRetriesPerServerPrimeConnection());
339         putDefaultIntegerProperty(CommonClientConfigKey.MaxTotalTimeToPrimeConnections, getDefaultMaxTotalTimeToPrimeConnections());
340         putDefaultStringProperty(CommonClientConfigKey.PrimeConnectionsURI, getDefaultPrimeConnectionsUri());
341         putDefaultIntegerProperty(CommonClientConfigKey.PoolMinThreads, getDefaultPoolMinThreads());
342         putDefaultIntegerProperty(CommonClientConfigKey.PoolMaxThreads, getDefaultPoolMaxThreads());
343         putDefaultLongProperty(CommonClientConfigKey.PoolKeepAliveTime, getDefaultPoolKeepAliveTime());
344         putDefaultTimeUnitProperty(CommonClientConfigKey.PoolKeepAliveTimeUnits, getDefaultPoolKeepAliveTimeUnits());
345         putDefaultBooleanProperty(CommonClientConfigKey.EnableZoneAffinity, getDefaultEnableZoneAffinity());
346         putDefaultBooleanProperty(CommonClientConfigKey.EnableZoneExclusivity, getDefaultEnableZoneExclusivity());
347         putDefaultStringProperty(CommonClientConfigKey.ClientClassName, getDefaultClientClassname());
348         putDefaultStringProperty(CommonClientConfigKey.NFLoadBalancerClassName, getDefaultNfloadbalancerClassname());
349         putDefaultStringProperty(CommonClientConfigKey.NFLoadBalancerRuleClassName, getDefaultNfloadbalancerRuleClassname());
350         putDefaultStringProperty(CommonClientConfigKey.NFLoadBalancerPingClassName, getDefaultNfloadbalancerPingClassname());
351         putDefaultBooleanProperty(CommonClientConfigKey.PrioritizeVipAddressBasedServers, getDefaultPrioritizeVipAddressBasedServers());
352         putDefaultFloatProperty(CommonClientConfigKey.MinPrimeConnectionsRatio, getDefaultMinPrimeConnectionsRatio());
353         putDefaultStringProperty(CommonClientConfigKey.PrimeConnectionsClassName, getDefaultPrimeConnectionsClass());
354         putDefaultStringProperty(CommonClientConfigKey.NIWSServerListClassName, getDefaultSeverListClass());
355         putDefaultStringProperty(CommonClientConfigKey.VipAddressResolverClassName, getDefaultVipaddressResolverClassname());
356         putDefaultBooleanProperty(CommonClientConfigKey.IsClientAuthRequired, getDefaultIsClientAuthRequired());
357         // putDefaultStringProperty(CommonClientConfigKey.RequestIdHeaderName, getDefaultRequestIdHeaderName());
358         putDefaultBooleanProperty(CommonClientConfigKey.UseIPAddrForServer, getDefaultUseIpAddressForServer());
359         putDefaultStringProperty(CommonClientConfigKey.ListOfServers, "");
360     }
361 }
View Code

也可以在配置檔案中定製配置,例如配置超時和重試:

 1 # 全域性配置
 2 ribbon:
 3   # 客戶端讀取超時時間
 4   ReadTimeout: 3000
 5   # 客戶端連線超時時間
 6   ConnectTimeout: 3000
 7   # 預設只重試 GET,設定為 true 時將重試所有型別,如 POST、PUT、DELETE
 8   OkToRetryOnAllOperations: false
 9   # 重試次數
10   MaxAutoRetries: 1
11   # 最多重試幾個例項
12   MaxAutoRetriesNextServer: 1
13 
14 # 只針對 demo-producer 客戶端
15 demo-producer:
16   ribbon:
17     # 客戶端讀取超時時間
18     ReadTimeout: 5000
19     # 客戶端連線超時時間
20     ConnectTimeout: 3000

2、均衡策略 — IRule

IRule 是最終選擇 Server 的策略規則類,核心的介面就是 choose。

 1 public interface IRule{
 2 
 3     // 選擇 Server
 4     public Server choose(Object key);
 5 
 6     // 設定 ILoadBalancer
 7     public void setLoadBalancer(ILoadBalancer lb);
 8 
 9     // 獲取 ILoadBalancer
10     public ILoadBalancer getLoadBalancer();
11 }

Ribbon 提供了豐富的負載均衡策略,我們也可以通過配置指定使用某個均衡策略。下面是整個Ribbon提供的 IRule 均衡策略。

3、服務檢查 — IPing

IPing 是用於定期檢查 Server 的可用性的,它只提供了一個介面,用來判斷 Server 是否存活:

1 public interface IPing {
2     
3     public boolean isAlive(Server server);
4 }

IPing 也提供了多種策略可選,下面是整個 IPing 體系結構:

4、獲取服務列表 — ServerList

ServerList 提供了兩個介面,一個是第一次獲取 Server 列表,一個是更新 Server 列表,其中 getUpdatedListOfServers 會每被 Loadbalancer 隔 30 秒調一次來更新 allServerList。

 1 public interface ServerList<T extends Server> {
 2 
 3     public List<T> getInitialListOfServers();
 4     
 5     /**
 6      * Return updated list of servers. This is called say every 30 secs
 7      * (configurable) by the Loadbalancer's Ping cycle
 8      */
 9     public List<T> getUpdatedListOfServers();   
10 }

ServerList 也提供了多種實現,ServerList 體系結構如下:

5、過濾服務 — ServerListFilter

ServerListFilter 提供了一個介面用來過濾出可用的 Server。

1 public interface ServerListFilter<T extends Server> {
2 
3     public List<T> getFilteredListOfServers(List<T> servers);
4 }

ServerListFilter 體系結構如下:

6、服務列表更新 — ServerListUpdater

ServerListUpdater 有多個介面,最核心的就是 start 開啟定時任務呼叫 updateAction 來更新 allServerList。

 1 public interface ServerListUpdater {
 2 
 3     /**
 4      * an interface for the updateAction that actually executes a server list update
 5      */
 6     public interface UpdateAction {
 7         void doUpdate();
 8     }
 9 
10     /**
11      * start the serverList updater with the given update action
12      * This call should be idempotent.
13      */
14     void start(UpdateAction updateAction);
15 }

預設有兩個實現類:

7、負載均衡器 — ILoadBalancer

ILoadBalancer 是負載均衡選擇服務的核心介面,主要提供瞭如下的獲取Server列表和根據客戶端名稱選擇Server的介面。

 1 public interface ILoadBalancer {
 2 
 3     // 新增Server
 4     public void addServers(List<Server> newServers);
 5     
 6     // 根據key選擇一個Server
 7     public Server chooseServer(Object key);
 8     
 9     // 獲取存活的Server列表,返回 upServerList
10     public List<Server> getReachableServers();
11 
12     // 獲取所有Server列表,返回 allServerList
13     public List<Server> getAllServers();
14 }

ILoadBalancer 的體系結構如下:

8、Ribbon 相關配置類

從前面一直看下來,可以發現有很多與 Ribbon 相關的配置類,這裡總結下與 Ribbon 相關的配置類,看每個配置類的配置順序,以及都主要配置了哪些東西。

① 首先是Eureka客戶端配置類 EurekaClientAutoConfiguration,這個自動化配置類主要配置了 Ribbon 所需的 EurekaClient。

 1 @Configuration(proxyBeanMethods = false)
 2 @EnableConfigurationProperties
 3 @ConditionalOnClass(EurekaClientConfig.class)
 4 @ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
 5 @ConditionalOnDiscoveryEnabled
 6 @AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class,
 7         CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class })
 8 @AutoConfigureAfter(name = {
 9         "org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration",
10         "org.springframework.cloud.autoconfigure.RefreshAutoConfiguration",
11         "org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration",
12         "org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration" })
13 public class EurekaClientAutoConfiguration {
14     // ....
15 }
View Code

② 接著是Ribbon自動化配置類 RibbonAutoConfiguration,這個類主要配置瞭如下類:

  • SpringClientFactory:管理 Ribbon 客戶端上下文。
  • LoadBalancerClient:負載均衡客戶端,預設實現類為 RibbonLoadBalancerClient(實際是在 RibbonClientConfiguration 中配置的)。
  • PropertiesFactory:用於判斷配置檔案中是否自定義了核心介面的實現類,如 NFLoadBalancerClassName、NFLoadBalancerPingClassName 等。
  • RibbonApplicationContextInitializer:開啟飢餓配置的時候,用這個類來在啟動時初始化 Ribbon 客戶端上下文。
 1 package org.springframework.cloud.netflix.ribbon;
 2 
 3 @Configuration
 4 @Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
 5 @RibbonClients
 6 // 在 EurekaClientAutoConfiguration 之後配置
 7 @AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
 8 // 在 LoadBalancerAutoConfiguration、AsyncLoadBalancerAutoConfiguration 之前配置
 9 @AutoConfigureBefore({ LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class })
10 @EnableConfigurationProperties({ RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class })
11 public class RibbonAutoConfiguration {
12 
13     @Autowired(required = false)
14     private List<RibbonClientSpecification> configurations = new ArrayList<>();
15 
16     @Autowired
17     private RibbonEagerLoadProperties ribbonEagerLoadProperties;
18 
19     @Bean
20     public HasFeatures ribbonFeature() {
21         return HasFeatures.namedFeature("Ribbon", Ribbon.class);
22     }
23 
24     @Bean
25     @ConditionalOnMissingBean
26     public SpringClientFactory springClientFactory() {
27         SpringClientFactory factory = new SpringClientFactory();
28         factory.setConfigurations(this.configurations);
29         return factory;
30     }
31 
32     @Bean
33     @ConditionalOnMissingBean(LoadBalancerClient.class)
34     public LoadBalancerClient loadBalancerClient() {
35         return new RibbonLoadBalancerClient(springClientFactory());
36     }
37 
38     @Bean
39     @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
40     @ConditionalOnMissingBean
41     public LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(final SpringClientFactory clientFactory) {
42         return new RibbonLoadBalancedRetryFactory(clientFactory);
43     }
44 
45     @Bean
46     @ConditionalOnMissingBean
47     public PropertiesFactory propertiesFactory() {
48         return new PropertiesFactory();
49     }
50 
51     @Bean
52     @ConditionalOnProperty("ribbon.eager-load.enabled")
53     public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {
54         return new RibbonApplicationContextInitializer(springClientFactory(), ribbonEagerLoadProperties.getClients());
55     }
56 }
View Code

③ 接著是負載均衡器配置類 LoadBalancerAutoConfiguration,這個類主要是建立了負載均衡攔截器 LoadBalancerInterceptor,並新增到 RestTemplae 的攔截器中。

 1 package org.springframework.cloud.client.loadbalancer;
 2 
 3 @Configuration(proxyBeanMethods = false)
 4 @ConditionalOnClass(RestTemplate.class)
 5 @ConditionalOnBean(LoadBalancerClient.class)
 6 @EnableConfigurationProperties(LoadBalancerRetryProperties.class)
 7 public class LoadBalancerAutoConfiguration {
 8 
 9     @LoadBalanced
10     @Autowired(required = false)
11     private List<RestTemplate> restTemplates = Collections.emptyList();
12 
13     @Autowired(required = false)
14     private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
15 
16     // 對 RestTemplate 定製化
17     @Bean
18     public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
19             final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
20         return () -> restTemplateCustomizers.ifAvailable(customizers -> {
21             for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
22                 for (RestTemplateCustomizer customizer : customizers) {
23                     customizer.customize(restTemplate);
24                 }
25             }
26         });
27     }
28 
29     @Bean
30     @ConditionalOnMissingBean
31     public LoadBalancerRequestFactory loadBalancerRequestFactory(LoadBalancerClient loadBalancerClient) {
32         return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
33     }
34 
35     @Configuration(proxyBeanMethods = false)
36     @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
37     static class LoadBalancerInterceptorConfig {
38 
39         // 建立 RestTemplate 攔截器
40         @Bean
41         public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient,
42                                                          LoadBalancerRequestFactory requestFactory) {
43             return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
44         }
45 
46         @Bean
47         @ConditionalOnMissingBean
48         public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
49             return restTemplate -> {
50                 List<ClientHttpRequestInterceptor> list = new ArrayList<>(
51                         restTemplate.getInterceptors());
52                 list.add(loadBalancerInterceptor);
53                 restTemplate.setInterceptors(list);
54             };
55         }
56 
57     }
58 }
View Code

④ 之後是預設的 Ribbon 客戶端配置類 RibbonClientConfiguration,這個類主要配置了 Ribbon 核心介面的預設實現。

  • IClientConfig:Ribbon 客戶端配置類,預設實現是 DefaultClientConfigImpl。
  • IRule:負載均衡策略規則元件,預設實現是 ZoneAvoidanceRule。
  • IPing:判斷 Server 是否存活,預設實現是 DummyPing,永遠都是返回 true。
  • ServerList:獲取 Server 的元件,預設實現類為 ConfigurationBasedServerList,從配置檔案獲取。
  • ServerListUpdater:Server 列表更新元件,預設實現類為 PollingServerListUpdater。
  • ServerListFilter:過濾可用的 Server 列表,預設實現類為 ZonePreferenceServerListFilter。
  • RibbonLoadBalancerContext:負載均衡客戶端。
  • RetryHandler:重試處理器,預設實現類為 DefaultLoadBalancerRetryHandler。
  1 package org.springframework.cloud.netflix.ribbon;
  2 
  3 @SuppressWarnings("deprecation")
  4 @Configuration(proxyBeanMethods = false)
  5 @EnableConfigurationProperties
  6 @Import({ HttpClientConfiguration.class, OkHttpRibbonConfiguration.class,
  7         RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class })
  8 public class RibbonClientConfiguration {
  9     public static final int DEFAULT_CONNECT_TIMEOUT = 1000;
 10     public static final int DEFAULT_READ_TIMEOUT = 1000;
 11     public static final boolean DEFAULT_GZIP_PAYLOAD = true;
 12 
 13     @RibbonClientName
 14     private String name = "client";
 15 
 16     @Autowired
 17     private PropertiesFactory propertiesFactory;
 18 
 19     @Bean
 20     @ConditionalOnMissingBean
 21     public IClientConfig ribbonClientConfig() {
 22         DefaultClientConfigImpl config = new DefaultClientConfigImpl();
 23         config.loadProperties(this.name);
 24         config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);
 25         config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);
 26         config.set(CommonClientConfigKey.GZipPayload, DEFAULT_GZIP_PAYLOAD);
 27         return config;
 28     }
 29 
 30     @Bean
 31     @ConditionalOnMissingBean
 32     public IRule ribbonRule(IClientConfig config) {
 33         if (this.propertiesFactory.isSet(IRule.class, name)) {
 34             return this.propertiesFactory.get(IRule.class, config, name);
 35         }
 36         ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
 37         rule.initWithNiwsConfig(config);
 38         return rule;
 39     }
 40 
 41     @Bean
 42     @ConditionalOnMissingBean
 43     public IPing ribbonPing(IClientConfig config) {
 44         if (this.propertiesFactory.isSet(IPing.class, name)) {
 45             return this.propertiesFactory.get(IPing.class, config, name);
 46         }
 47         return new DummyPing();
 48     }
 49 
 50     @Bean
 51     @ConditionalOnMissingBean
 52     @SuppressWarnings("unchecked")
 53     public ServerList<Server> ribbonServerList(IClientConfig config) {
 54         if (this.propertiesFactory.isSet(ServerList.class, name)) {
 55             return this.propertiesFactory.get(ServerList.class, config, name);
 56         }
 57         ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
 58         serverList.initWithNiwsConfig(config);
 59         return serverList;
 60     }
 61 
 62     @Bean
 63     @ConditionalOnMissingBean
 64     public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
 65         return new PollingServerListUpdater(config);
 66     }
 67 
 68     @Bean
 69     @ConditionalOnMissingBean
 70     public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
 71             ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
 72             IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
 73         if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
 74             return this.propertiesFactory.get(ILoadBalancer.class, config, name);
 75         }
 76         return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
 77                 serverListFilter, serverListUpdater);
 78     }
 79 
 80     @Bean
 81     @ConditionalOnMissingBean
 82     @SuppressWarnings("unchecked")
 83     public ServerListFilter<Server> ribbonServerListFilter(IClientConfig config) {
 84         if (this.propertiesFactory.isSet(ServerListFilter.class, name)) {
 85             return this.propertiesFactory.get(ServerListFilter.class, config, name);
 86         }
 87         ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
 88         filter.initWithNiwsConfig(config);
 89         return filter;
 90     }
 91 
 92     @Bean
 93     @ConditionalOnMissingBean
 94     public RibbonLoadBalancerContext ribbonLoadBalancerContext(ILoadBalancer loadBalancer,
 95             IClientConfig config, RetryHandler retryHandler) {
 96         return new RibbonLoadBalancerContext(loadBalancer, config, retryHandler);
 97     }
 98 
 99     @Bean
100     @ConditionalOnMissingBean
101     public RetryHandler retryHandler(IClientConfig config) {
102         return new DefaultLoadBalancerRetryHandler(config);
103     }
104 
105     @Bean
106     @ConditionalOnMissingBean
107     public ServerIntrospector serverIntrospector() {
108         return new DefaultServerIntrospector();
109     }
110 }
View Code

⑤ Ribbon Eureka 自動化配置類 RibbonEurekaAutoConfiguration,判斷是否啟用 Ribbon Eureka,並觸發 EurekaRibbonClientConfiguration 配置類。

 1 package org.springframework.cloud.netflix.ribbon.eureka;
 2 
 3 @Configuration(proxyBeanMethods = false)
 4 @EnableConfigurationProperties
 5 @ConditionalOnRibbonAndEurekaEnabled
 6 @AutoConfigureAfter(RibbonAutoConfiguration.class)
 7 @RibbonClients(defaultConfiguration = EurekaRibbonClientConfiguration.class)
 8 public class RibbonEurekaAutoConfiguration {
 9 
10 }
View Code

⑥ 預設啟用 Ribbon Eureka 的情況下,會使用 Ribbon Eureka 客戶端配置類 EurekaRibbonClientConfiguration:

  • IPing:替換了預設實現類 DummyPing,改為 NIWSDiscoveryPing,通過判斷 InstanceInfo 的狀態是否為 UP 來判斷 Server 是否存活。
  • ServerList:替換了預設的實現類 ConfigurationBasedServerList,改為 DomainExtractingServerList,實際是 DiscoveryEnabledNIWSServerList,從 EurekaClient 獲取 Server 列表。
 1 package org.springframework.cloud.netflix.ribbon.eureka;
 2 
 3 @Configuration(proxyBeanMethods = false)
 4 public class EurekaRibbonClientConfiguration {
 5 
 6     private static final Log log = LogFactory.getLog(EurekaRibbonClientConfiguration.class);
 7 
 8     @Value("${ribbon.eureka.approximateZoneFromHostname:false}")
 9     private boolean approximateZoneFromHostname = false;
10     @RibbonClientName
11     private String serviceId = "client";
12     @Autowired(required = false)
13     private EurekaClientConfig clientConfig;
14     @Autowired(required = false)
15     private EurekaInstanceConfig eurekaConfig;
16     @Autowired
17     private PropertiesFactory propertiesFactory;
18 
19     public EurekaRibbonClientConfiguration() {
20     }
21 
22     public EurekaRibbonClientConfiguration(EurekaClientConfig clientConfig,
23             String serviceId, EurekaInstanceConfig eurekaConfig,
24             boolean approximateZoneFromHostname) {
25         this.clientConfig = clientConfig;
26         this.serviceId = serviceId;
27         this.eurekaConfig = eurekaConfig;
28         this.approximateZoneFromHostname = approximateZoneFromHostname;
29     }
30 
31     @Bean
32     @ConditionalOnMissingBean
33     public IPing ribbonPing(IClientConfig config) {
34         if (this.propertiesFactory.isSet(IPing.class, serviceId)) {
35             return this.propertiesFactory.get(IPing.class, config, serviceId);
36         }
37         NIWSDiscoveryPing ping = new NIWSDiscoveryPing();
38         ping.initWithNiwsConfig(config);
39         return ping;
40     }
41 
42     @Bean
43     @ConditionalOnMissingBean
44     public ServerList<?> ribbonServerList(IClientConfig config, Provider<EurekaClient> eurekaClientProvider) {
45         if (this.propertiesFactory.isSet(ServerList.class, serviceId)) {
46             return this.propertiesFactory.get(ServerList.class, config, serviceId);
47         }
48         DiscoveryEnabledNIWSServerList discoveryServerList = new DiscoveryEnabledNIWSServerList(
49                 config, eurekaClientProvider);
50         DomainExtractingServerList serverList = new DomainExtractingServerList(
51                 discoveryServerList, config, this.approximateZoneFromHostname);
52         return serverList;
53     }
54 }
View Code

⑦ 各個配置類所屬模組

spring-cloud-netflix-eureka-client:

  • org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration
  • org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration
  • org.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonClientConfiguration

spring-cloud-netflix-ribbon:

  • org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration
  • org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration

spring-cloud-commons:

  • org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration

六、Ribbon HTTP客戶端元件

1、Java HTTP 元件庫

① HTTP 元件庫

首先簡單瞭解下常用的 Java HTTP 元件庫,Ribbon 中通過不同的配置便可以啟用某個 HTTP 元件來進行服務間的通訊。

Java 中的 HTTP 元件庫,大體可以分為三類:

  • JDK 自帶的標準庫 HttpURLConnection
  • Apache HttpComponents HttpClient
  • OkHttp

HttpURLConnection 發起 HTTP 請求最大的優點是不需要引入額外的依賴,但是 HttpURLConnection 封裝層次太低,使用起來非常繁瑣。支援的特性太少,缺乏連線池管理、域名機械控制,無法支援 HTTP/2等。

Apache HttpComponents HttpClient 和 OkHttp 都支援連線池管理、超時、空閒連線數控制等特性。OkHttp 介面設計更友好,且支援 HTTP/2,Android 開發中用得更多。

② 超時重試配置

先給 demo-consumer 中新增如下預設配置,即讀取、連線超時時間設定為 1 秒,這也是預設值。然後重試次數為1,重試一個Server。後面基於這些配置來驗證Ribbon HTTP客戶端的超時和重試。

 1 ribbon:
 2   # 客戶端讀取超時時間
 3   ReadTimeout: 1000
 4   # 客戶端連線超時時間
 5   ConnectTimeout: 1000
 6   # 預設只重試 GET,設定為 true 時將重試所有型別,如 POST、PUT、DELETE
 7   OkToRetryOnAllOperations: false
 8   # 重試次數
 9   MaxAutoRetries: 1
10   # 最多重試幾個例項
11   MaxAutoRetriesNextServer: 1

然後 demo-producer 的介面休眠3秒,造成網路延遲的現象,並且 demo-producer 啟兩個例項。

1 @GetMapping("/v1/uuid")
2 public ResponseEntity<String> getUUID() throws InterruptedException {
3     String uuid = UUID.randomUUID().toString();
4     logger.info("generate uuid: {}", uuid);
5     Thread.sleep(3000);
6     return ResponseEntity.ok(uuid);
7 }

2、Ribbon 預設使用 HttpURLConnection

① Ribbon 預設的 HTTP 元件

在不新增其它配置的情況下,我們來看下 Ribbon 預設使用的 HTTP 元件是什麼。

首先通過之前的分析可以知道,預設情況下,LoadBalancerAutoConfiguration 配置類會向 RestTemplate 新增 LoadBalancerInterceptor 攔截器。然後在 RestTemplate 呼叫時,即在 doExecute 方法中,建立 ClientHttpRequest 時,因為配置了攔截器,所以 ClientHttpRequestFactory 就是 InterceptingClientHttpRequestFactory,而且建立 InterceptingClientHttpRequestFactory 傳入的 ClientHttpRequestFactory 預設是父類的 SimpleClientHttpRequestFactory。

 1 protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
 2         @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
 3     //...
 4     ClientHttpResponse response = null;
 5     try {
 6         // 建立 ClientHttpRequest
 7         ClientHttpRequest request = createRequest(url, method);
 8         if (requestCallback != null) {
 9             requestCallback.doWithRequest(request);
10         }
11         // ClientHttpRequest 發起請求
12         response = request.execute();
13         handleResponse(url, method, response);
14         return (responseExtractor != null ? responseExtractor.extractData(response) : null);
15     }
16     catch (IOException ex) {
17         // ...
18     }
19     finally {
20         if (response != null) {
21             response.close();
22         }
23     }
24 }
25 
26 protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
27     // getRequestFactory 獲取 ClientHttpRequestFactory
28     ClientHttpRequest request = getRequestFactory().createRequest(url, method);
29     initialize(request);
30     if (logger.isDebugEnabled()) {
31         logger.debug("HTTP " + method.name() + " " + url);
32     }
33     return request;
34 }
35 
36 public ClientHttpRequestFactory getRequestFactory() {
37     List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
38     if (!CollectionUtils.isEmpty(interceptors)) {
39         ClientHttpRequestFactory factory = this.interceptingRequestFactory;
40         if (factory == null) {
41             // 有攔截器的情況,super.getRequestFactory() 預設返回的是 SimpleClientHttpRequestFactory
42             factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
43             this.interceptingRequestFactory = factory;
44         }
45         return factory;
46     }
47     else {
48         // 無攔截器的情況
49         return super.getRequestFactory();
50     }
51 }
View Code

InterceptingClientHttpRequestFactory 這個工廠類建立的 ClientHttpRequest 型別是 InterceptingClientHttpRequest。最終 RestTemplate 的 doExecute 方法中呼叫 ClientHttpRequest 的 execute 方法時,就呼叫到了 InterceptingClientHttpRequest 中的內部類 InterceptingRequestExecution 中。

在 InterceptingRequestExecution 的 execute 方法中,首先是遍歷所有攔截器對 RestTemplate 定製化,最後則通過 requestFactory 建立 ClientHttpRequest 來發起最終的 HTTP 呼叫。從這裡可以看出,無論有沒有攔截器,其實最終都會使用 requestFactory 來建立 ClientHttpRequest。

 1 private class InterceptingRequestExecution implements ClientHttpRequestExecution {
 2     private final Iterator<ClientHttpRequestInterceptor> iterator;
 3 
 4     @Override
 5     public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
 6         if (this.iterator.hasNext()) {
 7             // 攔截器定製化 RestTemplate
 8             ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
 9             return nextInterceptor.intercept(request, body, this);
10         }
11         else {
12             HttpMethod method = request.getMethod();
13             // delegate => SimpleBufferingClientHttpRequest
14             ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);
15             //...
16             return delegate.execute();
17         }
18     }
19 }

這裡的 requestFactory 就是前面傳進來的 SimpleClientHttpRequestFactory,從它的 createRequest 方法可以看出,預設情況下,就是用的 JDK 標準 HTTP 庫元件 HttpURLConnection 來進行服務間的請求通訊。

 1 public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
 2     // JDK 標準HTTP庫 HttpURLConnection
 3     HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
 4     prepareConnection(connection, httpMethod.name());
 5 
 6     if (this.bufferRequestBody) {
 7         return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
 8     }
 9     else {
10         return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
11     }
12 }

總結:

從前面的原始碼分析可以看出,Ribbon 預設的 HTTP 客戶端是 HttpURLConnection。

在前面預設的超時配置下,可以驗證出超時配置並未生效,一直阻塞3秒後才返回了結果,說明 Ribbon 預設情況下就不支援超時重試。

而且 HttpURLConnection 每次都是新建立的,請求返回來之後就關閉連線,沒有連線池管理機制,網路連線的建立和關閉本身就會損耗一定的效能,所以正式環境下,最好不要使用預設的配置。

② HttpClient 配置類

另外,我們從 RibbonClientConfiguration 配置類的定義可以看到,其匯入了 HttpClientConfiguration、OkHttpRibbonConfiguration、RestClientRibbonConfiguration、HttpClientRibbonConfiguration 四個 HttpClient 的配置類,通過註釋也可以瞭解到,最後一個是預設配置類,前面三個在某些配置啟用的情況下才會生效。

1 @Configuration(proxyBeanMethods = false)
2 @EnableConfigurationProperties
3 // Order is important here, last should be the default, first should be optional
4 @Import({ HttpClientConfiguration.class, OkHttpRibbonConfiguration.class,
5         RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class })
6 public class RibbonClientConfiguration {
7 
8 }

進入 HttpClientRibbonConfiguration,這個配置類在 ribbon.httpclient.enabled=true 時才生效,而且預設為 true。在從 SpringClientFactory 中獲取 ILoadBalancer 時,會通過這個配置類初始化 HttpClient,按先後順序會初始化 HttpClientConnectionManager、CloseableHttpClient、RibbonLoadBalancingHttpClient。CloseableHttpClient 是 Apache HttpComponents HttpClient 中的元件,也就是說預設情況下應該是使用 apache HttpComponents 作為 HTTP 元件庫。

但經過前面原始碼的分析,以及測試發現,最終其實走的的 HttpURLConnection,並沒有用到 CloseableHttpClient。把 ribbon.httpclient.enabled 設定為 false,也沒有什麼影響,還是預設走 HttpURLConnection。我們後面再來分析這個問題。

 1 @Configuration(proxyBeanMethods = false)
 2 @ConditionalOnClass(name = "org.apache.http.client.HttpClient")
 3 // ribbon.httpclient.enabled more文為 true
 4 @ConditionalOnProperty(name = "ribbon.httpclient.enabled", matchIfMissing = true)
 5 public class HttpClientRibbonConfiguration {
 6 
 7     @RibbonClientName
 8     private String name = "client";
 9 
10     // RibbonLoadBalancingHttpClient
11     @Bean
12     @ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
13     @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
14     public RibbonLoadBalancingHttpClient ribbonLoadBalancingHttpClient(
15             IClientConfig config, ServerIntrospector serverIntrospector,
16             ILoadBalancer loadBalancer, RetryHandler retryHandler,
17             CloseableHttpClient httpClient) {
18         RibbonLoadBalancingHttpClient client = new RibbonLoadBalancingHttpClient(
19                 httpClient, config, serverIntrospector);
20         client.setLoadBalancer(loadBalancer);
21         client.setRetryHandler(retryHandler);
22         Monitors.registerObject("Client_" + this.name, client);
23         return client;
24     }
25 
26     // 在引入了 spring-retry 時,即可以重試的 RetryTemplate 時,就建立 RetryableRibbonLoadBalancingHttpClient
27     @Bean
28     @ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
29     @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
30     public RetryableRibbonLoadBalancingHttpClient retryableRibbonLoadBalancingHttpClient(
31             IClientConfig config, ServerIntrospector serverIntrospector,
32             ILoadBalancer loadBalancer, RetryHandler retryHandler,
33             LoadBalancedRetryFactory loadBalancedRetryFactory,
34             CloseableHttpClient httpClient,
35             RibbonLoadBalancerContext ribbonLoadBalancerContext) {
36         RetryableRibbonLoadBalancingHttpClient client = new RetryableRibbonLoadBalancingHttpClient(
37                 httpClient, config, serverIntrospector, loadBalancedRetryFactory);
38         clien