Spring Boot微服务Eureka +功能区
在文章Spring Boot Microservices示例中,我们看到了一个Spring Boot Microservice示例,其中我们使用Eureka进行服务注册和发现。在本文中,我们将进一步扩展该示例,以了解如何将Eureka和Ribbon负载平衡器与Spring Boot一起使用,以在微服务实例之间加载平衡。
在该示例中,有两个单独的服务用户和帐户已在Eureka中注册。从"用户服务"中调用了"帐户服务"以获取一些数据。在本文中,我们将介绍如何配置多个帐户服务实例,以及如何使用功能区负载平衡器在这些帐户服务实例之间路由呼叫。
负载均衡
简而言之,负载均衡意味着将负载分配到多个资源上,而不是将所有负载都放在一个资源上。由于有更多资源可分担负载,因此有助于提高吞吐量;由于有更多冗余资源来处理请求(即使任何一个资源出现故障,因此也不会出现单点故障),因此有助于提高可靠性。
在Eureka中使用功能区负载平衡
众所周知,Eureka用于服务注册和发现,而Ribbon是客户端负载平衡器。在这里,我将尝试解释这两种工具如何协同工作。
Eureka服务器通过向Eureka服务器注册每个微服务来维护服务注册表。当服务间通信发生时,调用服务使用DiscoveryClient询问服务注册表,并返回被调用微服务的所有实例。现在的问题是,在所有返回的实例中,要调用哪个?
就是功能区之类的客户端负载平衡器出现在图片中的地方。客户端负载平衡器使用一种算法,例如循环轮询(依次调用每个实例)或者使用区域信息(将服务器与客户端定位在同一区域中)来获取必须调用的服务实例。
带功能区的Spring Boot Micro Service示例
在Spring Boot Microservices示例中,我们已经看到了如何通过启用发现客户端(使用@SpringCloudApplication注释)来配置Eureka服务器以及如何向其注册微服务用户和帐户。
一个问题是从用户调用帐户服务时使用的URL,该URL对主机和端口进行硬编码。这意味着即使我们创建更多的Account实例,每次都会调用相同的服务。
List<Account> accounts = new RestTemplate().exchange( "http://localhost:9000/accounts/{empId}", HttpMethod.GET, null, new ParameterizedTypeReference<List<Account>>(){}, id).getBody();
我们想要的是一种抽象,以便在运行时解析正确的主机和端口,这就是我们将尝试使用Netflix的Ribbon负载平衡服务进行配置的方式。要启用功能区,我们需要在pom.xml中添加以下依赖项
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency>
尽管在为eureka客户端添加依赖项时会自动添加此依赖项。因此,这种依赖性就足够了。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
创建另一个帐户微服务实例
现在,我们要扩大服务范围,并需要多个帐户副本。我们已经为application.properties中的以下内容创建了一个Spring Boot项目。
eureka.client.service-url.default-zone=http://localhost:8761/eureka server.port=9000 spring.application.name=account
启动该实例后,我们可以将端口更改为9001并再次启动"帐户"应用程序。这样,我们将拥有两个实例,一个实例在端口9000上侦听,另一个实例在端口9001上侦听。
我们还可以创建一个单独的Spring Boot项目,从Account中复制文件,并将以下内容粘贴到application.properties中
eureka.client.service-url.default-zone=http://localhost:8761/eureka server.port=9001 spring.application.name=account
无论哪种方式,我们都将运行同一服务的两个实例。我们可以通过访问URL在Eureka Server中进行验证– http:// localhost:8761 /
创建了解LoadBalanced的RestTemplate
由于我们使用RestTemplate的实例来调用另一个Service,因此我们可以使RestTemplate bean知道负载平衡。这可以使用@LoadBalanced注释完成,该注释指示Netflix Ribbon用负载平衡建议包装此RestTemplate bean。
如下所示在SpringBootUser项目中创建Config.java类。
import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class Config { @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } }
注入负载均衡的RestTemplate
现在,我们可以将此负载平衡的RestTemplate注入到UserController中。然后,在URL中,可以使用用于向Eureka注册该服务的服务的逻辑名。这就是我们使用此URL http:// ACCOUNT / accounts / {empId}来访问Account MicroService实例的方式。
@RestController public class UserController { @Autowired private RestTemplate restTemplate; @GetMapping(value="/{id}") public List<Account> showEmployees(@PathVariable("id") String id) { // Calling two times to demonstrate load balancing List<Account> accounts = restTemplate.exchange( "http://ACCOUNT/accounts/{empId}", HttpMethod.GET, null, new ParameterizedTypeReference<List<Account>>(){}, id).getBody(); accounts = restTemplate.exchange( "http://ACCOUNT/accounts/{empId}", HttpMethod.GET, null, new ParameterizedTypeReference<List<Account>>(){}, id).getBody(); for(Account acct : accounts) { System.out.println(acct.getEmpId()); System.out.println(acct.getAccountId()); System.out.println(acct.getBranch()); } return accounts; } }
测试应用程序
更改完成后,启动所有应用程序,即Eureka Server,User MicroService和Account MicroService的两个实例。
在SpringBootUser应用程序的控制台中,我们可以看到负载均衡器已经识别出服务器列表,并且其中之一将被调用以服务请求。
2020-04-25 17:02:06.405 INFO 9908 --- [nio-8080-exec-1] c.netflix.config.ChainedDynamicProperty : Flipping property: ACCOUNT.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647 2020-04-25 17:02:06.573 INFO 9908 --- [nio-8080-exec-1] c.n.u.concurrent.ShutdownEnabledTimer : Shutdown hook installed for: NFLoadBalancer-PingTimer-ACCOUNT 2020-04-25 17:02:06.575 INFO 9908 --- [nio-8080-exec-1] c.netflix.loadbalancer.BaseLoadBalancer : Client: ACCOUNT instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=ACCOUNT,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null 2020-04-25 17:02:06.637 INFO 9908 --- [nio-8080-exec-1] c.n.l.DynamicServerListLoadBalancer : Using serverListUpdater PollingServerListUpdater 2020-04-25 17:02:06.905 INFO 9908 --- [nio-8080-exec-1] c.netflix.config.ChainedDynamicProperty : Flipping property: ACCOUNT.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647 2020-04-25 17:02:06.923 INFO 9908 --- [nio-8080-exec-1] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client ACCOUNT initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=ACCOUNT,current list of Servers=[user:9000, user:9001],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:2; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;] },Server stats: [[Server:user:9000; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 05:30:00 IST 1970; First connection made: Thu Jan 01 05:30:00 IST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0] , [Server:user:9001; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 05:30:00 IST 1970; First connection made: Thu Jan 01 05:30:00 IST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0] ]}ServerList:org.springfHyman@theitroad7ef76128
访问服务
我们可以通过访问URLhttp:// localhost:8080/1来启动服务
尝试在不同的浏览器选项卡中访问相同的URL,我们可以看到请求在两个Account Service实例之间被划分。