使用loadbalancer
springcloud-2020.0.*版本后逐渐移除了netflix的组件(ribbon, hystrix),现在SpringCloud不是与某一个类库绑定,而是提供了一套抽象,这样就可以在保持接口不变的情况下切换实现方案,新版本负载算法使用loadbalancer实现
切换负载算法
- 默认负载算法 RoundRobinLoadBalancer
@Configuration(proxyBeanMethods = false)@ConditionalOnDiscoveryEnabledpublic class LoadBalancerClientConfiguration {private static final int REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER = 193827465;@Bean@ConditionalOnMissingBeanpublic ReactorLoadBalancer reactorServiceInstanceLoadBalancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);}
- 模仿 RoundRobinLoadBalancer 写自定义的负载均衡类
public class MyLoadBalancer implements ReactorServiceInstanceLoadBalancer {private static final Log log = LogFactory.getLog(RoundRobinLoadBalancer.class);final AtomicInteger position;/**轮询间隔*/final Integer interval = 3;final String serviceId;ObjectProvider serviceInstanceListSupplierProvider;public MyLoadBalancer(ObjectProvider serviceInstanceListSupplierProvider,String serviceId) {this(serviceInstanceListSupplierProvider, serviceId, new Random().nextInt(1000));}public MyLoadBalancer(ObjectProvider serviceInstanceListSupplierProvider,String serviceId, int seedPosition) {this.serviceId = serviceId;this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;this.position = new AtomicInteger(seedPosition);}@SuppressWarnings(“rawtypes”)@Overridepublic Mono choose(Request request) {ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);return supplier.get(request).next().map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));}private Response processInstanceResponse(ServiceInstanceListSupplier supplier,List serviceInstances) {Response serviceInstanceResponse = getInstanceResponse(serviceInstances);if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());}return serviceInstanceResponse;}private Response getInstanceResponse(List instances) {if (instances.isEmpty()) {if (log.isWarnEnabled()) {log.warn(“No servers available for service: ” + serviceId);}return new EmptyResponse();}int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;ServiceInstance instance = instances.get(pos / interval % instances.size());log.info(“instance:”+instance.getInstanceId());return new DefaultResponse(instance);}}
- 自定义负载配置类
public class MybanlancerConfiguration {/** * 自定义负载 * @author Noodles * @date 2022/5/24 21:40 */@BeanReactorLoadBalancer myloadBalancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new MyLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),name);}}
如果要提供自定义的ServiceInstanceListSupplier 就在此类中加入自定义的bean即可,如下所示
@Bean public ServiceInstanceListSupplier myDiscoveryClientServiceInstanceListSupplier( ConfigurableApplicationContext context) { return ServiceInstanceListSupplier.builder().withDiscoveryClient().build(context); }
- 配置自定义负载均衡的服务,其中 “NOODLES_PROVIDER” 为注册到注册中心的serviceId
@Configuration@LoadBalancerClients(value = {@LoadBalancerClient(name = “NOODLES-PROVIDER”, configuration = MybanlancerConfiguration.class) })public class LoadBalanceConfig {Logger logger = LoggerFactory.getLogger(LoadBalanceConfig.class);@LoadBalanced@BeanWebClient.Builder webClientBuilder() {return WebClient.builder();}}
代码实现
- spring-cloud-parent -> noodles-consumer
- spring-cloud-parent -> spring-cloud-mygateway
参考资料
- https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#spring-cloud-loadbalancer
扩展知识
- Spring 接口 BeanPostProcessor
- 代理设计模式