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 }
進入到 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