第一:其实就是请求的计数
第二:有几台服务,就用计数% 服务数。
第三:数组保存服务。数组的下标正好是数量-1; 取模的数 最大也正好是数量-1.正好对于应。
第四:用 arr【第二的结果】取到对应的服务。即可实现轮询。
轮询的源代码 用得就是以上的思路。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.netflix.loadbalancer;
import com.netflix.client.config.IClientConfig;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RoundRobinRule extends AbstractLoadBalancerRule {
private AtomicInteger nextServerCyclicCounter;
private static final boolean AVAILABLE_ONLY_SERVERS = true;
private static final boolean ALL_SERVERS = false;
private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class);
public RoundRobinRule() {
this.nextServerCyclicCounter = new AtomicInteger(0);
}
public RoundRobinRule(ILoadBalancer lb) {
this();
this.setLoadBalancer(lb);
}
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
log.warn("no load balancer");
return null;
} else {
Server server = null;
int count = 0;
while(true) {
if (server == null && count++ < 10) {
List<Server> reachableServers = lb.getReachableServers();//得到可以连接的server
List<Server> allServers = lb.getAllServers();//得到所有的server 并且放在list中了。
int upCount = reachableServers.size();//在注册中心连接的。得到数量
int serverCount = allServers.size();
if (upCount != 0 && serverCount != 0) {
int nextServerIndex = this.incrementAndGetModulo(serverCount);//关键在于这个方法的的next值。
server = (Server)allServers.get(nextServerIndex);
if (server == null) {
Thread.yield();
} else {
if (server.isAlive() && server.isReadyToServe()) {
return server;
}
server = null;
}
continue;
}
log.warn("No up servers available from load balancer: " + lb);
return null;
}
if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: " + lb);
}
return server;
}
}
}
private int incrementAndGetModulo(int modulo) {
int current;
int next;
do {
current = this.nextServerCyclicCounter.get();//这个是我们的atmic原子类。得到默认的值0
next = (current + 1) % modulo;//这个是服务的个数。这个next的值如果这一样取,其实就是和serverlist的下标对应,并且,每次加1.
} while(!this.nextServerCyclicCounter.compareAndSet(current, next));//因为是多线程,我们要保证安全,所以一用了循环的CAS
return next;
}
public Server choose(Object key) {
return this.choose(this.getLoadBalancer(), key);
}
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
自己模拟源码写一个相同的轮询
package home.lb;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Service
public class MyLb implements Lb {
AtomicInteger n = new AtomicInteger(0);
public final int getNext(int size){
int cur;
int next;
for(;;){
cur = n.get();
next = (cur+1)%size;
if(n.compareAndSet(cur,next)){//如果设置成功了,就得到了想要的值。
System.out.println("第几次访问:"+n.get());
return next;
}
}
}
@Override
public ServiceInstance isntance(List<ServiceInstance> instances) {
int index = getNext(instances.size());
return instances.get(index);
}
}
这个是service
package home.controller;
import home.entity.MyResult;
import home.entity.Pay;
import home.lb.Lb;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
@Slf4j
public class OrderCon {
@Autowired
RestTemplate restTemplate;
@Autowired
Lb lb;
@Autowired
DiscoveryClient discoveryClient;
@GetMapping(value = "/order/create")
public MyResult create(Pay pay) {//pay/create
log.info(pay + "");
// "http://localhost:8001/pay/create" 之前我们是写死,现在是多台的集群了。不能写死
return restTemplate.postForObject("http://PAY-SERVICE/pay/create", pay, MyResult.class);
}
@GetMapping(value = "/order/create/entity")
public MyResult createEntity(Pay pay) {//pay/create
log.info(pay + "");
// "http://localhost:8001/pay/create" 之前我们是写死,现在是多台的集群了。不能写死
ResponseEntity<MyResult> myResultResponseEntity = restTemplate.postForEntity("http://PAY-SERVICE/pay/create", (Object) pay, MyResult.class);
return myResultResponseEntity.getBody();
}
@GetMapping(value = "/order/get/{id}")
public MyResult get(@PathVariable(value = "id") int id) {
return restTemplate.getForObject("http://PAY-SERVICE/pay/get/" + id, MyResult.class);//这个返回的是你传入的返回值
}
@GetMapping(value = "/order/getEntity/{id}")
public MyResult getEntity(@PathVariable(value = "id") int id) {
ResponseEntity<MyResult> forEntity = restTemplate.getForEntity("http://PAY-SERVICE/pay/get/" + id, MyResult.class);
//这个返回的是一个封装了其他信息和你的返回值的对象
return forEntity.getBody();
}
@GetMapping(value = "/order/getlb/{id}")
public MyResult getLb(@PathVariable(value =
"id") int id){
List<ServiceInstance> instances = discoveryClient.getInstances("pay-service");
for (ServiceInstance instance : instances) {
System.out.println("instance.getUri() = " + instance.getUri());
}
ServiceInstance isntance = lb.isntance(instances);
System.out.println(isntance.getUri());
return restTemplate.getForObject(isntance.getUri()+"/pay/get/" + id, MyResult.class);//这个返回的是你传入的返回值
}
}
这个是controller 主要最后一个方法。
package home.config;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
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 RestTem {
@Bean
// @LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
不需要@LoadBalanced 也可以
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class,HibernateJpaAutoConfiguration.class})
@EnableEurekaClient
//@RibbonClient(name = "PAY-SERVICE",configuration = bal.class) //也就是用这个注解引入这个bean 单独使用。
public class Order80 {
public static void main(String[] args) {
SpringApplication.run(Order80.class, args);
}
}
也不需要这个客户端了
因为@EnableEurekaClient 这个已经自动的引入ribbon了。所以肯定会引入DiscoverClient ,所以不用显示enable也能用。