负载均衡演示
总述
在现代分布式系统中,负载均衡是确保系统高效运行的关键技术之一,通过将网络流量或计算任务分配到多个服务器或资源上,负载均衡能够优化资源使用、最大化吞吐量、减少响应时间,并提高系统的可靠性和可用性,本文将以一个“电子商务”应用为例,详细演示从一百个到千万级并发情况下服务端架构的演进过程,并介绍每个阶段遇到的相关技术和问题。
的详细叙述:
一、场景复现
为了更好地演示负载均衡的效果,我们可以模拟一个电子商务应用,其中订单服务需要调用商品服务来获取商品信息,为了实现这一目标,我们需要启动多个商品服务的实例,并通过负载均衡机制将请求分配到不同的实例上。
二、启动多个商品服务实例
我们需要启动多个商品服务(product-service)的实例,可以通过修改配置文件和端口号来实现这一点,假设我们启动了三个商品服务实例,分别监听不同的端口号。
启动第一个商品服务实例 java -jar product-service.jar --server.port=8081 启动第二个商品服务实例 java -jar product-service.jar --server.port=8082 启动第三个商品服务实例 java -jar product-service.jar --server.port=8083
启动后,我们可以观察到Eureka服务发现中有三个商品服务实例。
三、访问订单服务
我们访问订单服务,该服务会远程调用商品服务来获取商品信息,在初始情况下,所有的请求都会被路由到同一台机器上,这并不是我们期望的结果,我们希望请求能够均匀地分配到不同的商品服务实例上。
四、修改远程调用逻辑
为了实现负载均衡,我们需要修改订单服务中的远程调用逻辑,我们需要从Eureka服务发现中获取所有可用的商品服务实例,并根据某种算法选择一个实例进行调用,这里我们采用轮询算法作为示例。
代码示例:
package com.guan.order.service; import com.guan.order.mapper.OrderMapper; import com.guan.order.model.OrderInfo; import com.guan.product.model.ProductInfo; import jakarta.annotation.PostConstruct; import jakarta.annotation.Resource; 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.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @Service @Slf4j public class OrderService { @Autowired private OrderMapper orderMapper; @Resource private DiscoveryClient discoveryClient; @Autowired private RestTemplate restTemplate; private static AtomicInteger atomicInteger = new AtomicInteger(1); private static List<ServiceInstance> instances; @PostConstruct public void init() { // 根据应用名称获取服务列表 instances = discoveryClient.getInstances("product-service"); } public OrderInfo selectOrderByID(Integer orderID) { OrderInfo orderInfo = orderMapper.selectOrderById(orderID); // 从Eureka获取服务列表 List<ServiceInstance> instances = discoveryClient.getInstances("product-service"); // 有多个服务,根据轮询获取 int index = atomicInteger.getAndIncrement() % instances.size(); String uri = instances.get(index).getUri().toString(); String url = uri + "/product/" + orderInfo.getProductId(); log.info("远程调用url:{}", url); ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class); orderInfo.setProductInfo(productInfo); return orderInfo; } }
通过上述代码,我们实现了一个简单的轮询负载均衡机制,每次远程调用时,都会根据atomicInteger
的值选择一个商品服务实例进行调用,从而实现请求的均匀分配。
五、负载均衡算法详解及Java代码演示
负载均衡算法可以分为静态负载均衡和动态负载均衡两大类,以及基于权重和基于性能的负载均衡,下面将详细介绍几种常见的负载均衡算法及其Java代码实现。
1. 轮询(Round Robin)
轮询算法是最简单的一种静态负载均衡算法,它按照顺序依次将请求分配给每个服务器,当到达列表末尾时,重新回到列表开头继续分配。
Java代码实现:
import java.util.ArrayList; import java.util.List; public class RoundRobinLoadBalancer { private List<String> servers; private int currentIndex; public RoundRobinLoadBalancer(List<String> servers) { this.servers = servers; currentIndex = 0; } public String getNextServer() { String server = servers.get(currentIndex); currentIndex = (currentIndex + 1) % servers.size(); return server; } public static void main(String[] args) { List<String> servers = new ArrayList<>(); servers.add("Server1"); servers.add("Server2"); servers.add("Server3"); RoundRobinLoadBalancer loadBalancer = new RoundRobinLoadBalancer(servers); for (int i = 0; i < 10; i++) { String server = loadBalancer.getNextServer(); System.out.println("Request " + i + " routed to " + server); } } }
2. 随机(Random)
随机算法也是一种静态负载均衡算法,它随机选择一个服务器来处理请求,虽然简单,但在某些情况下可能会导致负载不均。
Java代码实现:
import java.util.ArrayList; import java.util.List; import java.util.Random; public class RandomLoadBalancer { private List<String> servers; private Random random; public RandomLoadBalancer(List<String> servers) { this.servers = servers; random = new Random(); } public String getRandomServer() { int index = random.nextInt(servers.size()); return servers.get(index); } public static void main(String[] args) { List<String> servers = new ArrayList<>(); servers.add("Server1"); servers.add("Server2"); servers.add("Server3"); RandomLoadBalancer loadBalancer = new RandomLoadBalancer(servers); for (int i = 0; i < 10; i++) { String server = loadBalancer.getRandomServer(); System.out.println("Request " + i + " routed to " + server); } } }
3. 最小连接数(Least Connections)
最小连接数是一种动态负载均衡算法,它总是选择当前活动连接数最少的服务器来处理新的请求,这需要实时监控每个服务器的连接数。
Java代码实现:
import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; public class LeastConnectionsLoadBalancer { private Map<String, AtomicInteger> serverConnections; public LeastConnectionsLoadBalancer(List<String> servers) { serverConnections = new HashMap<>(); for (String server : servers) { serverConnections.put(server, new AtomicInteger(0)); } } public String getLeastConnectionsServer() { String selectedServer = null; int minConnections = Integer.MAX_VALUE; for (Map.Entry<String, AtomicInteger> entry : serverConnections.entrySet()) { int currentConnections = entry.getValue().get(); if (currentConnections < minConnections) { minConnections = currentConnections; selectedServer = entry.getKey(); } } serverConnections.get(selectedServer).incrementAndGet(); // 增加选中服务器的连接数 return selectedServer; } public void releaseServer(String server) { serverConnections.get(server).decrementAndGet(); // 释放服务器连接数 } public static void main(String[] args) { List<String> servers = new ArrayList<>(); servers.add("Server1"); servers.add("Server2"); servers.add("Server3"); LeastConnectionsLoadBalancer loadBalancer = new LeastConnectionsLoadBalancer(servers); for (int i = 0; i < 10; i++) { String server = loadBalancer.getLeastConnectionsServer(); System.out.println("Request " + i + " routed to " + server); // 模拟请求完成后释放连接 loadBalancer.releaseServer(server); } } }
4. 最短响应时间(Least Response Time)
最短响应时间算法也是一种动态负载均衡算法,它选择当前响应时间最短的服务器来处理新的请求,这同样需要实时监控每个服务器的响应时间。
Java代码实现:
import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; public class LeastResponseTimeLoadBalancer { private Map<String, AtomicLong> responseTimes; private Map<String, AtomicInteger requestCounts; private Map<String, Long lastResponseTimes; private long startTime; public LeastResponseTimeLoadBalancer() { responseTimes = new ConcurrentHashMap<>(); requestCounts = new ConcurrentHashMap<>(); lastResponseTimes = new ConcurrentHashMap<>(); startTime = System.currentTimeMillis(); } public void recordResponseTime(String server, long responseTime) { responseTimes.computeIfAbsent(server, k -> new AtomicLong(0)).addAndGet(responseTime); requestCounts.computeIfAbsent(server, k -> new AtomicInteger(0)).incrementAndGet(); lastResponseTimes.put(server, responseTime); } public String getLeastResponseTimeServer() { long currentTime = System.currentTimeMillis(); return requestCounts.entrySet().stream() .min((e1, e2) -> { long avgTime1 = lastResponseTimes.get(e1.getKey()) / e1.getValue().get(); long avgTime2 = lastResponseTimes.get(e2.getKey()) / e2.getValue().get(); return Long.compare(avgTime1, avgTime2); }) .map(Map.Entry::getKey) .orElse(null); } public static void main(String[] args) { List<String> servers = new ArrayList<>(); servers.add("Server1"); servers.add("Server2"); servers.add("Server3"); LeastResponseTimeLoadBalancer loadBalancer = new LeastResponseTimeLoadBalancer(); for (int i = 0; i < 10; i++) { String server = loadBalancer.getLeastResponseTimeServer(); System.out.println("Request " + i + " routed to " + server); // 模拟请求完成后记录响应时间(假设为100ms) loadBalancer.recordResponseTime(server, 100); } } }
5. 基于权重的负载均衡(Weighted Load Balancing)
基于权重的负载均衡算法允许为不同的服务器分配不同的权重,从而更灵活地控制负载分配,性能较好的服务器可以分配更高的权重。
Java代码实现:
import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.Comparator; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.function.BinaryOperator; import java.util.function.UnaryOperator; import java.util.function.Function;import java.util.*;import java.util.concurrent.*;import java.util.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import java.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk.*;import jdk;;public class WeightedLoadBalancer { private Map<String, Integer> weights; public WeightedLoadBalancer(Map<String, Integer> weights) { this.weights = weights; } public String getWeightedServer() { // 将所有服务器按权重比例放入列表中 List<String> weightedList = new ArrayList<>(); for (Map.Entry<String, Integer> entry : weights.entrySet()) { for (int i = 0; i < entry.getValue(); i++) { weightedList.add(entry.getKey()); } Collections.shuffle(weightedList); return weightedList.isEmpty() ? null : weightedList.remove(0); }} public static void main(String[] args) { Map<String, Integer> weights = new HashMap<>(); weights.put("Server1", 1); weights.put("Server2", 3); weights.put("Server3", 5); WeightedLoadBalancer loadBalancer = new WeightedLoadBalancer(weights); for (int i = 0; i < 10; i++) { String server = loadBalancer.getWeightedServer(); System.out.println("Request " + i + " routed to " + server); } }}```
各位小伙伴们,我刚刚为大家分享了有关“负载均衡演示之”的知识,希望对你们有所帮助。如果您还有其他相关问题需要解决,欢迎随时提出哦!