SpringCloud Source series (4)—— Load balancing Ribbon( On )

SpringCloud Source series (5)—— Load balancing Ribbon( Next )

5、 ... and 、Ribbon The core interface

I've learned that Ribbon How the core interface and the default implementation work together to find an instance to call , In this section, let's take a look at some features of each core interface and other implementation classes .

1、 Client configuration — IClientConfig

IClientConfig It is the core interface for managing client configuration , Its default implementation class is  DefaultClientConfigImpl. You can see that you are creating  IClientConfig when , Set up Ribbon The default connection and read timeout of the client is 1 second , For example, if read more than 1 second , It will return to timeout , These two generally need to be adjusted according to the actual situation .、

 1 @Bean
2 @ConditionalOnMissingBean
3 public IClientConfig ribbonClientConfig() {
4 DefaultClientConfigImpl config = new DefaultClientConfigImpl();
5 // Load the configuration
6 config.loadProperties(this.name);
7 // Connection timeout by default 1 second
8 config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);
9 // Read timeout default 1 second
10 config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);
11 config.set(CommonClientConfigKey.GZipPayload, DEFAULT_GZIP_PAYLOAD);
12 return config;
13 }

CommonClientConfigKey This class is defined Ribbon Key constants for all configurations related to the client , You can use this class to see what configuration .

  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 }

Enter into  DefaultClientConfigImpl, You can see  CommonClientConfigKey There is a default value for each configuration in . When loading the configuration , If the user does not have a custom configuration , Will use the default configuration .

  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 }

You can also customize the configuration in the configuration file , For example, configure timeout and retrying :

 1 #  Global configuration 
2 ribbon:
3 # Client read timeout
4 ReadTimeout: 3000
5 # Client connection timeout
6 ConnectTimeout: 3000
7 # By default, only try again GET, Set to true All types will be retried when , Such as POST、PUT、DELETE
8 OkToRetryOnAllOperations: false
9 # Retry count
10 MaxAutoRetries: 1
11 # Try a few instances at most
12 MaxAutoRetriesNextServer: 1
13
14 # Only aim at demo-producer client
15 demo-producer:
16 ribbon:
17 # Client read timeout
18 ReadTimeout: 5000
19 # Client connection timeout
20 ConnectTimeout: 3000

2、 Equilibrium strategy — IRule

IRule It's the final choice Server Policy rule class of , The core interface is choose.

 1 public interface IRule{
2
3 // choice Server
4 public Server choose(Object key);
5
6 // Set up ILoadBalancer
7 public void setLoadBalancer(ILoadBalancer lb);
8
9 // obtain ILoadBalancer
10 public ILoadBalancer getLoadBalancer();
11 }

Ribbon It provides rich load balancing strategies , We can also specify the use of a certain balancing strategy through configuration . Here is the whole Ribbon Provided IRule Equilibrium strategy .

3、 Service check — IPing

IPing It's for regular inspection Server The usability of , It only provides one interface , Used to determine Server Survival :

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

IPing It also offers a variety of strategies to choose from , Here is the whole IPing Architecture :

4、 Get a list of services — ServerList

ServerList Two interfaces are provided , One is the first acquisition of Server list , One is to update Server list , among getUpdatedListOfServers Every time you get Loadbalancer Partition 30 One second to update 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 It also provides a variety of implementations ,ServerList The architecture is as follows :

5、 Filtering Services — ServerListFilter

ServerListFilter Provides an interface to filter out available Server.

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

ServerListFilter The architecture is as follows :

6、 Service list update — ServerListUpdater

ServerListUpdater There are multiple interfaces , The core is start Turn on timed task call updateAction To update 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 }

By default, there are two implementation classes :

7、 Load Balancer — ILoadBalancer

ILoadBalancer It is the core interface of load balancing selection service , It mainly provides the following access Server List and select according to the client name Server The interface of .

 1 public interface ILoadBalancer {
2
3 // add to Server
4 public void addServers(List<Server> newServers);
5
6 // according to key Select a Server
7 public Server chooseServer(Object key);
8
9 // Get the living Server list , return upServerList
10 public List<Server> getReachableServers();
11
12 // Get all Server list , return allServerList
13 public List<Server> getAllServers();
14 }

ILoadBalancer The architecture of is as follows :

8、Ribbon Related configuration classes

Look straight down from the front , It can be found that there is a lot to do with Ribbon Related configuration classes , Here is a summary of the relationship with Ribbon Related configuration classes , Look at the configuration order of each configuration class , And what things are mainly configured .

① First of all Eureka Client configuration class EurekaClientAutoConfiguration, This automatic configuration class mainly configures Ribbon The required 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 }

② Next is Ribbon Automatic configuration class RibbonAutoConfiguration, This class is mainly configured with the following classes :

  • SpringClientFactory: management Ribbon Client context .
  • LoadBalancerClient: Load balancing client , The default implementation class is RibbonLoadBalancerClient( It's actually in RibbonClientConfiguration Configured in ).
  • PropertiesFactory: The implementation class used to determine whether the core interface is customized in the configuration file , Such as  NFLoadBalancerClassName、NFLoadBalancerPingClassName etc. .
  • RibbonApplicationContextInitializer: When the hunger configuration is turned on , Use this class to initialize at startup Ribbon Client context .

 1 package org.springframework.cloud.netflix.ribbon;
2
3 @Configuration
4 @Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
5 @RibbonClients
6 // stay EurekaClientAutoConfiguration After configuration
7 @AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
8 // stay LoadBalancerAutoConfiguration、AsyncLoadBalancerAutoConfiguration Previous configuration
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 }

③ Next comes the load balancer configuration class LoadBalancerAutoConfiguration, This class mainly creates a load balancing interceptor LoadBalancerInterceptor, To add to RestTemplae In the interceptor of .

 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 // Yes RestTemplate Customized
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 // establish RestTemplate Interceptor
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 }

④ After that is the default Ribbon Client configuration class RibbonClientConfiguration, This class is mainly configured with Ribbon The default implementation of the core interface .

  • IClientConfig:Ribbon Client configuration class , The default implementation is DefaultClientConfigImpl.
  • IRule: Load balancing policy rule component , The default implementation is ZoneAvoidanceRule.
  • IPing: Judge Server Survival , The default implementation is DummyPing, Always return true.
  • ServerList: obtain Server The components of , The default implementation class is ConfigurationBasedServerList, Get... From the configuration file .
  • ServerListUpdater:Server List update component , The default implementation class is PollingServerListUpdater.
  • ServerListFilter: Filter the available Server list , The default implementation class is ZonePreferenceServerListFilter.
  • RibbonLoadBalancerContext: Load balancing client .
  • RetryHandler: Retry processor , The default implementation class is 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 }

⑤ Ribbon Eureka Automatic configuration class RibbonEurekaAutoConfiguration, Determine whether to enable Ribbon Eureka, And trigger EurekaRibbonClientConfiguration Configuration class .

 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 }

⑥ Enabled by default Ribbon Eureka Under the circumstances , Will use Ribbon Eureka Client configuration class EurekaRibbonClientConfiguration:

  • IPing: Replaced the default implementation class DummyPing, Change it to NIWSDiscoveryPing, By judgment InstanceInfo Is the state of UP To judge Server Survival .
  • ServerList: Replaced the default implementation class ConfigurationBasedServerList, Change it to DomainExtractingServerList, the truth is that DiscoveryEnabledNIWSServerList, from EurekaClient obtain Server list .

 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 }

⑦ Each configuration class belongs to a module

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

6、 ... and 、Ribbon HTTP Client component

1、Java HTTP Component library

① HTTP Component library

First of all, a brief understanding of the commonly used Java HTTP Component library ,Ribbon You can enable a HTTP Component to communicate between services .

Java Medium HTTP Component library , It can be roughly divided into three categories :

  • JDK Standard library with you HttpURLConnection
  • Apache HttpComponents HttpClient
  • OkHttp

HttpURLConnection launch HTTP The biggest advantage of requests is that they don't need to introduce additional dependencies , however HttpURLConnection The encapsulation level is too low , It's very cumbersome to use . There are too few features to support , Lack of connection pool management 、 Domain name mechanical control , Unable to support HTTP/2 etc. .

Apache HttpComponents HttpClient and OkHttp Both support connection pool management 、 Overtime 、 Idle connection number control and other characteristics .OkHttp Interface design is more friendly , And support HTTP/2,Android More in development .

② Timeout retry configuration

First give demo-consumer Add the following default configuration , Read 、 The connection timeout is set to 1 second , This is also the default value . Then try again for 1, Try one again Server. Based on these configurations Ribbon HTTP Client timeout and retry .

 1 ribbon:
2 # Client read timeout
3 ReadTimeout: 1000
4 # Client connection timeout
5 ConnectTimeout: 1000
6 # By default, only try again GET, Set to true All types will be retried when , Such as POST、PUT、DELETE
7 OkToRetryOnAllOperations: false
8 # Retry count
9 MaxAutoRetries: 1
10 # Try a few instances at most
11 MaxAutoRetriesNextServer: 1

then demo-producer The interface is dormant 3 second , The phenomenon of network delay , also demo-producer Two examples .

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 By default HttpURLConnection

① Ribbon default HTTP Components

Without adding other configurations , Let's see Ribbon Default HTTP What are the components .

First of all, we can know from the previous analysis that , By default ,LoadBalancerAutoConfiguration The configuration class will send RestTemplate add to LoadBalancerInterceptor Interceptor . And then in RestTemplate Invocation time , That is to say doExecute In the method , establish ClientHttpRequest when , Because the interceptor is configured , therefore ClientHttpRequestFactory Namely InterceptingClientHttpRequestFactory, And create InterceptingClientHttpRequestFactory Incoming ClientHttpRequestFactory The default is parent class 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 // establish ClientHttpRequest
7 ClientHttpRequest request = createRequest(url, method);
8 if (requestCallback != null) {
9 requestCallback.doWithRequest(request);
10 }
11 // ClientHttpRequest Initiate request
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 obtain 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 // In the case of interceptors ,super.getRequestFactory() The default return is SimpleClientHttpRequestFactory
42 factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
43 this.interceptingRequestFactory = factory;
44 }
45 return factory;
46 }
47 else {
48 // Without interceptors
49 return super.getRequestFactory();
50 }
51 }

