四、Ribbon 客户端负载均衡

5 篇文章 0 订阅
4 篇文章 0 订阅

4、Ribbon 客户端负载均衡

4.1 配置Ribbon

微服务的注册与发现搭建好后,现在框架面临着另一个问题。在生产环境中,一个服务肯定有多个实例,那多个实例如何访问呢?如何将消费者的请求分摊到各个实例中呢? Ribbon 正好解决了此问题。 Ribbon是Netflix发布的负载均衡器,他有助于控制HTTP 与TCP客户端的行为。为Ribbon 提供服务地址列表和,ribbon 就可以基于某种负载均衡算法,自动的帮助服务调用者去请求。 之前的创建APP-8801作为服务的提供者。我现在创建服务的消费者APP2-8901, 向pom.xml引入:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>

其实,无需引入, 我们使用 mvn:dependency:tree 命令查看依赖树 可以发现Eureka中已经包含ribbon。

其中application.properties 配置如下:

server.port=8901
spring.application.name=app2-8901 #用于指定注册到EurekaServer上的应用名称
eureka.client.service-url.defalutZone= http://localhost:8761/eureka/ #EurekaServer服务地址
eureka.instance.prefer-ip-address=true #表示将自己的IP注册到EurekaServer 上,如果不配置或者将其设置为false,则表示将微服务所在系统的hostname注册到EurekaServer上

Ribbon 配置

ribbon.ConnectionTimeOut=600 #全局修改ribbon的客户端调用超时时间
ribbon.ReadTimeOut=5000 #请求处理的超时时间

指定服务配置,ribbon 负载rule 不同应用对应不同的负载策略 格式为:服务名称.ribbon.NFLoadBalancerRuleClassName=策略 app-8801应用的负载rule

app-8801.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
app-8801.ribbon.ConnectionTimeOut=500
app-8801.ribbon.ReadTimeOut=5000

Ribbon重试配置 开启重试机制,默认为关闭

spring.cloud.loadbalancer.retry.enabled=true
对所有操作请求都进行重试
app-8801.ribbon.OkToRetryOnAllOperations=true

切换实例的重试次数

app-8801.ribbon.MaxAutoRetriesNextServer=2

对当前实例的重试次数

app-8801.ribbon.maxAutoRetries=1

上述重试配置的意思:当访问到故障请求的时候,它会尝试再访问一次当前实例(次数由maxAutoRetries确定),如果不行,则切换一个实例访问 如果还是不行,在切换一个实例访问(切换次数由MaxAutoRetriesNextServer确定)如果依然不行,则访问失败。

4.2 具体实现负载均衡

在 启动类 App2Application 中添加一个RestTemplate对象。该对象会使用Ribbon的自动化配置,同时通过配置@LoadBalanced,能够开启对客户端请求的负载均衡。

@SpringBootApplication
@EnableDiscoveryClient
public class App2Application {



    /**
     * 可以使用一个pojo自定义Ribbon的配置。这种配置是细粒度的,不同的Ribbon客户的可以使用不同的配置。
     * 第二种是使用属性自定义Ribbon配置,见properties
     * @return
     */
    @Bean
    @LoadBalanced//负载均衡
    public RestTemplate restTemplate()
    {
        return new RestTemplate();
    }

    public static void main(String[] args) {

        SpringApplication.run(App2Application.class, args);
    }
}

现在我们就可以在controller 中通过restTemplate去调用服务。下面根据每种调用方法和入参的不同分别列举如下调用方式(FeignServiceImpl类):

public Domain getDomain11(HttpServletRequest request,String id)
{
    ServiceCallLog log=new ServiceCallLog(appName,new Date(),request.getHeader(Span.TRACE_ID_NAME),  request.getHeader(Span.SPAN_ID_NAME), request.getHeader(Span.PARENT_ID_NAME), request.getHeader(Span.SPAN_NAME_NAME));
    LOGGER.info(JSONObject.toJSONString(log));
    SERVICECAllLOGGER.info(JSONObject.toJSONString(log));
    return restTemplate.getForObject("http://app-8801/domain/get001/"+id,Domain.class);
}


/**
 * getForEntity方式一
 * @param userName
 * @param pwd
 * @param address
 * @return
 */
public Domain getDomain12(String userName, String pwd, String address)
{
    String url="http://app-8801/domain/get003?userName={0}&pwd={1}&address={2}";
    ResponseEntity<Domain> responseEntity=restTemplate.getForEntity(url,Domain.class,userName,pwd,address);
    return responseEntity.getBody();
}
/**
 * getForObject方式一
 * @param userName
 * @param pwd
 * @param address
 * @return
 */
