使用loadbalancer
springcloud-2020.0.*版本后逐漸移除了netflix的組件(ribbon, hystrix),現(xiàn)在SpringCloud不是與某一個類庫綁定,而是提供了一套抽象,這樣就可以在保持接口不變的情況下切換實現(xiàn)方案,新版本負載算法使用loadbalancer實現(xiàn)
切換負載算法
- 默認負載算法 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); }
- 配置自定義負載均衡的服務(wù),其中 “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();}}
代碼實現(xiàn)
- 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
- 代理設(shè)計模式