InterceptingClientHttpRequestFactory This factory class creates ClientHttpRequest The type is InterceptingClientHttpRequest. Final RestTemplate Of doExecute Call in method ClientHttpRequest Of execute When the method is used , It's time to call InterceptingClientHttpRequest Inner class in InterceptingRequestExecution in .

stay InterceptingRequestExecution Of execute In the method , The first is to traverse all interceptor pairs RestTemplate Customized , Finally, it passed requestFactory establish ClientHttpRequest To launch the final HTTP call . You can see it here , With or without interceptors , In fact, it will be used in the end requestFactory To create 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 // Interceptor customization 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 }

there requestFactory It's from the front SimpleClientHttpRequestFactory, From its createRequest The method shows that , By default , Is to use the JDK standard HTTP Library components HttpURLConnection To communicate requests between services .

 1 public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
2 // JDK standard HTTP library 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 }

summary :

From the source code analysis, we can see that ,Ribbon default HTTP The client is HttpURLConnection.

In the previous default timeout configuration , You can verify that the timeout configuration is not in effect , Keep blocking 3 Seconds later, the result returned , explain Ribbon Timeout retries are not supported by default .

and HttpURLConnection Every time it's new , Close the connection after the request returns , There is no connection pool management mechanism , The establishment and closing of network connection will lose certain performance itself , So in a formal environment , It's best not to use the default configuration .

② HttpClient Configuration class

in addition , We from RibbonClientConfiguration You can see the configuration of the class , It's imported HttpClientConfiguration、OkHttpRibbonConfiguration、RestClientRibbonConfiguration、HttpClientRibbonConfiguration four HttpClient Configuration class , You can also learn from the comments that , The last one is the default configuration class , The first three take effect only when certain configurations are enabled .

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 }

Get into HttpClientRibbonConfiguration, This configuration class is in ribbon.httpclient.enabled=true When it comes into effect , And the default is true. In from SpringClientFactory In order to get ILoadBalancer when , Will be initialized through this configuration class HttpClient, In order to initialize HttpClientConnectionManager、CloseableHttpClient、RibbonLoadBalancingHttpClient.CloseableHttpClient yes  Apache HttpComponents HttpClient Components in , That is to say, it should be used by default apache HttpComponents As HTTP Component library .

But after the analysis of the previous source code , And the test found , In the end, it was  HttpURLConnection, Not used CloseableHttpClient. hold ribbon.httpclient.enabled Set to false, It doesn't matter , Or default to go HttpURLConnection. We will analyze this problem later .

 1 @Configuration(proxyBeanMethods = false)
2 @ConditionalOnClass(name = "org.apache.http.client.HttpClient")
3 // ribbon.httpclient.enabled more The text is 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 // It is introduced. spring-retry when , You can try again RetryTemplate when , create 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 client.setLoadBalancer(loadBalancer);
39 client.setRetryHandler(retryHandler);
40 client.setRibbonLoadBalancerContext(ribbonLoadBalancerContext);
41 Monitors.registerObject("Client_" + this.name, client);
42 return client;
43 }
44
45 @Configuration(proxyBeanMethods = false)
46 protected static class ApacheHttpClientConfiguration {
47
48 private final Timer connectionManagerTimer = new Timer(
49 "RibbonApacheHttpClientConfiguration.connectionManagerTimer", true);
50
51 private CloseableHttpClient httpClient;
52
53 @Autowired(required = false)
54 private RegistryBuilder registryBuilder;
55
56 // HttpClient Connection pool manager
57 @Bean
58 @ConditionalOnMissingBean(HttpClientConnectionManager.class)
59 public HttpClientConnectionManager httpClientConnectionManager(
60 IClientConfig config,
61 ApacheHttpClientConnectionManagerFactory connectionManagerFactory) {
62 RibbonProperties ribbon = RibbonProperties.from(config);
63 int maxTotalConnections = ribbon.maxTotalConnections();
64 int maxConnectionsPerHost = ribbon.maxConnectionsPerHost();
65 int timerRepeat = ribbon.connectionCleanerRepeatInterval();
66 long timeToLive = ribbon.poolKeepAliveTime();
67 TimeUnit ttlUnit = ribbon.getPoolKeepAliveTimeUnits();
68 final HttpClientConnectionManager connectionManager = connectionManagerFactory
69 .newConnectionManager(false, maxTotalConnections,
70 maxConnectionsPerHost, timeToLive, ttlUnit, registryBuilder);
71 this.connectionManagerTimer.schedule(new TimerTask() {
72 @Override
73 public void run() {
74 connectionManager.closeExpiredConnections();
75 }
76 }, 30000, timerRepeat);
77 return connectionManager;
78 }
79
80 // HttpClient => CloseableHttpClient
81 @Bean
82 @ConditionalOnMissingBean(CloseableHttpClient.class)
83 public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
84 HttpClientConnectionManager connectionManager, IClientConfig config) {
85 RibbonProperties ribbon = RibbonProperties.from(config);
86 Boolean followRedirects = ribbon.isFollowRedirects();
87 Integer connectTimeout = ribbon.connectTimeout();
88 RequestConfig defaultRequestConfig = RequestConfig.custom()
89 .setConnectTimeout(connectTimeout)
90 .setRedirectsEnabled(followRedirects).build();
91 this.httpClient = httpClientFactory.createBuilder()
92 .setDefaultRequestConfig(defaultRequestConfig)
93 .setConnectionManager(connectionManager).build();
94 return httpClient;
95 }
96 }
97 }

③ In the default configuration RestTemplate The calling process of can be roughly represented by the following figure .

3、 Enable RestClient

① Enable RestClient

You can add the following configuration to enable RestClient:

 1 ribbon:
2 # close httpclient
3 httpclient:
4 enabled: false
5 # Enable RestClient
6 restclient:
7 enabled: true
8 # Enable RestClient
9 http:
10 client:
11 enabled: true

Get into RestClientRibbonConfiguration  You can see , as long as ribbon.http.client.enabled、ribbon.restclient.enabled One of them is configured to enable , You can enable RestClient.

 1 @SuppressWarnings("deprecation")
2 @Configuration(proxyBeanMethods = false)
3 // Enabling conditions ConditionalOnRibbonRestClient
4 @RibbonAutoConfiguration.ConditionalOnRibbonRestClient
5 class RestClientRibbonConfiguration {
6 @RibbonClientName
7 private String name = "client";
8
9 // RestClient Has expired
10 @Bean
11 @Lazy
12 @ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
13 public RestClient ribbonRestClient(IClientConfig config, ILoadBalancer loadBalancer,
14 ServerIntrospector serverIntrospector, RetryHandler retryHandler) {
15 RestClient client = new RibbonClientConfiguration.OverrideRestClient(config, serverIntrospector);
16 client.setLoadBalancer(loadBalancer);
17 client.setRetryHandler(retryHandler);
18 return client;
19 }
20
21 }
22
23 @Target({ ElementType.TYPE, ElementType.METHOD })
24 @Retention(RetentionPolicy.RUNTIME)
25 @Documented
26 @Conditional(OnRibbonRestClientCondition.class)
27 @interface ConditionalOnRibbonRestClient {
28 }
29
30 private static class OnRibbonRestClientCondition extends AnyNestedCondition {
31 @Deprecated // remove in Edgware"
32 @ConditionalOnProperty("ribbon.http.client.enabled")
33 static class ZuulProperty {
34 }
35
36 @ConditionalOnProperty("ribbon.restclient.enabled")
37 static class RibbonProperty {
38 }
39 }

RestClient Inherited from AbstractLoadBalancerAwareClient. It should be noted that ,RestClient It's overdue , Therefore, we should not enable it in the production environment RestTemplate 了 .

1 @Deprecated
2 public class RestClient extends AbstractLoadBalancerAwareClient<HttpRequest, HttpResponse> {
3
4 }

② LoadBalancerContext Class architecture

Load balancing context LoadBalancerContext The class structure of the system is as follows . It can be seen that ,Ribbon It's supporting Feign、OkHttp、HttpClient、RestClient Of . The implementation class used in the default configuration is RibbonLoadBalancerContext.

③ RestTemplate Of ClientHttpRequest Factory class configuration

Then look at RibbonAutoConfiguration There are the following configurations in , Follow the front RestClientRibbonConfiguration Is the same , Satisfy @ConditionalOnRibbonRestClient Conditions .

You can see , It will create RibbonClientHttpRequestFactory And set it to RestTemplate in , in other words , At this time RestTemplate Medium requestFactory It's not the default SimpleClientHttpRequestFactory 了 , It is RibbonClientHttpRequestFactory.

 1 @Configuration(proxyBeanMethods = false)
2 @ConditionalOnClass(HttpRequest.class)
3 @ConditionalOnRibbonRestClient
4 protected static class RibbonClientHttpRequestFactoryConfiguration {
5 @Autowired
6 private SpringClientFactory springClientFactory;
7
8 @Bean
9 public RestTemplateCustomizer restTemplateCustomizer(
10 final RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory) {
11 // RestTemplate Set up requestFactory by RibbonClientHttpRequestFactory
12 return restTemplate -> restTemplate
13 .setRequestFactory(ribbonClientHttpRequestFactory);
14 }
15
16 // ClientHttpRequest Factory => RibbonClientHttpRequestFactory
17 @Bean
18 public RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory() {
19 return new RibbonClientHttpRequestFactory(this.springClientFactory);
20 }
21 }

and , Because of the configuration here RestTemplateCustomizer, In the original default configuration , stay LoadBalancerAutoConfiguration Created in RestTemplateCustomizer And it won't work .

LoadBalancerAutoConfiguration Medium RestTemplateCustomizer Is to RestTemplate Add LoadBalancerInterceptor Interceptor , So it's on RestClient Under the circumstances , The original LoadBalancerInterceptor It won't work .

 1 @Configuration(proxyBeanMethods = false)
