Spring Boot微服务Eureka +功能区

时间:2020-01-09 10:44:31  来源:igfitidea点击:

在文章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实例之间被划分。