Java案例怎么负载均衡请求?

wen java案例 77

本文目录导读:

Java案例怎么负载均衡请求?

  1. 场景一:客户端负载均衡(微服务架构-Ribbon / Spring Cloud LoadBalancer)
  2. 场景二:服务器端负载均衡(Nginx + Java 应用)
  3. 场景三:使用 Java 内置工具类实现几种常见算法

在Java中实现负载均衡请求,通常有两种主要的应用场景:客户端负载均衡(如微服务调用)和 服务器端负载均衡(如反向代理)。

下面针对这两种场景,分别给出常见的实现案例和代码示例。


客户端负载均衡(微服务架构-Ribbon / Spring Cloud LoadBalancer)

在微服务架构中(例如Spring Cloud),调用方(消费者)会从注册中心获取服务列表,然后在本地通过负载均衡算法选择一个实例进行调用。

原生 Java + 简单轮询算法(手写)

这是一个最基础的方式,展示了负载均衡的核心逻辑。

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
// 模拟一个服务实例
class ServerInstance {
    private String ip;
    private int port;
    public ServerInstance(String ip, int port) {
        this.ip = ip;
        this.port = port;
    }
    public String getUrl() {
        return "http://" + ip + ":" + port + "/api/data";
    }
}
// 简易轮询负载均衡器
public class RoundRobinLoadBalancer {
    private final List<ServerInstance> serverList;
    private final AtomicInteger counter = new AtomicInteger(0);
    public RoundRobinLoadBalancer(List<ServerInstance> serverList) {
        this.serverList = serverList;
    }
    // 获取下一个服务实例(轮询)
    public ServerInstance getNextServer() {
        int index = counter.getAndIncrement() % serverList.size();
        return serverList.get(index);
    }
    // 测试
    public static void main(String[] args) {
        List<ServerInstance> servers = List.of(
                new ServerInstance("192.168.1.1", 8080),
                new ServerInstance("192.168.1.2", 8080),
                new ServerInstance("192.168.1.3", 8080)
        );
        RoundRobinLoadBalancer lb = new RoundRobinLoadBalancer(servers);
        // 模拟10次请求
        for (int i = 0; i < 10; i++) {
            ServerInstance server = lb.getNextServer();
            System.out.println("请求 " + (i + 1) + " 分发到: " + server.getUrl());
        }
    }
}

输出示例:

请求 1 分发到: http://192.168.1.1:8080/api/data
请求 2 分发到: http://192.168.1.2:8080/api/data
请求 3 分发到: http://192.168.1.3:8080/api/data
请求 4 分发到: http://192.168.1.1:8080/api/data
...

Spring Cloud + LoadBalancer(推荐,企业级)

如果项目使用Spring Cloud,推荐使用 Spring Cloud LoadBalancer(取代了已进入维护状态的 Netflix Ribbon)。

依赖(pom.xml):

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