2 @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
3 static class LoadBalancerInterceptorConfig {
4
5 @Bean
6 public LoadBalancerInterceptor ribbonInterceptor(
7 LoadBalancerClient loadBalancerClient,
8 LoadBalancerRequestFactory requestFactory) {
9 return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
10 }
11
12 @Bean
13 @ConditionalOnMissingBean
14 public RestTemplateCustomizer restTemplateCustomizer(
15 final LoadBalancerInterceptor loadBalancerInterceptor) {
16 return restTemplate -> {
17 List<ClientHttpRequestInterceptor> list = new ArrayList<>(
18 restTemplate.getInterceptors());
19 list.add(loadBalancerInterceptor);
20 restTemplate.setInterceptors(list);
21 };
22 }
23 }

that RestTemplate Of doExecute In the method , Calling createRequest Method creation ClientHttpRequest when , Will use RibbonClientHttpRequestFactory To create , Go in and see ClientHttpRequest The actual type of is RibbonHttpRequest.

 1 public ClientHttpRequest createRequest(URI originalUri, HttpMethod httpMethod) throws IOException {
2 String serviceId = originalUri.getHost();
3 if (serviceId == null) {
4 throw new IOException("Invalid hostname in the URI [" + originalUri.toASCIIString() + "]");
5 }
6 IClientConfig clientConfig = this.clientFactory.getClientConfig(serviceId);
7 RestClient client = this.clientFactory.getClient(serviceId, RestClient.class);
8 HttpRequest.Verb verb = HttpRequest.Verb.valueOf(httpMethod.name());
9
10 return new RibbonHttpRequest(originalUri, verb, client, clientConfig);
11 }

call RibbonHttpRequest Of execute Method , It is called in the actual group executeInternal Method , And then finally, use RestClient To initiate a load balancing call .

 1 protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
2 try {
3 // ...
4 HttpRequest request = builder.build();
5 // client => RestClient
6 HttpResponse response = client.executeWithLoadBalancer(request, config);
7 return new RibbonHttpResponse(response);
8 }
9 catch (Exception e) {
10 throw new IOException(e);
11 }
12 }

④ RestClient HTTP call

RestClient Of executeWithLoadBalancer It's actually going into the parent class AbstractLoadBalancerAwareClient Of executeWithLoadBalancer   In the method .

From this method we can know that , The main load balancing requests are in LoadBalancerCommand Medium ,LoadBalancerCommand Must pass through the load balancer ILoadBalancer Get one Server, And then through submit This ServerOperation Original URI refactoring , After reconstruction, it is called RestClient Of execute launch HTTP request .

 1 public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
2 // Load balancing command
3 LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);
4
5 try {
6 // Initiate a load balancing request
7 return command.submit(
8 new ServerOperation<T>() {
9 @Override
10 public Observable<T> call(Server server) {
11 // restructure URI, Use the service name as Server Of IP And port replacement
12 URI finalUri = reconstructURIWithServer(server, request.getUri());
13 S requestForServer = (S) request.replaceUri(finalUri);
14 try {
15 // execute A call , What's actually called is RestClient Medium execute
16 return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
17 }
18 catch (Exception e) {
19 return Observable.error(e);
20 }
21 }
22 })
23 .toBlocking()
24 .single();
25 } catch (Exception e) {
26 //....
27 }
28 }

Look again RestClient Of execute Method , In the end, it can be found that ,RestClient Actually, it's based on jersey Of WebResource To initiate HTTP Requested .

 1 private HttpResponse execute(HttpRequest.Verb verb, URI uri,
2 Map<String, Collection<String>> headers, Map<String, Collection<String>> params,
3 IClientConfig overriddenClientConfig, Object requestEntity) throws Exception {
4 // ...
5 // WebResource Is based on jersey Packaged HTTP Client component
6 WebResource xResource = restClient.resource(uri.toString());
7 ClientResponse jerseyResponse;
8 Builder b = xResource.getRequestBuilder();
9 Object entity = requestEntity;
10
11 switch (verb) {
12 case GET:
13 jerseyResponse = b.get(ClientResponse.class);
14 break;
15 case POST:
16 jerseyResponse = b.post(ClientResponse.class, entity);
17 break;
18 case PUT:
19 jerseyResponse = b.put(ClientResponse.class, entity);
20 break;
21 case DELETE:
22 jerseyResponse = b.delete(ClientResponse.class);
23 break;
24 case HEAD:
25 jerseyResponse = b.head();
26 break;
27 case OPTIONS:
28 jerseyResponse = b.options(ClientResponse.class);
29 break;
30 default:
31 throw new ClientException(
32 ClientException.ErrorType.GENERAL,
33 "You have to one of the REST verbs such as GET, POST etc.");
34 }
35
36 thisResponse = new HttpClientResponse(jerseyResponse, uri, overriddenClientConfig);
37 if (thisResponse.getStatus() == 503){
38 thisResponse.close();
39 throw new ClientException(ClientException.ErrorType.SERVER_THROTTLED);
40 }
41 return thisResponse;
42 }

⑤ Last ,RestTemplate be based on RestClient The request process can be summarized with the following figure .

4、Apache HttpClient or OkHttp Yes RestTemplate Don't take effect (BUG?)

① Apache HttpClient

By default ,ribbon.httpclient.enabled=true, stay  HttpClientRibbonConfiguration Will initialize apache httpcomponents Related components , I've analyzed it before , But in RestTemplate No related components are used in .

in other words , By default apache httpcomponents, however RestTemplate The last is to use HttpURLConnection To initiate HTTP Requested , Not configured CloseableHttpClient.

② OkHttp

First you need to join OkHttp Dependence :

1 <dependency>
2 <groupId>com.squareup.okhttp3</groupId>
3 <artifactId>okhttp</artifactId>
4 </dependency>

Then add the following configuration to enable OkHttp:

1 ribbon:
2 httpclient:
3 enabled: false
4 # Enable okhttp
5 okhttp:
6 enabled: true

To configure ribbon.okhttp.enabled=true after , stay OkHttpRibbonConfiguration Will initialize OkHttp Related components .

But after debugging, you will find that , In fact, it still follows the default process , It's the end use HttpURLConnection launch HTTP request , Follow httpcomponents It's the same effect .

 1 @Configuration(proxyBeanMethods = false)
2 @ConditionalOnProperty("ribbon.okhttp.enabled")
3 @ConditionalOnClass(name = "okhttp3.OkHttpClient")
4 public class OkHttpRibbonConfiguration {
5
6 @RibbonClientName
7 private String name = "client";
8
9 @Bean
10 @ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
11 @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
12 public RetryableOkHttpLoadBalancingClient retryableOkHttpLoadBalancingClient(
13 IClientConfig config, ServerIntrospector serverIntrospector,
14 ILoadBalancer loadBalancer, RetryHandler retryHandler,
15 LoadBalancedRetryFactory loadBalancedRetryFactory, OkHttpClient delegate,
16 RibbonLoadBalancerContext ribbonLoadBalancerContext) {
17 RetryableOkHttpLoadBalancingClient client = new RetryableOkHttpLoadBalancingClient(
18 delegate, config, serverIntrospector, loadBalancedRetryFactory);
19 client.setLoadBalancer(loadBalancer);
20 client.setRetryHandler(retryHandler);
21 client.setRibbonLoadBalancerContext(ribbonLoadBalancerContext);
22 Monitors.registerObject("Client_" + this.name, client);
23 return client;
24 }
25
26 @Bean
27 @ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
28 @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
29 public OkHttpLoadBalancingClient okHttpLoadBalancingClient(IClientConfig config,
30 ServerIntrospector serverIntrospector, ILoadBalancer loadBalancer,
31 RetryHandler retryHandler, OkHttpClient delegate) {
32 OkHttpLoadBalancingClient client = new OkHttpLoadBalancingClient(delegate, config,
33 serverIntrospector);
34 client.setLoadBalancer(loadBalancer);
35 client.setRetryHandler(retryHandler);
36 Monitors.registerObject("Client_" + this.name, client);
37 return client;
38 }
39
40 @Configuration(proxyBeanMethods = false)
41 protected static class OkHttpClientConfiguration {
42
43 private OkHttpClient httpClient;
44
45 @Bean
46 @ConditionalOnMissingBean(ConnectionPool.class)
47 public ConnectionPool httpClientConnectionPool(IClientConfig config,
48 OkHttpClientConnectionPoolFactory connectionPoolFactory) {
49 RibbonProperties ribbon = RibbonProperties.from(config);
50 int maxTotalConnections = ribbon.maxTotalConnections();
51 long timeToLive = ribbon.poolKeepAliveTime();
52 TimeUnit ttlUnit = ribbon.getPoolKeepAliveTimeUnits();
53 return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
54 }
55
56 @Bean
57 @ConditionalOnMissingBean(OkHttpClient.class)
58 public OkHttpClient client(OkHttpClientFactory httpClientFactory,
59 ConnectionPool connectionPool, IClientConfig config) {
60 RibbonProperties ribbon = RibbonProperties.from(config);
61 this.httpClient = httpClientFactory.createBuilder(false)
62 .connectTimeout(ribbon.connectTimeout(), TimeUnit.MILLISECONDS)
63 .readTimeout(ribbon.readTimeout(), TimeUnit.MILLISECONDS)
64 .followRedirects(ribbon.isFollowRedirects())
65 .connectionPool(connectionPool).build();
66 return this.httpClient;
67 }
68
69 @PreDestroy
70 public void destroy() {
71 if (httpClient != null) {
72 httpClient.dispatcher().executorService().shutdown();
73 httpClient.connectionPool().evictAll();
74 }
75 }
76
77 }
78
79 }

View Cod

③ Enable HttpClient or OkHttp Reasons for non effectiveness

After the previous analysis , You can know to enable apache httpcomponents perhaps OkHttp, Yes RestTemplate It didn't work , In the end HttpURLConnection launch HTTP request . So why does this happen ? We can look at it RestTemplate  Of setRequestFactory Method .

adopt RestTemplate Of setRequestFactory The annotation of the method also shows that , default requestFactory yes SimpleClientHttpRequestFactory, It is based on JDK standard HTTP Library HttpURLConnection.

