提示:以下是本篇文章正文內(nèi)容,SpringCloud 系列學習將會持續(xù)更新
LoadBalancer 負載均衡
前面我們講解了如何對服務進行拆分、如何通過 Eureka 服務器進行服務注冊與發(fā)現(xiàn),那么現(xiàn)在我們來看看,它的負載均衡到底是如何實現(xiàn)的,實際上之前演示的負載均衡是依靠 LoadBalancer 實現(xiàn)的。
在2020年前的 SpringCloud 版本是采用 Ribbon 作為負載均衡實現(xiàn),但是2020年的版本之后SpringCloud 把 Ribbon 移除了,進而用自己編寫的 LoadBalancer 替代。
那么,負載均衡是如何進行的呢?
一、@LoadBalanced 負載均衡
我們之前注冊 RestTemplate 時,就用 @LoadBalanced
注解進行了修飾:
@Configuration
public class BeanConfiguration {
@Bean
@LoadBalanced // 負載均衡
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
// User user = this.template.getForObject("http://userservice/user/"+uid, User.class);
??①觀察負載均衡現(xiàn)象
a. 我們有2個 UserApplication 的實例,可以在控制層的某個服務調(diào)用代碼中添加日志打?。?code>當前實例的IP:PORT
@RestController
@Slf4j
public class UserController {
@Resource
private UserService userService;
@Resource
Environment environment; // org.springframework.core.env.Environment
@GetMapping("/user/{uid}")
public User findUserById(@PathVariable("uid") int uid) throws UnknownHostException {
String hostIp = InetAddress.getLocalHost().getHostAddress();
String port = environment.getProperty("server.port");
log.info(hostIp + ":" + port + " 的findUserById()被訪問了!");
return userService.getUserById(uid);
}
}
b. 然后重新啟動。我們多次訪問http://localhost:8082/borrow/3
,其中 BorrowService 就會進行遠程調(diào)用 UserService,這時我們通過查看 UserApplication-1 和 UserApplication-2 的控制臺日志,就可以發(fā)現(xiàn)它們是被輪循調(diào)用的。
這樣,服務自動發(fā)現(xiàn)以及簡單的負載均衡就實現(xiàn)完成了,并且,如果某個微服務掛掉了,只要存在其他同樣的微服務實例在運行,那么就不會導致整個微服務不可用,極大地保證了安全性。
??②@LoadBalanced 源碼剖析
實際上,在添加 @LoadBalanced 注解之后,會啟用LoadBalancerInterceptor
攔截器對我們發(fā)起的服務調(diào)用請求進行攔截(只針對我們發(fā)起的請求)。
它實現(xiàn)ClientHttpRequestInterceptor
接口:
@FunctionalInterface
public interface ClientHttpRequestInterceptor {
ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException;
}
LoadBalancerInterceptor
類對 intercept()方法 的實現(xiàn):
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
URI originalUri = request.getURI();
String 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));
}
我們可以打個斷點看看實際是怎么在執(zhí)行的,可以看到:
服務端會在發(fā)起請求時執(zhí)行這些攔截器。它們會去找 Eureka 獲取真正需要訪問的主機名稱。
我們來看看BlockingLoadBalancerClient
類對 loadBalancer.execute()方法 的具體實現(xiàn):
//從上面給進來了服務的名稱和具體的請求實體
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
String hint = this.getHint(serviceId);
LoadBalancerRequestAdapter<T, DefaultRequestContext> lbRequest = new LoadBalancerRequestAdapter(request, new DefaultRequestContext(request, hint));
Set<LoadBalancerLifecycle> supportedLifecycleProcessors = this.getSupportedLifecycleProcessors(serviceId);
supportedLifecycleProcessors.forEach((lifecycle) -> {
lifecycle.onStart(lbRequest);
});
//可以看到在這里會調(diào)用choose方法自動獲取對應的服務實例信息
ServiceInstance serviceInstance = this.choose(serviceId, lbRequest);
if (serviceInstance == null) {
supportedLifecycleProcessors.forEach((lifecycle) -> {
lifecycle.onComplete(new CompletionContext(Status.DISCARD, lbRequest, new EmptyResponse()));
});
//沒有發(fā)現(xiàn)任何此服務的實例就拋異常(之前的測試中可能已經(jīng)遇到了)
throw new IllegalStateException("No instances available for " + serviceId);
} else {
//成功獲取到對應服務的實例,這時就可以發(fā)起HTTP請求獲取信息了
return this.execute(serviceId, serviceInstance, lbRequest);
}
}
所以,實際上在進行負載均衡的時候,會向 Eureka 發(fā)起請求,選擇一個可用的對應服務,然后會返回此服務的主機地址等信息:
回到目錄…文章來源:http://www.zghlxwxcb.cn/news/detail-699614.html
二、自定義負載均衡
LoadBalancer 默認提供了兩種負載均衡策略:
-
RandomLoadBalancer
- 隨機分配策略 -
RoundRobinLoadBalancer
- (默認)輪詢分配策略
現(xiàn)在我們希望修改默認的負載均衡策略,可以進行指定,比如我們現(xiàn)在希望用戶服務采用隨機分配策略。
①我們需要先創(chuàng)建隨機分配策略的配置類(不用加@Configuration):
public class LoadBalancerConfig {
//將官方提供的 RandomLoadBalancer 注冊為Bean
@Bean
public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory){
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}
②接著我們需要為對應的服務指定負載均衡策略,直接使用注解即可:
@Configuration
// 指定只要是 userservice 服務,都會使用我們指定的策略 LoadBalancerConfig
@LoadBalancerClient(value = "userservice", configuration = LoadBalancerConfig.class)
public class BeanConfiguration {
@Bean
@LoadBalanced // 負載均衡
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
接著我們在BlockingLoadBalancerClient
中添加斷點,觀察是否采用我們指定的策略進行請求:
發(fā)現(xiàn)訪問 userservice 服務的策略已經(jīng)更改為我們指定的策略了。
回到目錄…
三、OpenFeign 實現(xiàn)負載均衡
官方文檔:https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/
- Feign 是?個聲明式的 HTTP 客戶端組件,它旨在是編寫 Http 客戶端變得更加容易。
- OpenFeign 同時集成了
Spring Cloud LoadBalancer
和Spring Cloud CircuitBreaker
,提供負載均衡和熔斷降級的功能。 - Feign 默認的負載均衡策略是輪詢調(diào)用。
??①添加依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
??②啟動類添加 @EnableFeignClients
@SpringBootApplication
@EnableFeignClients
public class BorrowApplication {
public static void main(String[] args) {
SpringApplication.run(BorrowApplication.class, args);
}
}
那么現(xiàn)在我們需要調(diào)用其他微服務提供的接口,該怎么做呢?
??③創(chuàng)建客戶端接口 UserClient
我們的客戶端接口需要用 @FeignClient
注解來指定向哪個微服務發(fā)送 HTTP 請求。
@FeignClient("userservice") //聲明為 userservice 服務的 HTTP 請求客戶端
public interface UserClient {
}
我們之前的遠程調(diào)用:
RestTemplate template = new RestTemplate();
User user = template.getForObject("http://userservice/user/"+uid, User.class);
現(xiàn)在直接在客戶端接口中寫入控制層的方法:
@FeignClient("userservice")
public interface UserClient {
//路徑保證和其他微服務提供的一致即可
@RequestMapping("/user/{uid}")
User getUserById(@PathVariable("uid") int uid); //參數(shù)和返回值也保持一致
}
??④service業(yè)務中調(diào)用客戶端接口
我們直接注入使用(有 Mybatis 那味了):
@Service
public class BorrowServiceImpl implements BorrowService {
@Resource
private BorrowMapper borrowMapper;
@Resource
private UserClient userClient;
@Resource
private BookClient bookClient;
@Override
public UserBorrowView getBorrowViewByUid(int uid) {
// 現(xiàn)在拿到借閱關(guān)聯(lián)信息了,怎么調(diào)用其他服務獲取信息呢?
List<Borrow> borrowList = borrowMapper.getBorrowsByUid(uid);
// 直接調(diào)用客戶端接口的方法
User user = userClient.findUserById(uid);
List<Book> bookList = borrowList
.stream()
.map(b -> bookClient.findBookById(b.getBid()))
.collect(Collectors.toList());
return new UserBorrowView(user, bookList);
}
}
繼續(xù)訪問進行測試:
OK,正常。
當然,F(xiàn)eign 也有很多的其他配置選項,這里就不多做介紹了,詳細請查閱官方文檔。
回到目錄…
總結(jié):
提示:這里對文章進行總結(jié):
本文是對SpringCloud的學習, 了解了LoadBalancer 負載均衡策略的內(nèi)部流程,學習了如何自定義負載均衡策略,并且學習了使用OpenFeign實現(xiàn)負載均衡。之后的學習內(nèi)容將持續(xù)更新?。?!文章來源地址http://www.zghlxwxcb.cn/news/detail-699614.html
到了這里,關(guān)于SpringCloud之 LoadBalancer和Feign負載均衡的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!