RestTemplate 配置:

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 RestTemplateConfig {
    @Bean
    @LoadBalanced  // 关键注解:启用负载均衡
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

服务调用代码:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class OrderController {
    @Autowired
    private RestTemplate restTemplate;
    private static final String PRODUCT_SERVICE_URL = "http://product-service"; // 服务名
    @GetMapping("/order/create")
    public String createOrder() {
        // 直接调用服务名,LoadBalancer会自动从注册中心获取列表并轮询
        String response = restTemplate.getForObject(
                PRODUCT_SERVICE_URL + "/products",
                String.class
        );
        return "Order created, product info: " + response;
    }
}

原理: Spring Cloud LoadBalancer 默认使用轮询算法,也可以切换为随机权重等策略。


服务器端负载均衡(Nginx + Java 应用)

这是最经典的架构:Nginx 作为反向代理,把请求分发到多个 Tomcat / Spring Boot 实例。

Nginx 配置示例(nginx.conf):

upstream backend_servers {
    # 默认使用轮询(round-robin)
    server 192.168.1.10:8080 weight=2;   # 权重2
    server 192.168.1.11:8080 weight=1;   # 权重1
    server 192.168.1.12:8080 weight=1;
    # 其他可选的算法:
    # least_conn;   # 最少连接
    # ip_hash;      # IP哈希(会话保持)
}
server {
    listen 80;
    server_name example.com;
    location /api/ {
        proxy_pass http://backend_servers;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Java 端不需要任何负载均衡代码,只需要部署多个实例即可:

# 启动3个实例(端口不同)
java -jar myapp.jar --server.port=8080
java -jar myapp.jar --server.port=8081
java -jar myapp.jar --server.port=8082

Nginx 会自动将请求分发到这三个实例。


使用 Java 内置工具类实现几种常见算法

有时你需要在一个 Java 进程中做简单的负载均衡(RPC 客户端、网关、SDK)。

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class LoadBalancerAlgorithms {
    // 1. 随机算法
    public static <T> T random(List<T> servers) {
        Random random = new Random();
        return servers.get(random.nextInt(servers.size()));
    }
    // 2. 加权随机算法
    public static <T> T weightedRandom(Map<T, Integer> serverWeights) {
        List<T> weightedList = new ArrayList<>();
        serverWeights.forEach((server, weight) -> {
            for (int i = 0; i < weight; i++) {
                weightedList.add(server);
            }
        });
        Random random = new Random();
        return weightedList.get(random.nextInt(weightedList.size()));
    }
    // 3. 平滑加权轮询(解决普通加权轮询的突发流量问题)
    static class SmoothWeightedRoundRobin<T> {
        private final List<ServerWeight<T>> servers;
        static class ServerWeight<T> {
            T server;
            int weight;
            int currentWeight;
            ServerWeight(T server, int weight) {
                this.server = server;
                this.weight = weight;
                this.currentWeight = 0;
            }
        }
        public SmoothWeightedRoundRobin(Map<T, Integer> serverWeights) {
            this.servers = new ArrayList<>();
            serverWeights.forEach((server, weight) ->
                    servers.add(new ServerWeight<>(server, weight))
            );
        }
        public T getNext() {
            int totalWeight = 0;
            ServerWeight<T> selected = null;
            for (ServerWeight<T> sw : servers) {
                sw.currentWeight += sw.weight;
                totalWeight += sw.weight;
                if (selected == null || sw.currentWeight > selected.currentWeight) {
                    selected = sw;
                }
            }
            if (selected != null) {
                selected.currentWeight -= totalWeight;
                return selected.server;
            }
            return null;
        }
    }
    // 测试
    public static void main(String[] args) {
        List<String> servers = List.of("A", "B", "C");
        System.out.println("随机算法: " + random(servers));
        Map<String, Integer> weightedServers = new HashMap<>();
        weightedServers.put("A", 5);
        weightedServers.put("B", 3);
        weightedServers.put("C", 2);
        System.out.println("加权随机: " + weightedRandom(weightedServers));
        SmoothWeightedRoundRobin<String> swrr = new SmoothWeightedRoundRobin<>(weightedServers);
        for (int i = 0; i < 10; i++) {
            System.out.println("平滑轮询 " + (i + 1) + ": " + swrr.getNext());
        }
    }
}

场景 推荐方案 适用场景
微服务调用 Spring Cloud LoadBalancer / Ribbon 服务间 RPC 调用
反向代理 Nginx / HAProxy + Java 后端 对外统一入口
网关 Spring Cloud Gateway / Zuul API 网关层
客户端直连 手写轮询/随机/一致性Hash 自定义 RPC 客户端、SDK
消息队列 消息队列自带的生产者/消费者负载均衡 异步解耦

最佳实践建议:

  1. 不要重复造轮子 —— 微服务优先用 Spring Cloud LoadBalancer。
  2. Nginx 是最成熟的服务器端方案,性能好、配置简单。
  3. 如果需要会话保持,注意使用 ip_hash(Nginx)或 Sticky Session
  4. 如果是长连接(如 gRPC),要考虑连接池和健康检查。

如果你有更具体的场景需求(比如需要一致性哈希、动态权重调整等),可以继续追问,我可以给出针对性的实现。

抱歉,评论功能暂时关闭!