Spring Cloud 服务消费(Ribbon)


之前介绍了使用 Eureka 作为服务发现组件,构建了 Eureka Server 作为服务注册中心,使用 Eureka Client 去注册服务 Spring Cloud 服务注册与发现、高可用(Eureka),那服务间又是怎样相互调用的呢?这里介绍使用 Ribbon 实现负载均衡

准备

构建一个 Eureka Server 服务注册中心 eureka-server 和两个注册服务 product-service (用来提供服务) 和 order-service-ribbon (消费服务者) 参考,这里我们启动两个 product-service 实例

这里分别给出配置信息

spring:
  application:
    name: eureka-server
server:
  port: 8761
eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

我们启动两个 product-service 服务实例,在 idea 中的 Edit Configurations 中复制一个 product-service 改变端口为 8072

spring:
  application:
    name: product-service
server:
  port: 8071
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

spring-cloud-consumer-ribbon-1-1.jpg

order-service-ribbon 需要添加 Ribbon 的依赖,如下:

spring:
  application:
    name: order-service-ribbon
server:
  port: 8081
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

启动服务注册中心 eureka-server 和两个注册服务,打开 http://localhost:8761/eureka/ 查看服务注册情况

spring-cloud-consumer-ribbon-1-2.jpg

Ribbon 实现负载均衡

实际生产中,基本上每个服务都会部署多个实例,那么服务消费者的请求该怎么分配到多个服务提供者实例上呢?

Ribbon 简介

Ribbon 是 Netflix 发布的负载均衡器,是在客户端实现负载均衡,对客户端的HTTP和TCP行为有很好的控制。它可以在客户端为其配置服务提供者地址列表 ribbonServerList,然后基于某种负载均衡算法(轮询、随机等),自动帮助客户端去请求

当 Eureka 与 Ribbon 结合使用时,ribbonServerList 将被扩展为DiscoveryEnabledNIWSServerList,扩展为 Eureka 的服务器注册实例列表。同时还会用 NIWSDiscoveryPing 替换 IPing 接口,让 Eureka 来确定服务端是否启动

RestTemplate 作为负载均衡客户端

RestTemplate 可以自动配置为使用 ribbon,要创建一个负载均衡的 RestTemplate,需要加上注解 @LoadBalanced

@SpringBootApplication
@EnableDiscoveryClient
public class OrderServiceRibbonApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderServiceRibbonApplication.class, args);
    }

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

创建一个 Controller 来测试请求

@RestController
public class ProductController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/product/{id}")
    public Product getProduct(@PathVariable Long id){
        return restTemplate.getForObject("http://product-service/product/" + id, Product.class);
    }
}

重新启动服务,访问路径 http://localhost:8081/product/1 得到如下结果

{"id":1,"name":"秋裤","descriptionl":"花秋裤","price":9.9,"count":99}

有上面的代码我们可以看到请求的地址为 http://product-service/product/1,其中的 product-service 就是我们要请求的服务的虚拟主机名,Ribbon 会自动把这个虚拟主机名映射成要请求的服务网络地址

LoadBalancerClient

LoadBalancerClient 是 Spring Cloud Commons 中提供的一个抽象接口,可以使用它来调用 Ribbon API

@RestController
@Log4j2
public class ProductController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @GetMapping("/product/{id}")
    public Product getProduct(@PathVariable Long id){
        return restTemplate.getForObject("http://product-service/product/" + id, Product.class);
    }

    @GetMapping("/product/log/{id}")
    public void logProduct(@PathVariable Long id){
        ServiceInstance serviceInstance = loadBalancerClient.choose("product-service");
        // URI storesUri = URI.create(String.format("https://%s:%s/"+ id, serviceInstance.getHost(), serviceInstance.getPort()));
        log.info("{}:{}:{}", serviceInstance.getHost(), serviceInstance.getPort());
    }
}

现在我们重新启动服务,访问 http://localhost:8081/product/log/1 ,上面代码可以看到我们会打印查询日志,会记录下请求服务的实例id、主机名、端口,多次访问上面的地址

2019-04-30 09:19:01.437  INFO 22024 --- [nio-8081-exec-3] c.t.order.controller.ProductController   : DESKTOP-G11TC44.mshome.net:8071
2019-04-30 09:19:01.687  INFO 22024 --- [nio-8081-exec-4] c.t.order.controller.ProductController   : DESKTOP-G11TC44.mshome.net:8072
2019-04-30 09:19:01.843  INFO 22024 --- [nio-8081-exec-5] c.t.order.controller.ProductController   : DESKTOP-G11TC44.mshome.net:8071
2019-04-30 09:19:01.984  INFO 22024 --- [nio-8081-exec-6] c.t.order.controller.ProductController   : DESKTOP-G11TC44.mshome.net:8072
2019-04-30 09:19:02.140  INFO 22024 --- [nio-8081-exec-7] c.t.order.controller.ProductController   : DESKTOP-G11TC44.mshome.net:8071
2019-04-30 09:19:02.297  INFO 22024 --- [nio-8081-exec-8] c.t.order.controller.ProductController   : DESKTOP-G11TC44.mshome.net:8072