default HttpURLConnection I won't support it PATCH, If you want to support , It needs to be set to Apache HttpComponents or OkHttp Of request factory.

 1 /**
2 * Set the request factory that this accessor uses for obtaining client request handles.
3 * <p>The default is a {@link SimpleClientHttpRequestFactory} based on the JDK's own
4 * HTTP libraries ({@link java.net.HttpURLConnection}).
5 * <p><b>Note that the standard JDK HTTP library does not support the HTTP PATCH method.
6 * Configure the Apache HttpComponents or OkHttp request factory to enable PATCH.</b>
7 * @see #createRequest(URI, HttpMethod)
8 * @see SimpleClientHttpRequestFactory
9 * @see org.springframework.http.client.HttpComponentsAsyncClientHttpRequestFactory
10 * @see org.springframework.http.client.OkHttp3ClientHttpRequestFactory
11 */
12 public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
13 Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null");
14 this.requestFactory = requestFactory;
15 }

ClientHttpRequestFactory The architecture is as follows :

that RestClient How did it work ? From the analysis in the previous section, we can know that , stay RibbonAutoConfiguration There are the following configurations in , This RibbonClientHttpRequestFactoryConfiguration By customizing RestTemplateCustomizer towards RestTemplate Set up requestFactory by RibbonClientHttpRequestFactory.

 1 @Configuration(proxyBeanMethods = false)
2 @ConditionalOnClass(HttpRequest.class)
3 @ConditionalOnRibbonRestClient
4 protected static class RibbonClientHttpRequestFactoryConfiguration {
5 @Autowired
6 private SpringClientFactory springClientFactory;
7
8 @Bean
9 public RestTemplateCustomizer restTemplateCustomizer(final RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory) {
10 return restTemplate -> restTemplate.setRequestFactory(ribbonClientHttpRequestFactory);
11 }
12
13 @Bean
14 public RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory() {
15 return new RibbonClientHttpRequestFactory(this.springClientFactory);
16 }
17 }

RibbonClientHttpRequestFactory It's corresponding to RestClient Of , In other words, to enable OkHttp or HttpClient, You also need to create your own ClientHttpRequestFactory, And set to RestTemplate. You can see from the above class structure that , Yes, it provides HttpComponentsClientHttpRequestFactory and OkHttp3ClientHttpRequestFactory It's like a factory .

It's really strange here , Now that it's enabled apache httpcomponents perhaps OkHttp, It didn't create the default ClientHttpRequestFactory The implementation class is set to RestTemplate, It feels like spring-cloud-netflix-ribbon One of the BUG.

5、 customized RestTemplate Use Apache httpcomponents

If you want to let RestTemplate Use httpcomponents  The components of , You need to create your own ClientHttpRequestFactory, And set to RestTemplate. Let's take a step-by-step look at how to fix this problem .

① Set up HttpComponentsClientHttpRequestFactory

httpcomponents  Provided in ClientHttpRequestFactory The implementation class is HttpComponentsClientHttpRequestFactory, But you can't use this factory class directly , Because it creates HttpComponentsClientHttpRequest You don't have the ability to try again , It directly uses CloseableHttpClient Perform the requested , Although it has the function of overtime , But you can't try again . and , It doesn't have the ability of load balancing in essence , Need help LoadBalancerInterceptor Interceptor to refactor URI.

 1 final class HttpComponentsClientHttpRequest extends AbstractBufferingClientHttpRequest {
2 private final HttpClient httpClient;
3 private final HttpUriRequest httpRequest;
4 private final HttpContext httpContext;
5
6 @Override
7 protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
8 // ...
9 // httpClient => CloseableHttpClient
10 HttpResponse httpResponse = this.httpClient.execute(this.httpRequest, this.httpContext);
11 return new HttpComponentsClientHttpResponse(httpResponse);
12 }
13 }

therefore , If you don't need to try again , You can create a HttpComponentsClientHttpRequest, And set to RestTemplate that will do . This will use LoadBalancerInterceptor To do load balancing , restructure URI, And then use HttpComponentsClientHttpRequest To execute the request .

1 @Bean
2 @LoadBalanced
3 public RestTemplate restTemplate() {
4 HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
5 RestTemplate restTemplate = new RestTemplate();
6 restTemplate.setRequestFactory(requestFactory);
7 return restTemplate;
8 }

② customized apache ClientHttpRequestFactory

If you want to let RestTemplate It has the ability of load balancing , Can use again apache HttpComponents Components , And it has the function of retrying , We need to customize it ClientHttpRequestFactory 了 . About retrying, we'll talk about it separately .

contrast RestClient You can find ,RibbonClientHttpRequestFactory Created RibbonHttpRequest It's actually using RestClient Perform the requested , and RestClient  For internal use LoadBalancerCommand To try again .

Allied , We should at least use the one that has been configured RibbonLoadBalancingHttpClient To execute the request , So you need to customize a similar RibbonHttpRequest .

1) customized apache ClientHttpRequest

establish ApacheClientHttpRequest Inherited from RibbonHttpRequest, The core point is to inject RibbonLoadBalancingHttpClient, If you want to support retrying , Need to inject RetryableRibbonLoadBalancingHttpClient.RetryableRibbonLoadBalancingHttpClient In the introduction of spring-retry It will be created later , This later analysis will be retrying .

And then in executeInternal according to retryable Judge , If you want to try again , Just call execute Method , see RetryableRibbonLoadBalancingHttpClient Source code can be found , It supports load balancing itself , Will automatically select Server.

If you don't need to try again , You need to call executeWithLoadBalancer, It is used. LoadBalancerCommand To submit a request , Just follow RestClient It's the same . But it's different RibbonLoadBalancingHttpClient Of executeWithLoadBalancer It's not going to be retried , This is going to be analyzed later .

 1 package com.lyyzoo.sunny.register.ribbon.apache;
2
3 import java.io.IOException;
4 import java.net.URI;
5 import java.util.ArrayList;
6
7 import com.netflix.client.config.IClientConfig;
8 import com.netflix.client.http.HttpResponse;
9 import org.springframework.cloud.netflix.ribbon.RibbonHttpRequest;
10 import org.springframework.cloud.netflix.ribbon.RibbonHttpResponse;
11 import org.springframework.cloud.netflix.ribbon.apache.RetryableRibbonLoadBalancingHttpClient;
12 import org.springframework.cloud.netflix.ribbon.apache.RibbonApacheHttpRequest;
13 import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
14 import org.springframework.cloud.netflix.ribbon.support.RibbonCommandContext;
15 import org.springframework.http.HttpHeaders;
16 import org.springframework.http.HttpMethod;
17 import org.springframework.http.client.ClientHttpResponse;
18 import org.springframework.util.LinkedMultiValueMap;
19
20 /**
21 * Apache ClientHttpRequest
22 *
23 * @author bojiangzhou
24 */
25 public class ApacheClientHttpRequest extends RibbonHttpRequest {
26
27 private final URI uri;
28
29 private final HttpMethod httpMethod;
30
31 private final String serviceId;
32
33 private final RibbonLoadBalancingHttpClient client;
34
35 private final IClientConfig config;
36 /**
37 * Do you want to try again
38 */
39 private final boolean retryable;
40
41 public ApacheClientHttpRequest(URI uri,
42 HttpMethod httpMethod,
43 String serviceId,
44 RibbonLoadBalancingHttpClient client,
45 IClientConfig config,
46 boolean retryable) {
47 super(uri, null, null, config);
48 this.uri = uri;
49 this.httpMethod = httpMethod;
50 this.serviceId = serviceId;
51 this.client = client;
52 this.config = config;
53 this.retryable = retryable;
54 if (retryable && !(client instanceof RetryableRibbonLoadBalancingHttpClient)) {
55 throw new IllegalArgumentException("Retryable client must be RetryableRibbonLoadBalancingHttpClient");
56 }
57 }
58
59 @Override
60 protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
61 try {
62 RibbonCommandContext ribbonCommandContext = new RibbonCommandContext(serviceId, httpMethod.name(),
63 uri.toString(), retryable, headers, new LinkedMultiValueMap<>(), null, new ArrayList<>());
64
65 RibbonApacheHttpRequest request = new RibbonApacheHttpRequest(ribbonCommandContext);
66
67 HttpResponse response;
68 if (retryable) {
69 // RetryableRibbonLoadBalancingHttpClient It has the ability of load balancing
70 response = client.execute(request, config);
71 } else {
72 // RibbonLoadBalancingHttpClient Need to call executeWithLoadBalancer To have the ability of load balancing
73 response = client.executeWithLoadBalancer(request, config);
74 }
75
76 return new RibbonHttpResponse(response);
77 } catch (Exception e) {
78 throw new IOException(e);
79 }
80 }
81 }

2) customized apache ClientHttpRequestFactory

establish ApacheClientHttpRequestFactory Inherited from HttpComponentsClientHttpRequestFactory, Mainly in the createRequest Method to create a custom ApacheClientHttpRequest.RibbonLoadBalancingHttpClient It can be downloaded from SpringClientFactory In order to get .

 1 package com.lyyzoo.sunny.register.ribbon.apache;
2
3 import java.io.IOException;
4 import java.net.URI;
5
6 import com.netflix.client.config.IClientConfig;
7 import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
8 import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
9 import org.springframework.http.HttpMethod;
10 import org.springframework.http.client.ClientHttpRequest;
11 import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
12 import org.springframework.lang.NonNull;
13
14 /**
15 * Apache HttpComponents ClientHttpRequest factory
16 *
17 * @author bojiangzhou
18 */
19 public class ApacheClientHttpRequestFactory extends HttpComponentsClientHttpRequestFactory {
20
21 private final SpringClientFactory clientFactory;
22 private final boolean retryable;
23
24 public ApacheClientHttpRequestFactory(SpringClientFactory clientFactory, boolean retryable) {
25 this.clientFactory = clientFactory;
26 this.retryable = retryable;
27 }
28
29 @Override
30 @NonNull
31 public ClientHttpRequest createRequest(URI originalUri, HttpMethod httpMethod) throws IOException {
32 String serviceId = originalUri.getHost();
33 if (serviceId == null) {
34 throw new IOException(
35 "Invalid hostname in the URI [" + originalUri.toASCIIString() + "]");
36 }
37 IClientConfig clientConfig = this.clientFactory.getClientConfig(serviceId);
38 RibbonLoadBalancingHttpClient httpClient = this.clientFactory.getClient(serviceId, RibbonLoadBalancingHttpClient.class);
39
40 return new ApacheClientHttpRequest(originalUri, httpMethod, serviceId, httpClient, clientConfig, retryable);
41 }
42 }

3) customized apache ClientHttpRequestFactory Configuration class

