1:背景介绍
有没有想过在多个服务调用之间,例如Netflix的Ribbon(spring cloud Ribbon)是如何做的负载均衡?就比如A服务调用B服务,但是此时B服务是多台机器部署,你要选择那一台进行调用?是随机选一台?还是轮询选一台?还是按照权重选一台?本次不介绍Ribbon,咱们就借助Ribbon这个场景来实现下策略设计模式;
2:类图设计
设计思想:首先定义一个负载均衡接口(LoadBalanceStrategy),然后使用不同的子类去实现此接口,在程序启动的时候分别实例化负载均衡类,将负载均衡类注入到LoadBalanceContent策略上下文中,类型是HashMap<String, LoadBalanceStrateg>,key主要是负载均衡的名字,值就是它本身,程序启动根据配置负载均衡策略的名字,来使用不同的负载均衡器;AbstractLoadBalanceStrategy中维护目标主机的列表,只能子类使用;
3:代码
public interface LoadBalanceStrategy {
String getHost();
}
public abstract class AbstractLoadBalanceStrategy implements LoadBalanceStrategy {
/**
* 维护的主机ip地址列表
*/
protected List<String> hosts;
public AbstractLoadBalanceStrategy(List<String> hosts) {
this.hosts = hosts;
}
}
轮询负载均衡器
public class PollLoadBalance extends AbstractLoadBalanceStrategy {
final AtomicInteger atomicInteger = new AtomicInteger(0);
public PollLoadBalance(List<String> hosts) {
super(hosts);
}
@Override
public String getHost() {
// 轮询选机器
synchronized (atomicInteger){
atomicInteger.compareAndSet(hosts.size() - 1, 0);
int index = atomicInteger.getAndIncrement();
return hosts.get(index);
}
}
}
随机负载均衡器
public class RandomLoadBalance extends AbstractLoadBalanceStrategy {
public RandomLoadBalance(List<String> hosts) {
super(hosts);
}
@Override
public String getHost() {
// 随机选择目标主机
int random = (int)(Math.random() * 10000);
return hosts.get(random % hosts.size());
}
}
权重负载均衡器
public class WeightLoadBalance extends AbstractLoadBalanceStrategy {
/**
* ip 权重
* 例: ["192.168.0.1", "192.168.0.1", "192.168.0.1", "192.168.0.2"]
* 192.168.0.1 权重是3, 192.168.0.2 权重是1
*/
private List<String> hostWeight;
public WeightLoadBalance(List<String> hosts) {
super(hosts);
// 将每个主机ip,随机生成权重
hostWeight = hosts.stream()
.map(ip -> {
String[] ips = new String[(int) (Math.random() * 10)];
Arrays.fill(ips, ip);
return ips;
})
.flatMap(Stream::of)
.collect(Collectors.toList());
}
@Override
public String getHost() {
// 权重选机器
Random random = new Random();
int index = random.nextInt(hostWeight.size());
return hostWeight.get(index);
}
}
策略上下文
@Component
public class LoadBalanceContent {
@Value("${loadBalanceName}")
private String LoadBalanceName;
private HashMap<String, LoadBalanceStrategy> loadBalanceMap;
public LoadBalanceContent() {
// 假如初始化 3台机器
ArrayList<String> hosts = new ArrayList<>();
hosts.add("192.168.0.1");
hosts.add("192.168.0.2");
hosts.add("192.168.0.3");
// 初始化负载均衡器
loadBalanceMap = new HashMap<>();
loadBalanceMap.put("POLL", new PollLoadBalance(hosts));
loadBalanceMap.put("WEIGHT", new WeightLoadBalance(hosts));
loadBalanceMap.put("RANDOM", new RandomLoadBalance(hosts));
}
/**
* 获取下一台主机
* @return
*/
public String nextHost() {
return loadBalanceMap.get(LoadBalanceName).getHost();
}
}
application.yml配置文件
loadBalanceName: POLL # 轮询负载均衡策略
Controller
@RestController
@RequestMapping("/fuzhu/simple")
public class SimpleController {
@Autowired
private SimpleService simpleService;
@GetMapping("/testLoadbalance")
public ResultInfo testLoadbalance(){
return simpleService.testLoadbalance();
}
}
Service
@Service
@Slf4j
public class SimpleServiceImpl implements SimpleService {
@Autowired
private LoadBalanceContent loadBalanceContent;
@Override
public ResultInfo testLoadbalance() {
// 获取目标机器
String host = loadBalanceContent.nextHost();
log.info("目标主机是: {}", host);
return ResultInfo.buildSuccess("success");
}
}
4:测试
轮询负载均衡策略
随机负载均衡策略
权重负载均衡策略
5:策略设计模式的优点
每个算法单独封装,减少了算法和算法调用者的耦合。
不过每多一个策略,就需要一个策略类
代码中难免会有不正确的地方,还望多多指正