如何在Spring Boot中使用Ribbon配置4:1权重比例的负载均衡?
Hey there, let's tackle your requirement to route 80% of traffic to your high-resource server and 20% to the one with limited cache. Since you're currently using Ribbon's default round-robin (which splits requests evenly), here are two reliable ways to set up fixed weighted distribution:
Method 1: Build a Custom Load Balancing Rule
Ribbon's core load balancing logic is handled by the IRule interface. We can extend the base rule class to create our own fixed-weight logic tailored to your 4:1 ratio.
Step 1: Create a Custom Weight Rule Class
Make a class that inherits AbstractLoadBalancerRule and overrides the choose method to pick servers based on your desired weight distribution.
import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; import java.util.List; import java.util.concurrent.ThreadLocalRandom; public class FixedWeightDistributionRule extends AbstractLoadBalancerRule { // Hardcode weights here (4 for your first server, 1 for the second) private final int[] serverWeights = {4, 1}; @Override public void initWithNiwsConfig(IClientConfig clientConfig) { // No extra setup needed here } @Override public Server choose(Object key) { ILoadBalancer loadBalancer = getLoadBalancer(); if (loadBalancer == null) return null; List<Server> availableServers = loadBalancer.getAllServers(); if (availableServers.isEmpty()) return null; // Calculate total weight to set our random range int totalWeight = 0; for (int weight : serverWeights) { totalWeight += weight; } // Generate a random number between 0 and totalWeight-1 int randomWeight = ThreadLocalRandom.current().nextInt(totalWeight); // Match the random number to the corresponding server int cumulativeWeight = 0; for (int i = 0; i < availableServers.size(); i++) { cumulativeWeight += serverWeights[i]; if (randomWeight < cumulativeWeight) { return availableServers.get(i); } } // Fallback to the first server if something goes wrong return availableServers.get(0); } }
Step 2: Configure Ribbon to Use Your Custom Rule
Create a Ribbon client configuration to tell your target service to use this new rule.
import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.context.annotation.Configuration; // Replace "your-target-service" with the actual name of your service @RibbonClient(name = "your-target-service", configuration = RibbonCustomConfig.class) @Configuration public class GlobalRibbonConfig { } // Keep this config class separate to avoid making it global (unless you want that!) @Configuration class RibbonCustomConfig { @Bean public FixedWeightDistributionRule fixedWeightDistributionRule() { return new FixedWeightDistributionRule(); } }
Pro Tip: Don't put
RibbonCustomConfigin the same package as your main application class (or any package scanned by@ComponentScan). If you do, this rule will apply to all Ribbon clients in your app instead of just the target service.
Method 2: Dynamic Weights via Eureka Metadata (If Using Service Discovery)
If your servers are registered with Eureka, you can set weights directly in each server's config and adjust your custom rule to read these values dynamically (so you don't have to hardcode weights in the rule class).
Step 1: Set Weights in Each Server's Config
For your high-resource server:
eureka: instance: metadata-map: weight: 4
For your low-resource server:
eureka: instance: metadata-map: weight: 1
Step 2: Update the Custom Rule to Read Metadata
Modify the choose method in your FixedWeightDistributionRule to pull weights from Eureka metadata instead of hardcoding:
@Override public Server choose(Object key) { ILoadBalancer loadBalancer = getLoadBalancer(); if (loadBalancer == null) return null; List<Server> availableServers = loadBalancer.getAllServers(); if (availableServers.isEmpty()) return null; int[] serverWeights = new int[availableServers.size()]; int totalWeight = 0; // Pull weights from Eureka instance metadata for (int i = 0; i < availableServers.size(); i++) { Server server = availableServers.get(i); String weightValue = server.getMetaInfo().getMetadata().get("weight"); // Default to weight 1 if no value is set serverWeights[i] = weightValue != null ? Integer.parseInt(weightValue) : 1; totalWeight += serverWeights[i]; } // Same random weight matching logic as before int randomWeight = ThreadLocalRandom.current().nextInt(totalWeight); int cumulativeWeight = 0; for (int i = 0; i < availableServers.size(); i++) { cumulativeWeight += serverWeights[i]; if (randomWeight < cumulativeWeight) { return availableServers.get(i); } } return availableServers.get(0); }
Verify the Setup
Once you deploy these changes, monitor your request logs or use a tool like Spring Boot Actuator to check traffic distribution. Over time, you should see roughly 4 requests going to your first server for every 1 to the second. Short-term fluctuations are normal (since we're using random weighting), but the long-term ratio will match your 4:1 target.
内容的提问来源于stack exchange,提问作者Piyush Raghuwanshi

