咱们继续以以前博客的代码为基础,增长Ribbon组件来提供客户端负载均衡。负载均衡是实现高并发、高性能、可伸缩服务的重要组成部分,它能够把请求分散到一个集群中不一样的服务器中,以减轻每一个服务器的负担。客户端负载均衡是运行在客户端程序中的,如咱们的web项目,而后经过获取集群的IP地址列表,随机选择一个server发送请求。相对于服务端负载均衡来讲,它不须要消耗服务器的资源。git
Gitee码云web
咱们此次须要在本地启动两个产品服务程序,用来验证负载均衡,因此须要为第二个程序提供不一样的端口。Spring Cloud配置服务中心的配置默认会覆盖本地系统环境变量,而咱们须要经过系统环境变量来设置产品服务的端口,因此须要在配置中心git仓库中修改产品服务的配置文件product-service.yml
spring
server: port: 8081 spring: cloud: config: allow-override: true override-system-properties: false
allow-override
的默认值即为true,写出它来是想做说明,它的意思是容许远程配置中心的配置项覆盖本地的配置,并非说容许本地的配置去覆盖远程的配置。固然咱们能够把它设置成false,可是为了提供更精确的覆盖规则,这里保留了默认值。 咱们添加了override-system-properties=false
,即虽然远程配置中心的配置文件能够覆盖本地的配置,可是不要覆盖本地系统变量。修改完成后提交到git仓库。shell
另外,在productService
项目的ProductController
中添加一些log,用来验证负载均衡是否生效:编程
package cn.zxuqian.controllers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ProductController { private static Logger log = LoggerFactory.getLogger(ProductController.class); @RequestMapping("/products") public String productList() { log.info("Access to /products endpoint"); return "外套,夹克,毛衣,T恤"; } }
首先在pom.xml
中添加Ribbon的依赖:服务器
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency>
而后修改Application
类,添加以下代码:并发
@EnableCircuitBreaker @EnableDiscoveryClient @RibbonClient(name = "product-service") @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Bean @LoadBalanced public RestTemplate rest(RestTemplateBuilder builder) { return builder.build(); } }
这里用到了@RibbonClient(name = "product-service")
注解,用来标记此项目为Ribbon负载均衡的客户端,它须要选择产品服务集群中其中的一台来访问所须要的服务,这里的name
属性对应于productService项目中配置的spring.application.name
属性。 @LoadBalanced
注解标明了RestTemplate
会被配置为自动使用Ribbon的LoadBalancerClient
来选择服务的uri并发送请求。app
在咱们在ProductService
类中添加以下代码:负载均衡
@Service public class ProductService { private final RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; public ProductService(RestTemplate restTemplate) { this.restTemplate = restTemplate; } @HystrixCommand(fallbackMethod = "backupProductList") public String productList() { List<ServiceInstance> instances = this.discoveryClient.getInstances("product-service"); if(instances != null && instances.size() > 0) { return this.restTemplate.getForObject(instances.get(0).getUri() + "/products", String.class); } return ""; } public String backupProductList() { return "夹克,毛衣"; } public String productListLoadBalanced() { return this.restTemplate.getForObject("http://product-service/products", String.class); } }
这里新添加了一个productListLoadBalanced
方法,跟以前的productList
方法访问的是同一服务,只不过是用Ribbon Client去作了负载均衡,这里的uri的host变成了product-service
即要访问的服务的名字,跟@RibbonClient
中配置的name
属性保持一致。最后在咱们的ProductController
中添加下面的代码:ide
@RestController public class ProductController { @Autowired private ProductService productService; @RequestMapping("/products") public String productList() { return productService.productList(); } @RequestMapping("/productslb") public String productListLoadBalanced() { return productService.productListLoadBalanced(); } }
来建立一个专门处理/productslb
请求的方法,调用productServie
提供负载均衡的方法。
到这里咱们的代码就完成了,代码看似简单,实际上是全部的配置都使用了默认值。Ribbon提供了编程式和配置式两种方式来配置Ribbon Client。现简单介绍下,后续深刻Ribbon时再和你们一块儿看看如何修改它的配置。Ribbon提供以下配置(左边是接口,右边是默认实现):
IClientConfig
ribbonClientConfig: DefaultClientConfigImplIRule ribbonRule
: ZoneAvoidanceRuleIPing ribbonPing
: DummyPingServerList<Server>
ribbonServerList: ConfigurationBasedServerListServerListFilter<Server>
ribbonServerListFilter: ZonePreferenceServerListFilterILoadBalancer
ribbonLoadBalancer: ZoneAwareLoadBalancerServerListUpdater
ribbonServerListUpdater: PollingServerListUpdater由于咱们这个项目用了Eureka,因此有些配置项和默认实现有所不一样,如Eureka使用DiscoveryEnabledNIWSServerList
取代ribbonServerList
来获取在Eureka上注册的服务的列表。下边有一个简单的Congiguration类,来自Spring官网:
public class SayHelloConfiguration { @Autowired IClientConfig ribbonClientConfig; @Bean public IPing ribbonPing(IClientConfig config) { return new PingUrl(); } @Bean public IRule ribbonRule(IClientConfig config) { return new AvailabilityFilteringRule(); } }
Ribbon默认不会发送Ping检查server的健康状态,默认均正常,而后IRune默认实现为ZoneAvoidanceRule
用来避免AWS EC2问题较多的zone,这在本地测试环境来讲是用不到的,而后替换成了AvailabilityFilteringRule
,这个能够开启Ribbon自带的断路器功能,来过滤不正常工做的服务器。
首先启动咱们的configserver
配置中心服务,而后启动registry
Eureka注册与发现服务,而后启动两个productService
,第一个咱们能够正常使用spring-boot:run
插件来启动,第二个咱们须要给它提供一个新的端口,能够用以下命令启动:
$ SERVER_PORT=8082 mvn spring-boot:run
最后启动咱们的web
客户端项目,访问http://localhost:8080/productslb
,而后刷新几回,会看到运行着productService
的两个命令行窗口会随机出现咱们的log:
Access to /products endpoint
欢迎访问个人博客张旭乾的博客