Follow RestClient The configuration class of is similar to , customized ApacheClientHttpRequestFactory Configuration class , alike , Enabled by default httpclient. In existence RetryTemplate when , Is set ApacheClientHttpRequestFactory Of retryable Parameter is true, Otherwise false.

Then customize RestTemplateCustomizer, take ApacheClientHttpRequestFactory Set to RestTemplate in , Pay attention to this moment LoadBalancerInterceptor Will not be added to RestTemplate It's in .

 1 package com.lyyzoo.sunny.register.ribbon.apache;
2
3 import org.apache.http.client.HttpClient;
4 import org.springframework.beans.factory.annotation.Autowired;
5 import org.springframework.boot.autoconfigure.AutoConfigureAfter;
6 import org.springframework.boot.autoconfigure.AutoConfigureBefore;
7 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
8 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
9 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
10 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
11 import org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration;
12 import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
13 import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer;
14 import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
15 import org.springframework.context.annotation.Bean;
16 import org.springframework.context.annotation.Configuration;
17 import org.springframework.web.client.RestTemplate;
18
19 /**
20 *
21 * @author bojiangzhou
22 */
23 @Configuration
24 @ConditionalOnClass(RestTemplate.class)
25 @AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
26 @AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
27 @ConditionalOnProperty(name = "ribbon.httpclient.restTemplate.enabled", matchIfMissing = true)
28 public class ApacheClientHttpRequestFactoryConfiguration {
29
30 @Configuration(proxyBeanMethods = false)
31 @ConditionalOnClass(HttpClient.class)
32 @ConditionalOnProperty(name = "ribbon.httpclient.enabled", matchIfMissing = true)
33 static class HttpComponentsClientHttpRequestFactoryConfiguration {
34
35 @Autowired
36 private SpringClientFactory springClientFactory;
37
38 @Bean
39 @ConditionalOnMissingBean
40 public RestTemplateCustomizer restTemplateCustomizer(
41 final ApacheClientHttpRequestFactory apacheClientHttpRequestFactory) {
42 return restTemplate -> restTemplate
43 .setRequestFactory(apacheClientHttpRequestFactory);
44 }
45
46 @Bean
47 @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
48 public ApacheClientHttpRequestFactory apacheClientHttpRequestFactory() {
49 return new ApacheClientHttpRequestFactory(springClientFactory, false);
50 }
51
52 @Bean
53 @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
54 public ApacheClientHttpRequestFactory retryableApacheClientHttpRequestFactory() {
55 return new ApacheClientHttpRequestFactory(springClientFactory, true);
56 }
57 }
58 }

4) Simple debugging

After configuration , hold demo-consumer The service starts up , A simple test .

a) First of all, the request goes to RestTemplate Of doExecute in , And then through createRequest, call ApacheClientHttpRequestFactory establish ApacheClientHttpRequest.

b) Then call ApacheClientHttpRequest Of execute Method , stay ApacheClientHttpRequest  Of executeInternal in , Will call RibbonLoadBalancingHttpClient Of executeWithLoadBalancer Method .

c) Last , Get into RibbonLoadBalancingHttpClient Of execute In the method , It then forwards the request to the proxy object delegate To execute ,delegate Is in the HttpClientRibbonConfiguration Configured in CloseableHttpClient object , The actual type is InternalHttpClient.

After verification , Through custom configuration , Eventually making RestTemplate have access to apache httpcomponents Component to execute HTTP request . Try that piece again and study it later .

③ Let's summarize it with a picture RestTemplate be based on apache HttpClient After the implementation process

6、 customized RestTemplate Use OkHttp

① Set up OkHttp3ClientHttpRequestFactory

Allied , You can give RestTemplate Set up directly OkHttp3ClientHttpRequestFactory, But it also doesn't have the ability to try again .

1 @Bean
2 @LoadBalanced
3 public RestTemplate restTemplate() {
4 OkHttp3ClientHttpRequestFactory requestFactory = new OkHttp3ClientHttpRequestFactory();
5 RestTemplate restTemplate = new RestTemplate();
6 restTemplate.setRequestFactory(requestFactory);
7 return restTemplate;
8 }

② customized OkHttp ClientHttpRequestFactory

With customization apache httpcomponents  similar , I just put out the code of the three classes .

a) OkHttpClientHttpRequest:

 1 package com.lyyzoo.sunny.register.ribbon.okhttp;
2
3 import java.io.IOException;
4 import java.net.URI;
5 import java.util.ArrayList;
6
7 import com.netflix.client.config.IClientConfig;
8 import com.netflix.client.http.HttpResponse;
9 import org.springframework.cloud.netflix.ribbon.RibbonHttpRequest;
10 import org.springframework.cloud.netflix.ribbon.RibbonHttpResponse;
11 import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpLoadBalancingClient;
12 import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpRibbonRequest;
13 import org.springframework.cloud.netflix.ribbon.okhttp.RetryableOkHttpLoadBalancingClient;
14 import org.springframework.cloud.netflix.ribbon.support.RibbonCommandContext;
15 import org.springframework.http.HttpHeaders;
16 import org.springframework.http.HttpMethod;
17 import org.springframework.http.client.ClientHttpResponse;
18 import org.springframework.util.LinkedMultiValueMap;
19
20 /**
21 * OkHttp ClientHttpRequest
22 *
23 * @author bojiangzhou
24 */
25 public class OkHttpClientHttpRequest extends RibbonHttpRequest {
26
27 private final URI uri;
28
29 private final HttpMethod httpMethod;
30
31 private final String serviceId;
32
33 private final OkHttpLoadBalancingClient client;
34
35 private final IClientConfig config;
36 /**
37 * Do you want to try again
38 */
39 private final boolean retryable;
40
41 public OkHttpClientHttpRequest(URI uri,
42 HttpMethod httpMethod,
43 String serviceId,
44 OkHttpLoadBalancingClient client,
45 IClientConfig config,
46 boolean retryable) {
47 super(uri, null, null, config);
48 this.uri = uri;
49 this.httpMethod = httpMethod;
50 this.serviceId = serviceId;
51 this.client = client;
52 this.config = config;
53 this.retryable = retryable;
54 if (retryable && !(client instanceof RetryableOkHttpLoadBalancingClient)) {
55 throw new IllegalArgumentException("Retryable client must be RetryableOkHttpLoadBalancingClient");
56 }
57 }
58
59 @Override
60 protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
61 try {
62 RibbonCommandContext ribbonCommandContext = new RibbonCommandContext(serviceId, httpMethod.name(),
63 uri.toString(), retryable, headers, new LinkedMultiValueMap<>(), null, new ArrayList<>());
64
65 OkHttpRibbonRequest request = new OkHttpRibbonRequest(ribbonCommandContext);
66
67 HttpResponse response;
68 if (retryable) {
69 // RetryableRibbonLoadBalancingHttpClient It has the ability of load balancing
70 response = client.execute(request, config);
71 } else {
72 // RibbonLoadBalancingHttpClient Need to call executeWithLoadBalancer To have the ability of load balancing
73 response = client.executeWithLoadBalancer(request, config);
74 }
75
76 return new RibbonHttpResponse(response);
77 } catch (Exception e) {
78 throw new IOException(e);
79 }
80 }
81 }

b) OkHttpClientHttpRequestFactory

 1 package com.lyyzoo.sunny.register.ribbon.okhttp;
2
3 import java.io.IOException;
4 import java.net.URI;
5
6 import com.netflix.client.config.IClientConfig;
7 import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
8 import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpLoadBalancingClient;
9 import org.springframework.http.HttpMethod;
10 import org.springframework.http.client.ClientHttpRequest;
11 import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
12 import org.springframework.lang.NonNull;
13
14 /**
15 * OkHttp ClientHttpRequest factory
16 *
17 * @author bojiangzhou
18 */
19 public class OkHttpClientHttpRequestFactory extends HttpComponentsClientHttpRequestFactory {
20
21 private final SpringClientFactory clientFactory;
22 private final boolean retryable;
23
24 public OkHttpClientHttpRequestFactory(SpringClientFactory clientFactory, boolean retryable) {
25 this.clientFactory = clientFactory;
26 this.retryable = retryable;
27 }
28
29 @Override
30 @NonNull
31 public ClientHttpRequest createRequest(URI originalUri, HttpMethod httpMethod) throws IOException {
32 String serviceId = originalUri.getHost();
33 if (serviceId == null) {
34 throw new IOException(
35 "Invalid hostname in the URI [" + originalUri.toASCIIString() + "]");
36 }
37 IClientConfig clientConfig = this.clientFactory.getClientConfig(serviceId);
38 OkHttpLoadBalancingClient httpClient = this.clientFactory.getClient(serviceId, OkHttpLoadBalancingClient.class);
39
40 return new OkHttpClientHttpRequest(originalUri, httpMethod, serviceId, httpClient, clientConfig, retryable);
41 }
42 }

c) OkHttpClientHttpRequestFactoryConfiguration

 1 package com.lyyzoo.sunny.register.ribbon.okhttp;
2
3 import org.springframework.beans.factory.annotation.Autowired;
4 import org.springframework.boot.autoconfigure.AutoConfigureAfter;
5 import org.springframework.boot.autoconfigure.AutoConfigureBefore;
6 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
7 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
8 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
9 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
10 import org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration;
11 import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
12 import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer;
13 import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
14 import org.springframework.context.annotation.Bean;
15 import org.springframework.context.annotation.Configuration;
16 import org.springframework.web.client.RestTemplate;
17
18 /**
19 *
20 * @author bojiangzhou
21 */
22 @Configuration
23 @ConditionalOnClass(RestTemplate.class)
24 @AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
25 @AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
26 @ConditionalOnProperty(name = "ribbon.okhttp.restTemplate.enabled", matchIfMissing = true)
27 public class OkHttpClientHttpRequestFactoryConfiguration {
28
29 @Configuration(proxyBeanMethods = false)
30 @ConditionalOnProperty("ribbon.okhttp.enabled")
31 @ConditionalOnClass(name = "okhttp3.OkHttpClient")
32 static class ClientHttpRequestFactoryConfiguration {
33
34 @Autowired
35 private SpringClientFactory springClientFactory;
36
37 @Bean
38 @ConditionalOnMissingBean
39 public RestTemplateCustomizer restTemplateCustomizer(
40 final OkHttpClientHttpRequestFactory okHttpClientHttpRequestFactory) {
41 return restTemplate -> restTemplate
42 .setRequestFactory(okHttpClientHttpRequestFactory);
43 }
44
45 @Bean
46 @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
47 public OkHttpClientHttpRequestFactory okHttpClientHttpRequestFactory() {
48 return new OkHttpClientHttpRequestFactory(springClientFactory, false);
49 }
50
51 @Bean
52 @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
53 public OkHttpClientHttpRequestFactory retryableOkHttpClientHttpRequestFactory() {
54 return new OkHttpClientHttpRequestFactory(springClientFactory, true);
55 }
56 }
57 }

