Ribbon本地负载均衡客户端及轮询算法

Spring Cloud Ribbon是基于Netflix Ribbon实现的一套 客户端 负载均衡的工具

提供客户端的软件负载均衡算法和服务调用(ribbon负载均衡+restTemplate)

LB负载均衡(LoadBalance):将用户的请求平摊的分配到多个服务器上,从而达到系统的HA(高可用)

常见的负载均衡软件:Nginx,LVS,硬件 F5等

Ribbon本地负载均衡客户端、Nginx服务端负载均衡区别

Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求。即负载均衡由服务端实现的

Ribbon本地负载均衡,在调用微服务接口的时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术

集中式LB:即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5,也可以是软件,如nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方

进程内LB:将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器Ribbon就属于进程内LB,它是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。

Ribbon:为客户端提供服务端的地址,然后由客户端自行选择服务端

    工作步骤分为两步:1、先选择EurekaServer(在注册中心查询可用服务列表并选择服务),优先选择在同一个区域内存负载较少的server。2、在根据用户指定的策略,再从server取到的服务注册列表中选择一个地址

不考虑eurekaserver集群的情况下,都为单机版,这时可以选择server,然后再从server的列表中选择服务payment

导入pom依赖

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
</dependency

为什么我们只配置了Eureka的依赖,而没有导入ribbon的依赖??

点击去发现,eureka集成了ribbon的依赖,所以即使我们没有单独的配置ribbon,它还是能实现负载均衡的

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-ribbon</artifactId>
<version>2.2.1.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>

RestTemplate

可在服务之间发送HTTP请求进行调用

它提供了常见的REST请求方案的模板,例如GET请求、POST请求、PUT请求、DELETE请求以及一些通用的请求执行方法exchange以及execute。

RestTemplate继承自InterceptingHttpAccessor并实现了RestOperations接口,其中RestOperations接口定义了基本的RESTful操作,这些操作再RestTemplate中都得到了实现。

public class UserHelloController {
    @Autowired
    DiscoveryClient discoveryClient;
    @Autowired
    RestTemplate restTemplate;

    @GetMapping("/hello")
    public String hello(String name) {
        // 获取服务集合
        List<ServiceInstance> list = discoveryClient.getInstances("provider");
        // 获取集合中第一个服务
        ServiceInstance instance = list.get(0);
        // 获取服务的主机名
        String host = instance.getHost();
        // 获取服务的端口号
        int port = instance.getPort();
        String url = "http://" + host + ":" + port + "/hello?name={1}";
        // getForEntity第一个参数是url,第二个参数:接口返回的数据类型;第三个参数:可变长度的参数,用来给占位符填值的
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class, name);
        // 一般多线程用StringBuffer
        StringBuffer sb = new StringBuffer();
        // 获取响应状态码
        HttpStatus statusCode = responseEntity.getStatusCode();
        // 获取响应数据
        String body = responseEntity.getBody();
        sb.append("statusCode:")
        .append(statusCode)
        .append("</br>")
        .append("body:")
        .append(body)
        .append("</br>");
            // 获取响应头
            HttpHeaders headers = responseEntity.getHeaders();
            Set<String> keySet = headers.keySet();
            for (String s : keySet) {
                sb.append(s)
                .append(":")
                .append(headers.get(s))
                .append("</br>");
            }
            return sb.toString();
        }
    }
}

getForObject的返回值就是服务提供者返回的数据,使用getForObject无法获取到响应头

postForEntity,参数的传递可以实key/value的形式,也可以是JSON数据

唯一的区别就是第二个参数的类型不同,这个参数如果是一个 MultiValueMap 的实例,则以 key/value 的形式发送,如果是一个普通对象,则会被转成 json 发送。

面试题:你除了用过轮询这样的负载均衡方法还用没用过其它的,如果你要是用ribbon的话,能不能换一种,负载算法或者是轮询算法,你有没有自己写过或换过,说说思路

IRule:根据特定算法中从服务列表中选取一个要访问的服务

 

负载规则的替换与操作
这个自定义配置类不能放在@ComponentScan所扫描的当前包以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制的目的了。()

再@ComponentScan不能扫描到的包中新建类并自定义负载规则

@Configuration
public class MySelfRule
{
    @Bean
    public IRule myRule()
    {
        return new RandomRule();//定义为随机
    }
}

然后在启动类设置注解,配置为自定义的规则类

@RibbonClient(name="CLOUD-PAYMENT-SERVICE",configuration=MySelfRule.class)

Ribbon默认负载均衡轮询算法原理

负载均衡算法:rest接口第几次请求数%服务器集群总数量=实际调用服务器位置下标,每次服务重启动后rest接口计数从1开始

List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");

如:List[0] instances =127.0.0.1:8001

        List[1] instances =127.0.0.1:8002

8001 + 8002 组合成为集群,它们共计2台机器,集群总数为2,按照轮询算法原理:

 

轮询算法:

首先写一个LoadBalancer接口,收集服务器上现在有多少台能够提供服务的机器,并放到List集合中
public interface LoadBalancer{
    ServiceInstance instances(List<ServiceInstance> serviceInstances);
}

然后实现上面的接口

@Component
public class MyLB implements LoadBalancer{
//AtomicInteger类,实现数据原子性更新
    private AtomicInteger atomicInteger = new AtomicInteger(0);
    
    public final int getAndIncrement(){
        //当前值
        int current;
        //要更新的值
        int next;
        //轮询算法
        do{
            current=this.atomicInteger.get();
            next=current >= 2147483647 ? 0 : current + 1;
        }while(!this.atomicInteger.compareAndSet(current,next));
        System.out.println("****next"+next);
        return next
    }
//负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务重启动后rest接口计数从1开始
    @Override
    public ServiceInstance instances(List<ServiceInstance> serviceInstances){
//获取调用的服务器下标
        int index = getAndIncrement() % serviceInstances.size();
//返回获取调用的服务器实例
        return serviceInstances.get(index);
    }

}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值