public Domain getDomain13(String userName, String pwd, String address)
{
    String url="http://app-8801/domain/get003?userName={0}&pwd={1}&address={2}";
    Domain domain=restTemplate.getForObject(url,Domain.class,userName,pwd,address);
    return domain;
}

/**
 * getForEntity方式二
 * @param userName
 * @param pwd
 * @param address
 * @return
 */
public Domain getDomain14(String userName, String pwd, String address)
{
    String url="http://app-8801/domain/get003?userName={username}&pwd={pwd}&address={address}";
    Map<String,String> map=new HashMap<>();
    map.put("username",userName);
    map.put("pwd",pwd);
    map.put("address",address);
    ResponseEntity<Domain>  responseEntity=restTemplate.getForEntity(url,Domain.class,userName,pwd,map);
    return responseEntity.getBody();
}

/**
 * getForObject方式二
 * @param userName
 * @param pwd
 * @param address
 * @return
 */
public Domain getDomain15(String userName, String pwd, String address)
{
    String url="http://app-8801/domain/get003?userName={username}&pwd={pwd}&address={address}";
    Map<String,String> map=new HashMap<>();
    map.put("username",userName);
    map.put("pwd",pwd);
    map.put("address",address);
    Domain domain=restTemplate.getForObject(url,Domain.class,userName,pwd,map);
    return domain;
}

/**
 * post 方式一
 * @param domain
 * @return
 */
public Domain postDomain011(@RequestBody Domain domain){
    ResponseEntity<Domain>  responseEntity=restTemplate.postForEntity("http://app-8801/domain/post001",domain,Domain.class);
    return responseEntity.getBody();
}

/**
 * post 方式二
 * @param domain
 * @return
 */
public Domain postDomain012(@RequestBody Domain domain){
    return restTemplate.postForObject("http://app-8801/domain/post001",domain,Domain.class);
}

通过 DemoController 调用FeignServiceImpl里面的方法。 调用服务: 

app-8801 实例1:

app-8801 实例2:

我们会发现 服务名为app-8801 两服务端会随机打印相应的请求信息, 说明我们已经成功实现了请求的负载均衡。

4.3 基于java自定义实现Ribbon配置

有时候我们需要自定义Ribbon的配置,例如修改Ribbon负载均衡的规则(上一节中我们已经通过在application.properties 中去配置对某个服务的负载均衡的规则)

定义Ribbon配置类

/**
 * @Author: pangfei
 * @description: Ribbon 负载均衡的java实现
 * @Date: Create in 9:52 2018/1/21
 */
@Configuration
public class RibbonConfig {
    /**
     * 通过java实现以服务提供者的综合性能的计算去选择服务节点,实现负载均衡
     * 注意:该类不应该在主应用程序上下午的@ComponentScan扫描的范围之内
     * @param config
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public IRule ribbonRule(IClientConfig config)
    {
        ZoneAvoidanceRule rule=new ZoneAvoidanceRule();
        rule.initWithNiwsConfig(config);
        return  rule;
    }
}

指定服务与java配置

再次创建一个配置类 用于指点服务与ribbon java 配置:

/**
 * @Author: pangfei
 * @description: 使用@RibbonClient 实现对特定的服务(name)自定义配置
 * configuration 指定了Ribbon的配置类
 * @Date: Create in 9:58 2018/1/21
 */
@Configuration
@RibbonClient(name = "app-8801",configuration = RibbonConfig.class)
public class TestConfiguration {
}

此时重启项目,会发现,请求会随机分配到服务名称为app-8801的两个实例上面。

注意: RibbonConfig配置类不能被扫描到主应用程序上下文中,所以应该在@ComponentScan 中不扫描RibbonConfig

@ComponentScan( excludeFilters={@ComponentScan.Filter(type= FilterType.ASSIGNABLE_TYPE,value=com.example.app2.RibbonConfig.RibbonConfig.class)})

4.4 如何脱离Eureka使用Ribbon

前面所有的实例都是基于Eureka 与Ribbon的,在现实环境中,并不是所有的服务都会注册到Eureka 的,所以我们应该提供一下,脱离Eureka 是如何使用Ribbon的方法。 在之前的实例中,Eureka 依赖中 包含了Ribbon ,所以现在我们需要把:

<!--Eureka服务注册,其中包含了负载均衡器ribbon-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>

替换成:

 <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-ribbon</artifactId>
    </dependency>

然后修改application.properties

server.port=8901
spring.application.name=app2-8901 #用于指定注册到EurekaServer上的应用名称
app-8801.ribbon..listOfServers=localhost:8801,localhost:8802 #服务提供方实例列表

到此我们ribbon 负载均衡就结束了,下面我们将通过Feign实现声明试Rest调用。



所有代码地址:https://coding.net/u/pangfei/p/springcloud/git


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值