7、 ... and 、RestTemplate Time out to try again

1、AbstractLoadBalancerAwareClient

① AbstractLoadBalancerAwareClient

Through the analysis in the previous section , You can see that there are actually two components with the function of retrying , One is Ribbon Of LoadBalancerCommand, One is spring-retry Of RetryTemplate.RetryableRibbonLoadBalancingHttpClient and RetryableOkHttpLoadBalancingClient All depend on RetryTemplate, So we have to introduce spring-retry rely on , They all end up using RetryTemplate Implementation of the ability to request retries . except RetryTemplate, Other clients want to get the function of retrying , To use ribbon Medium  AbstractLoadBalancerAwareClient Related components , And call executeWithLoadBalancer Method .

Let's take a look at AbstractLoadBalancerAwareClient The system of , You can learn from the source code :

  • RetryableFeignLoadBalancer、RetryableRibbonLoadBalancingHttpClient、RetryableOkHttpLoadBalancingClient Is to use RetryTemplate The function of retrying , That is to say spring-retry Retry .
  • RestClient、FeignLoadBalancer、RibbonLoadBalancingHttpClient、OkHttpLoadBalancingClient Is in AbstractLoadBalancerAwareClient Use in LoadBalancerCommand The function of retrying , It's just Ribbon Retry .

② executeWithLoadBalancer

Concrete AbstractLoadBalancerAwareClient The client wants load balancing calls and can try again , Need to call AbstractLoadBalancerAwareClient Of executeWithLoadBalancer Method .

In this way , It builds LoadBalancerCommand, And then use command Submitted a ServerOperation, This ServerOperation Chinese vs URI the restructure , Turn to specific LoadBalancerContext To execute the request .

 1 public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
2 // Load balancing command
3 LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);
4
5 try {
6 return command.submit(
7 new ServerOperation<T>() {
8 @Override
9 public Observable<T> call(Server server) {
10 // restructure URI
11 URI finalUri = reconstructURIWithServer(server, request.getUri());
12 S requestForServer = (S) request.replaceUri(finalUri);
13 try {
14 // Use specific AbstractLoadBalancerAwareClient Client execution request
15 return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
16 }
17 catch (Exception e) {
18 return Observable.error(e);
19 }
20 }
21 })
22 .toBlocking()
23 .single();
24 }
25 }

Look again buildLoadBalancerCommand Method , It will pass first getRequestSpecificRetryHandler Method to get the request retrial processor RequestSpecificRetryHandler, and getRequestSpecificRetryHandler It's an abstract method . Here we should pay more attention to .

 1 //  Abstract method , Get request retrying processor 
2 public abstract RequestSpecificRetryHandler getRequestSpecificRetryHandler(S request, IClientConfig requestConfig);
3
4 protected LoadBalancerCommand<T> buildLoadBalancerCommand(final S request, final IClientConfig config) {
5 // Get request retrying processor
6 RequestSpecificRetryHandler handler = getRequestSpecificRetryHandler(request, config);
7 LoadBalancerCommand.Builder<T> builder = LoadBalancerCommand.<T>builder()
8 .withLoadBalancerContext(this)
9 .withRetryHandler(handler)
10 .withLoadBalancerURI(request.getUri());
11 customizeLoadBalancerCommandBuilder(request, config, builder);
12 return builder.build();
13 }

2、 Request retrying processor RequestSpecificRetryHandler

① Let's get to know RequestSpecificRetryHandler:

  • First of all, look at its construction method , Notice the first parameter and the second parameter , Because it's different getRequestSpecificRetryHandler Method realization , The main difference lies in these two parameters .
  • And then look isRetriableException, This method is LoadBalancerCommand The method used to determine whether to try again after an exception , You can see that okToRetryOnAllErrors=true You can try again , otherwise okToRetryOnConnectErrors=true To try again . Even if it's a return method, you need to pay attention to it true You don't have to try again , It also has something to do with the number of retries .
 1 public RequestSpecificRetryHandler(boolean okToRetryOnConnectErrors, boolean okToRetryOnAllErrors, RetryHandler baseRetryHandler, @Nullable IClientConfig requestConfig) {
2 Preconditions.checkNotNull(baseRetryHandler);
3 this.okToRetryOnConnectErrors = okToRetryOnConnectErrors;
4 this.okToRetryOnAllErrors = okToRetryOnAllErrors;
5 this.fallback = baseRetryHandler;
6 if (requestConfig != null) {
7 // In the same Server The number of retries on
8 if (requestConfig.containsProperty(CommonClientConfigKey.MaxAutoRetries)) {
9 retrySameServer = requestConfig.get(CommonClientConfigKey.MaxAutoRetries);
10 }
11 // Try the next one Server The number of times
12 if (requestConfig.containsProperty(CommonClientConfigKey.MaxAutoRetriesNextServer)) {
13 retryNextServer = requestConfig.get(CommonClientConfigKey.MaxAutoRetriesNextServer);
14 }
15 }
16 }
17
18 @Override
19 public boolean isRetriableException(Throwable e, boolean sameServer) {
20 // All errors are retried
21 if (okToRetryOnAllErrors) {
22 return true;
23 }
24 // ClientException To try again
25 else if (e instanceof ClientException) {
26 ClientException ce = (ClientException) e;
27 if (ce.getErrorType() == ClientException.ErrorType.SERVER_THROTTLED) {
28 return !sameServer;
29 } else {
30 return false;
31 }
32 }
33 else {
34 // Try again after connection error , Is to throw SocketException Try again when it is abnormal
35 return okToRetryOnConnectErrors && isConnectionException(e);
36 }
37 }

② Different LoadBalancerCommand Of getRequestSpecificRetryHandler Realization

a)RestClient

Under default configuration ,RestClient Of getRequestSpecificRetryHandler Will come to the last step ,okToRetryOnConnectErrors、okToRetryOnAllErrors All for true, in other words isRetriableException Always return true, That is to say, if an exception is thrown, it will be retried .

 1 @Override
2 public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
3 HttpRequest request, IClientConfig requestConfig) {
4 if (!request.isRetriable()) {
5 return new RequestSpecificRetryHandler(false, false, this.getRetryHandler(), requestConfig);
6 }
7 if (this.ncc.get(CommonClientConfigKey.OkToRetryOnAllOperations, false)) {
8 return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), requestConfig);
9 }
10 if (request.getVerb() != HttpRequest.Verb.GET) {
11 return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(), requestConfig);
12 } else {
13 // okToRetryOnConnectErrors、okToRetryOnAllErrors All for true
14 return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), requestConfig);
15 }
16 }

b)AbstractLoadBalancingClient

AbstractLoadBalancingClient Medium getRequestSpecificRetryHandler Equivalent to a default implementation , By default okToRetryOnAllOperations by false, Finally, it will be the last step , namely okToRetryOnConnectErrors、okToRetryOnAllErrors All for true,isRetriableException Always return true.

 1 @Override
2 public RequestSpecificRetryHandler getRequestSpecificRetryHandler(final S request, final IClientConfig requestConfig) {
3 // okToRetryOnAllOperations: Whether all operations are retried , Default false
4 if (this.okToRetryOnAllOperations) {
5 return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), requestConfig);
6 }
7 if (!request.getContext().getMethod().equals("GET")) {
8 return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(), requestConfig);
9 }
10 else {
11 return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), requestConfig);
12 }
13 }

c)RibbonLoadBalancingHttpClient

RibbonLoadBalancingHttpClient It's overloaded getRequestSpecificRetryHandler, But it sets okToRetryOnConnectErrors、okToRetryOnAllErrors All for false,isRetriableException Always return false.

At this point, we should know why RibbonLoadBalancingHttpClient Of executeWithLoadBalancer The reason why you don't have the ability to try again . So enable apache httpclient when ,RibbonLoadBalancingHttpClient Call does not support retrying .

1 @Override
2 public RequestSpecificRetryHandler getRequestSpecificRetryHandler(RibbonApacheHttpRequest request, IClientConfig requestConfig) {
3 // okToRetryOnConnectErrors、okToRetryOnAllErrors All for false
4 return new RequestSpecificRetryHandler(false, false, RetryHandler.DEFAULT, requestConfig);
5 }

RetryableRibbonLoadBalancingHttpClient It also rewrites getRequestSpecificRetryHandler, It's also the setting okToRetryOnConnectErrors、okToRetryOnAllErrors All for false. But in the introduction of spring-retry after , It will use RetryTemplate Realize the function of retrying .

1 @Override
2 public RequestSpecificRetryHandler getRequestSpecificRetryHandler(RibbonApacheHttpRequest request, IClientConfig requestConfig) {
3 // okToRetryOnConnectErrors、okToRetryOnAllErrors All for false
4 return new RequestSpecificRetryHandler(false, false, RetryHandler.DEFAULT, null);
5 }

d)OkHttpLoadBalancingClient

OkHttpLoadBalancingClient I didn't rewrite it getRequestSpecificRetryHandler, So it uses the parent class AbstractLoadBalancingClient The method in , That is to say okToRetryOnConnectErrors、okToRetryOnAllErrors All for true. therefore , Enable okhttp when ,OkHttpLoadBalancingClient It supports retrying , Here we need to pay attention to .

and RetryableOkHttpLoadBalancingClient Follow RetryableRibbonLoadBalancingHttpClient The same way to rewrite , Use RetryTemplate Implementation of retrying .

1 @Override
2 public RequestSpecificRetryHandler getRequestSpecificRetryHandler(RibbonApacheHttpRequest request, IClientConfig requestConfig) {
3 // okToRetryOnConnectErrors、okToRetryOnAllErrors All for false
4 return new RequestSpecificRetryHandler(false, false, RetryHandler.DEFAULT, null);
5 }

