負載均衡
負載均衡的原理(通用)
@LoadBalanced注解
用來攔截它所標記的RestTemplate
發起的http請求, 底層是利用了一個名為Ribbon
的組件來實現負載均衡功能(Cloud高版本已經棄用)
LoadBalancerInterceptor的intercept方法
會對RestTemplate的請求進行攔截
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {private LoadBalancerClient loadBalancer;private LoadBalancerRequestFactory requestFactory;public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {this.loadBalancer = loadBalancer;this.requestFactory = requestFactory;}public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));}public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {// 獲取請求uri,如http://userservice/user/1URI originalUri = request.getURI()// 獲取uri路徑的主機名,其實就是服務名稱userserviceString serviceName = originalUri.getHost();Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri); // 處理服務名稱和用戶請求return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));}
}
RibbonLoadBalancerClient的execute方法
根據服務名稱獲取服務實例地址列表,隨后利用負載均衡算法得到一個真實的服務地址信息
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {// 根據服務名稱得到去Eureka服務端中獲取服務的實例地址列表并保存DynamicServerListLoadBalancer到的allServeList屬性當中ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);// 利用內置的負載均衡算法,從服務的實例地址列表中選擇一個實例地址Server server = this.getServer(loadBalancer, hint);if (server == null) {throw new IllegalStateException("No instances available for " + serviceId);} else {RibbonServer ribbonServer = new RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);}
}
負載均衡規則IRule
: RibbonLoadBalancerClient的getServer方法最終調用父類BaseLoadBalancer的chooseServer方法
public class BaseLoadBalancer extends AbstractLoadBalancer implements PrimeConnectionListener, IClientConfigAware {private static Logger logger = LoggerFactory.getLogger(BaseLoadBalancer.class);// IRule的實現類不同表示負載均衡的策略不同,RoundRobinRule表示輪循調度private static final IRule DEFAULT_RULE = new RoundRobinRule();private static final BaseLoadBalancer.SerialPingStrategy DEFAULT_PING_STRATEGY = new BaseLoadBalancer.SerialPingStrategy((SyntheticClass_1)null);private static final String DEFAULT_NAME = "default";private static final String PREFIX = "LoadBalancer_";protected IRule rule;protected IPingStrategy pingStrategy;protected IPing ping;public Server chooseServer(Object key) {if (this.counter == null) {this.counter = this.createCounter();}this.counter.increment();if (this.rule == null) {return null;} else {try {// 選出一個負載均衡的策略return this.rule.choose(key);} catch (Exception var3) {logger.warn("LoadBalancer [{}]: Error choosing server for key {}", new Object[]{this.name, key, var3});return null;}}}
}
負載均衡策略
負載均衡的規則都定義在IRule接口中,而IRule有很多不同的實現類,默認的實現就是ZoneAvoidanceRule表示區域內輪詢方案
自定義負載均衡規則
編程方式(全局)
:在order-service模塊
中啟動類中注冊一個IRule類型的Bean到Spring容器中,對order-service服務
想要調用的微服務均有效
- 優缺點: 配置靈活但修改時需要重新打包發布
@Bean
public IRule randomRule(){// 創建負載均衡的實現類對象return new RandomRule();
}
配置文件方式(局部)
:在order-service模塊
的配置文件中添加userservice.ribbon.NFLoadBalancerRuleClassName
配置,只針對調用的某個微服務有效
- 優缺點: 直觀方便無需重新打包發布,但是無法做全局配置,只能指定調用的某一個微服務的負載均衡規則
userservice: # 給user-service服務添加負載均衡規則ribbon:NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 負載均衡規則
饑餓加載(通用)
Ribbon默認是采用懶加載
,只有在用戶第一次訪問時才會創建LoadBalanceClient負載均衡類做服務拉取的動作
,這樣用戶第一次訪問時就大大增加了請求時間
采用饑餓加載
是在項目啟動時就創建LoadBalanceClient負載均衡類加載指定的服務
, 這樣用戶第一次訪問時就大大縮短了請求時間
在order-service模塊
的application.yml配置文件中添加新的配置屬性ribbon.eager-load.enabled/clients
ribbon:eager-load:enabled: true # 開啟饑餓加載# 指定對userservice單個服務進行饑餓加載clients: userservice # 指定對多個服務進行饑餓加載clients: - userservice- xxxservice