可以看到请求的各个服务节点依次交替,说明已经实现负载均衡

restTemplate.getForObject() 不能和 loadBalancerClient.choose() 同时使用,因为注释了 @LoadBalanced 的 restTemplate 实际上就是一个 Ribbon 客户端,包含了 choose 的功能

自定义 Ribbon 客户端

在某些场景下,可能会需要自定义 Ribbon 的配置,如修改 Ribbon 的负载均衡规则。可以使用 @RibbonClient 声明自定义的配置,或者使用 <clientName>.Ribbon.* 中的外部属性配置 Ribbon 客户端

代码自定义 Ribbon 客户端

默认情况下,Ribbon 客户端已经实现了以下的 bean (BeanType beanName:ClassName

  • IClientConfig ribbonClientConfig: DefaultClientConfigImpl
  • IRule ribbonRule: ZoneAvoidanceRule
  • IPing ribbonPing: DummyPing
  • ServerList<Server> ribbonServerList: ConfigurationBasedServerList
  • ServerListFilter<Server> ribbonServerListFilter: ZonePreferenceServerListFilter
  • ILoadBalancer ribbonLoadBalancer: ZoneAwareLoadBalancer
  • ServerListUpdater ribbonServerListUpdater: PollingServerListUpdater

可通过自定义配置覆盖默认配置

@Configuration
public class RibbonConfiguration {

    @Bean
    public IPing ribbonPing() {
        return new PingUrl();
    }

    @Bean
    public IRule ribbonRule() {
        // 负载均衡规则:随机
        return new RandomRule();
    }
}
@Configuration
@RibbonClient(name = "product-service", configuration = RibbonConfiguration.class)
public class TestConfiguration {
}

这种配置是细粒度的,不同的 Ribbon 客户端可以使用不同的配置,使用 @RibbonClient 的 configuration 属性,就可以自定义 指定名称的 Ribbon 客户端的配置

这里的 RibbonConfiguration 类不能包含在主应用程序的上下文中的 @ComponentScan 中,否则该类的配置会被所有的 @RibbonClient 共享。因此只想自定义某一个 Ribbon 客户端的配置,必须防止 @Configuration 的注解的类所在包和 @ComponentScan 扫描的包重合,或显示指定 @ComponentScan 不扫描 @Configuration 类所在包

为所有Ribbon客户端自定义默认值

通过使用 @RibbonClients 注释并注册一个默认配置,可以为所有 Ribbon 客户端提供一个默认配置

@RibbonClients(defaultConfiguration = DefaultRibbonConfig.class)
public class RibbonClientDefaultConfigurationTestsConfig {

	public static class BazServiceList extends ConfigurationBasedServerList {

		public BazServiceList(IClientConfig config) {
			super.initWithNiwsConfig(config);
		}

	}

}

@Configuration
class DefaultRibbonConfig {

	@Bean
	public IRule ribbonRule() {
		return new BestAvailableRule();
	}

	@Bean
	public IPing ribbonPing() {
		return new PingUrl();
	}

	@Bean
	public ServerList<Server> ribbonServerList(IClientConfig config) {
		return new RibbonClientDefaultConfigurationTestsConfig.BazServiceList(config);
	}

	@Bean
	public ServerListSubsetFilter serverListFilter() {
		ServerListSubsetFilter filter = new ServerListSubsetFilter();
		return filter;
	}
}

属性自定义 Ribbon 客户端

使用属性自定义 Ribbon 客户端会比代码自定义更加方便。属性配置的前缀为 <clientName>.Ribbon.*

  • NFLoadBalancerClassName: 实现 ILoadBalancer
  • NFLoadBalancerRuleClassName: 实现 IRule
  • NFLoadBalancerPingClassName: 实现 IPing
  • NIWSServerListClassName: 实现 ServerList
  • NIWSServerListFilterClassName: 实现 ServerListFilter

这些属性中定义的类优先于使用 @RibbonClient(configuration=MyRibbonConfig.class) 定义的 bean 和Spring Cloud Netflix 提供的默认值定义的 bean。

product-service:
  ribbon:
    NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule

参考代码:demo

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值