3、LoadBalancerCommand

see LoadBalancerCommand Of submit Method , This method is the core code for retrying .

  • First of all, I got the same Server Retry count maxRetrysSame and Try the next one Server The number of times maxRetrysNext, In fact, it's the configuration in front of you ribbon.MaxAutoRetries and ribbon.MaxAutoRetriesNextServer, What I set up is 1.
  • And then I created one Observable, The first layer of it will go through first loadBalancerContext obtain Server. Try again next Server when , Here you get the next one Server.
  • In the second floor , Another Observable, This Observable It's called ServerOperation Of , It's reconstruction. URI, Call specific AbstractLoadBalancerAwareClient Perform the requested .
  • In the second floor , Will be based on maxRetrysSame Try the same one again Server, from retryPolicy We can learn from , When the number of retries is greater than maxRetrysSame after , The same Server Try again and it's over , Otherwise it will be used. retryHandler.isRetriableException Determine whether to try again , This has been analyzed before .
  • In the outer layer , According to maxRetrysNext Try different Server, from retryPolicy We can learn from , When it's different Server The number of retries is greater than maxRetrysNext after , It's all over again , The whole retrial is over , If it still fails , Will enter onErrorResumeNext Do the final failure handling .

Finally, let's summarize LoadBalancerCommand retry :

  • Retrying is divided into the same Server Retrying and retrying the next Server, When the number of retries is greater than the set value , Stop trying again . Otherwise, by retryHandler.isRetriableException Determine whether to try again .
  • How many requests have been made here ? We can sum up the following formula : Number of requests = (maxRetrysSame + 1) * (maxRetrysNext + 1), So press ribbon.MaxAutoRetries = 1、ribbon.MaxAutoRetriesNextServer = 1 Configuration of , If every request times out , It will initiate 4 Requests .
 1 public Observable<T> submit(final ServerOperation<T> operation) {
2 final ExecutionInfoContext context = new ExecutionInfoContext();
3
4 // The same Server Retry count
5 final int maxRetrysSame = retryHandler.getMaxRetriesOnSameServer();
6 // Try the next one Server The number of times
7 final int maxRetrysNext = retryHandler.getMaxRetriesOnNextServer();
8
9 // Create a Observable
10 Observable<T> o =
11 // Use loadBalancerContext obtain Server
12 (server == null ? selectServer() : Observable.just(server))
13 .concatMap(new Func1<Server, Observable<T>>() {
14 @Override
15 public Observable<T> call(Server server) {
16 // Set up Server
17 context.setServer(server);
18 final ServerStats stats = loadBalancerContext.getServerStats(server);
19
20 // establish Observable
21 Observable<T> o = Observable
22 .just(server)
23 .concatMap(new Func1<Server, Observable<T>>() {
24 @Override
25 public Observable<T> call(final Server server) {
26 // Increase the number of attempts
27 context.incAttemptCount();
28 // ...
29 // call ServerOperation
30 return operation.call(server).doOnEach(new Observer<T>() {
31 // Some callback methods
32 });
33 }
34 });
35 // Try the same one again Server
36 if (maxRetrysSame > 0)
37 o = o.retry(retryPolicy(maxRetrysSame, true));
38 return o;
39 }
40 });
41
42 if (maxRetrysNext > 0 && server == null)
43 // It's different Server
44 o = o.retry(retryPolicy(maxRetrysNext, false));
45
46 return o.onErrorResumeNext(new Func1<Throwable, Observable<T>>() {
47 @Override
48 public Observable<T> call(Throwable e) {
49 // exception handling
50 return Observable.error(e);
51 }
52 });
53 }
54
55 // retryPolicy Returns an assertion whether to try again
56 private Func2<Integer, Throwable, Boolean> retryPolicy(final int maxRetrys, final boolean same) {
57 return new Func2<Integer, Throwable, Boolean>() {
58 @Override
59 public Boolean call(Integer tryCount, Throwable e) {
60 // If the request is rejected, the exception is not allowed to be retried
61 if (e instanceof AbortExecutionException) {
62 return false;
63 }
64 // Whether the number of attempts is greater than the maximum number of retries
65 if (tryCount > maxRetrys) {
66 return false;
67 }
68 // Use RequestSpecificRetryHandler Determine whether to try again
69 return retryHandler.isRetriableException(e, same);
70 }
71 };
72 }

4、RetryTemplate

① spring-retry

To enable the RetryTemplate We need to introduce spring-retry:

1 <dependency>
2 <groupId>org.springframework.retry</groupId>
3 <artifactId>spring-retry</artifactId>
4 </dependency>

With RetryableRibbonLoadBalancingHttpClient For example , Take a look at it first execute Method , It first creates a load balancing retry policy class LoadBalancedRetryPolicy, Then encapsulate the logic of the request call to RetryCallback in , In the end, it's actually using RetryTemplate Execute this RetryCallback, That is to say, the logic of request for retrial is all in RetryTemplate in .

 1 public RibbonApacheHttpResponse execute(final RibbonApacheHttpRequest request, final IClientConfig configOverride) throws Exception {
2 //...
3
4 // Load balancing retry strategy RibbonLoadBalancedRetryPolicy
5 final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryFactory.createRetryPolicy(this.getClientName(), this);
6
7 RetryCallback<RibbonApacheHttpResponse, Exception> retryCallback = context -> {
8 // ...
9 // delegate => CloseableHttpClient
10 final HttpResponse httpResponse = RetryableRibbonLoadBalancingHttpClient.this.delegate.execute(httpUriRequest);
11 // ...
12 // success Return results
13 return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());
14 };
15
16 LoadBalancedRecoveryCallback<RibbonApacheHttpResponse, HttpResponse> recoveryCallback = new LoadBalancedRecoveryCallback<RibbonApacheHttpResponse, HttpResponse>()//...
17
18 return this.executeWithRetry(request, retryPolicy, retryCallback, recoveryCallback);
19 }
20
21 private RibbonApacheHttpResponse executeWithRetry(RibbonApacheHttpRequest request,
22 LoadBalancedRetryPolicy retryPolicy,
23 RetryCallback<RibbonApacheHttpResponse, Exception> callback,
24 RecoveryCallback<RibbonApacheHttpResponse> recoveryCallback)
25 throws Exception {
26 RetryTemplate retryTemplate = new RetryTemplate();
27
28 // retryable => Taken from the RibbonCommandContext Set up retryable Parameters
29 boolean retryable = isRequestRetryable(request);
30 // Set retry policy
31 retryTemplate.setRetryPolicy(retryPolicy == null || !retryable
32 ? new NeverRetryPolicy() : new RetryPolicy(request, retryPolicy, this, this.getClientName()));
33
34 BackOffPolicy backOffPolicy = loadBalancedRetryFactory.createBackOffPolicy(this.getClientName());
35 retryTemplate.setBackOffPolicy(backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy);
36
37 // utilize retryTemplate Perform the requested callback
38 return retryTemplate.execute(callback, recoveryCallback);
39 }

It should be noted that , stay executeWithRetry in , Will determine whether to try again , In the logic of judgment getRetryable In fact, it's just taking ApacheClientHttpRequest in executeInternal Method RibbonCommandContext Set up retryable Parameters , This is connected with the previous customized logic .

1 private boolean isRequestRetryable(ContextAwareRequest request) {
2 if (request.getContext() == null || request.getContext().getRetryable() == null) {
3 return true;
4 }
5 return request.getContext().getRetryable();
6 }

② RetryTemplate

Get into RetryTemplate Of execute Method , The core logic is reduced to the following code , It's mainly a while Loop to determine whether it can try again , And then call retryCallback Perform the requested . After the request fails , For example, timeout. , Throw an exception , will registerThrowable To register exceptions .

 1 protected <T, E extends Throwable> T doExecute(RetryCallback<T, E> retryCallback,
2 RecoveryCallback<T> recoveryCallback, RetryState state) throws E, ExhaustedRetryException {
3
4 // retryPolicy => InterceptorRetryPolicy
5 RetryPolicy retryPolicy = this.retryPolicy;
6 BackOffPolicy backOffPolicy = this.backOffPolicy;
7 //....
8 try {
9 // ...
10 // canRetry Determine whether to try again
11 while (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) {
12 try {
13 // retryCallback call
14 return retryCallback.doWithRetry(context);
15 }
16 catch (Throwable e) {
17 // ...
18 // Registration exception
19 registerThrowable(retryPolicy, state, context, e);
20 // ...
21 }
22 }
23 exhausted = true;
24 return handleRetryExhausted(recoveryCallback, context, state);
25 }
26 //...
27 }

see canRetry Method , It actually calls InterceptorRetryPolicy Of canRetry. On the first call , Will go to get Server; Otherwise it will be used. RibbonLoadBalancedRetryPolicy Decide whether to try the next one Server, Notice that the logic of its judgment is GET Request or allow all operations to be retried , And Server Retry count nextServerCount  Less than or equal to configured MaxAutoRetriesNextServer . in other words ,while Circular judgment canRetry It's retrying the next one Server Of .

 1 protected boolean canRetry(RetryPolicy retryPolicy, RetryContext context) {
2 return retryPolicy.canRetry(context);
3 }
4
5 //////////// InterceptorRetryPolicy
6 public boolean canRetry(RetryContext context) {
7 LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
8 if (lbContext.getRetryCount() == 0 && lbContext.getServiceInstance() == null) {
9 // obtain Server
10 lbContext.setServiceInstance(this.serviceInstanceChooser.choose(this.serviceName));
11 return true;
12 }
13 // RibbonLoadBalancedRetryPolicy => Try the next one Server
14 return this.policy.canRetryNextServer(lbContext);
15 }
16
17 ///////// RibbonLoadBalancedRetryPolicy
18 public boolean canRetryNextServer(LoadBalancedRetryContext context) {
19 // Judge to try again next Server
20 return nextServerCount <= lbContext.getRetryHandler().getMaxRetriesOnNextServer()
21 && canRetry(context);
22 }
23
24 public boolean canRetry(LoadBalancedRetryContext context) {
25 // GET When all operations are requested or allowed to be retried , You're allowed to try again
26 HttpMethod method = context.getRequest().getMethod();
27 return HttpMethod.GET == method || lbContext.isOkToRetryOnAllOperations();
28 }

Then look at the registration exception after the request fails registerThrowable, It will end up with RibbonLoadBalancedRetryPolicy Registration exception . stay RibbonLoadBalancedRetryPolicy Of registerThrowable In the method , If you don't try the same one again Server And you can try the next Server, It will poll for the next Server. If it could be in the same Server Try again on ,sameServerCount The counter is just +1, Otherwise reset sameServerCount, then nextServerCount +1.

 1 protected void registerThrowable(RetryPolicy retryPolicy, RetryState state,
2 RetryContext context, Throwable e) {
3 retryPolicy.registerThrowable(context, e);
4 registerContext(context, state);
5 }
6
7 ///////// InterceptorRetryPolicy /////////
8 public void registerThrowable(RetryContext context, Throwable throwable) {
9 LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
10 lbContext.registerThrowable(throwable);
11 // RibbonLoadBalancedRetryPolicy
12 this.policy.registerThrowable(lbContext, throwable);
13 }
14
15 ///////// RibbonLoadBalancedRetryPolicy /////////
16 public void registerThrowable(LoadBalancedRetryContext context, Throwable throwable) {
17 //...
18 // If not in the same Server Try again on and you can try the next one Server, Choose a new one Server
19 if (!canRetrySameServer(context) && canRetryNextServer(context)) {
20 context.setServiceInstance(loadBalanceChooser.choose(serviceId));
21 }
22
23 // The same Server After retrying beyond the set value , Just reset sameServerCount
24 if (sameServerCount >= lbContext.getRetryHandler().getMaxRetriesOnSameServer()
25 && canRetry(context)) {
26 // Reset nextServerCount
27 sameServerCount = 0;
28 // next Server Retry count +1
29 nextServerCount++;
30 if (!canRetryNextServer(context)) {
31 // You can't try the next one Server 了
32 context.setExhaustedOnly();
33 }
34 }
35 else {
36 // The same Server Retry count +1
37 sameServerCount++;
38 }
39 }
40
41 // Determine whether to retry the same Server
42 public boolean canRetrySameServer(LoadBalancedRetryContext context) {
43 return sameServerCount < lbContext.getRetryHandler().getMaxRetriesOnSameServer()
44 && canRetry(context);
45 }
46
47 public boolean canRetry(LoadBalancedRetryContext context) {
48 // GET When all operations are requested or allowed to be retried , You're allowed to try again
49 HttpMethod method = context.getRequest().getMethod();
50 return HttpMethod.GET == method || lbContext.isOkToRetryOnAllOperations();
51 }

5、Ribbon Try to summarize

① First ,Ribbon The configuration parameters for timeout and retry are as follows , These parameters can also be configured for a client :

 1 ribbon:
2 # Client read timeout
3 ReadTimeout: 1000
4 # Client connection timeout
5 ConnectTimeout: 1000
6 # By default, only try again GET, Set to true All types will be retried when , Such as POST、PUT、DELETE
7 OkToRetryOnAllOperations: false
8 # The same Server Retry count
9 MaxAutoRetries: 1
10 # Try a few more at most Server
11 MaxAutoRetriesNextServer: 1

② RetryTemplate yes spring-retry Retrying component for ,LoadBalancerCommand yes Ribbon Retrying component for . The number of requests they try again is the same , The retrial logic is similar , It's all about retrying the current Server, Try the next one again Server, Total requests = (MaxAutoRetries + 1) * (MaxAutoRetriesNextServer + 1).

③ But the difference is ,RetryTemplate Will judge that the request method is GET perhaps OkToRetryOnAllOperations=true Only when you try again , and LoadBalancerCommand  It's all http Methods can be retried . This is actually a problem , Usually only GET To allow you to try again , because GET It's a query operation , The interface is idempotent , and POST、PUT、DELETE It is generally non idempotent . So it is generally recommended to use RetryTemplate, And configure OkToRetryOnAllOperations=false.

④ In order to improve the communication performance between services , Generally, you can enable apache httpclient perhaps OkHttp, If you want to enable the retry function , It also needs to be introduced spring-retry rely on . Try again , At present Server Don't try again (MaxAutoRetries=0), Try the next one directly Server.

 1 ribbon:
2 # Client read timeout
3 ReadTimeout: 1000
4 # Client connection timeout
5 ConnectTimeout: 1000
6 # By default, only try again GET, Set to true All types will be retried when , Such as POST、PUT、DELETE
7 OkToRetryOnAllOperations: false
8 # The same Server Retry count
9 MaxAutoRetries: 0
10 # Try a few more at most Server
11 MaxAutoRetriesNextServer: 1
12 # Enable httpclient
13 httpclient:
14 enabled: false
15 # Enable RestClient
16 restclient:
17 enabled: false
18 # Enable okhttp
19 okhttp:
20 enabled: true

SpringCloud Source series (5)—— Load balancing Ribbon( Next ) More articles about

  1. SpringCloud To realize the load balance of the client Ribbon( Two )

    One  Ribbon brief introduction Ribbon yes Netflix Published load balancer , It helps control HTTP and TCP The behavior of the client . by Ribbon After the service provider address is configured ,Ribbon Can be based on some kind of load balancing algorithm , Automatically help services ...

  2. Dubbo Source code analysis 4 —— Load balancing LoadBalance

    Welcome to my Star Followers Continue to update later Dubbo Other articles Dubbo Source code analysis series one environment build Dubbo Introduction 2 --- Project structure analysis Dubbo Source code analysis series 3 -- The architecture is original ...

  3. dubbo Load balancing of source code parsing

    In distributed systems , Load balancing is an essential module ,dubbo Five load balancing implementations are provided in , Before reading this source code , It is recommended to learn the basic knowledge of load balancing first . See the source code as a process to confirm what you think , This will get twice the result with half the effort ...

  4. dubbo Source code analysis 1—— Load balancing

    dubbo There are only four load balancing algorithms involved in :Random LoadBalance( Random equalization algorithm ).RoundRobin LoadBalance( Weighted round robin equilibrium algorithm ).LeastAction LoadBa ...

  5. dubbo Load balancing of source code reading

    Load balancing In the previous article on clustering , We analyzed that multiple service providers can be obtained by listening to the registry , And create multiple Invoker, Then through the cluster class such as FailoverClusterInvoker Will be multiple Invoker Put it all together , ...

  6. 【Dubbo Source code learning 】 Load balancing algorithm (2)- Implementation of polling algorithm

    @Overrideprotected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL ur ...

  7. 【Dubbo Source code learning 】 Load balancing algorithm (1)- Random algorithm

    /** * random load balance. * */public class RandomLoadBalance extends AbstractLoadBalance { public s ...

  8. SpringCloud Learning Series II ----- Serving consumers (Feign) And load balancing (Ribbon) The use of,

    Preface This article mainly introduces SpringCloud Service consumers in (Feign) And load balancing (Ribbon) The implementation and use of functions Feign combination Ribbon Load balancing . SpringCloud Feign Fei ...

  9. SpringCloud First experience : 3、 ... and 、Feign Call between services (FeignClient)、 Load balancing (Ribbon)、 Fault tolerance / Degraded processing (Hystrix)

    FeignOpenFeign Feign It's declarative . templated HTTP client . After reading the explanation , It can be understood that he is a kind of client Configuration implementation strategy , It implements Call between services (FeignClient). Load balancing (Rib ...

  10. springcloud The source code parsing ( Catalog )

    springcloud It's based on springboot A one-stop enterprise level distributed application development framework of .springboot It provides the convenience of creating a single project ,springcloud Combining the existing . Common solutions for distributed projects ...

Random recommendation

  1. Sqlserver_In、exists Use

    in It's the exterior and the interior hash Connect , and exists It's external work loop loop , Every time loop Loop and query the inner table . It has always been thought that exists Than in The statement of high efficiency is not accurate . If the two tables in the query are the same size , ...

  2. Qt How to remove the dashed box of the control such as button ( Focus box )( The two methods )

    Method 1: It can be done through code ui->pushButton->setFocusPolicy(Qt::NoFocus) Or in the Qt Creator Set... In the property list of . Method 2: If you need to press a key in an embedded device ...

  3. Android APN To configure

    APN Concept APN(Access Point Name), namely “ Access point name ”, Used to identify GPRS Business type of , At present, it is divided into two categories :CMWAP( adopt GPRS visit WAP Business ).CMNET( except WAP Other services are currently ...

  4. Webservice-WSDL Detailed explanation ( 3、 ... and )

    How to introduce WS The function of ? We usually write interface documents , Or tell the user orally . There are problems with all these ways : One of them I said in the last article , The client can't use the server interface directly : Second, the programmer is in front of the computer , Want to use WS when , Their tools ( Such as Ec ...

  5. centos mount ntfs Format of mobile hard disk

    After searching for information, we found that ,linux It can also support ntfs Format partitioned , Just need to install ntfs-3g plug-in unit . CentOS mount ntfs How to move the hard disk : 1 install fuse. download fuse-2.9.3.tar.gz   ...

  6. JDK Source code —— The singleton pattern

    JDK The application of singleton mode in source code 1.Runtime class Runtime Class encapsulation Java Runtime environment . every last java The program actually starts a JVM process , Then each JVM Processes are all corresponding to this one Runtime example , This is true ...

  7. C# Subclasses and superclasses

    When instantiating subclasses , Always call the parent class's parameterless constructor first

  8. The architect has to understand DNS【 turn 】

    DNS, Full name Domain Name System, Domain name system , Make clear , It is not DNF Dungeons and warriors . DNS How did you get it , We know that resources to access a server can be accessed through IP Form access , but IP It's hard to remember the address , It's not easy to read ...

  9. Crack and use SMB Agreed Windows User password :acccheck

    One . working principle Acccheck It's for Microsoft SMB Protocol detection tools ( Dictionaries crack user names and passwords ), It doesn't have the ability to exploit vulnerabilities . SMB agreement :SMB(Server Message Block) Communication protocol is mainly used as ...

  10. Memory and cpu

    http://www.blogjava.net/fjzag/articles/317773.html ubuntu@ubuntu-vm:/work/sv-g5-application/projects ...