¶Ô±ÈÐÂÎļþ |
| | |
| | | /* |
| | | * Copyright (c) 2018-2028, Chill Zhuang All rights reserved. |
| | | * |
| | | * Redistribution and use in source and binary forms, with or without |
| | | * modification, are permitted provided that the following conditions are met: |
| | | * |
| | | * Redistributions of source code must retain the above copyright notice, |
| | | * this list of conditions and the following disclaimer. |
| | | * Redistributions in binary form must reproduce the above copyright |
| | | * notice, this list of conditions and the following disclaimer in the |
| | | * documentation and/or other materials provided with the distribution. |
| | | * Neither the name of the dreamlu.net developer nor the names of its |
| | | * contributors may be used to endorse or promote products derived from |
| | | * this software without specific prior written permission. |
| | | * Author: Chill åºéª (smallchill@163.com) |
| | | */ |
| | | package org.springblade.core.loadbalancer.rule; |
| | | |
| | | import com.alibaba.nacos.common.utils.StringUtils; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springblade.core.loadbalancer.props.BladeLoadBalancerProperties; |
| | | import org.springframework.beans.factory.ObjectProvider; |
| | | import org.springframework.cloud.client.ServiceInstance; |
| | | import org.springframework.cloud.client.loadbalancer.*; |
| | | import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier; |
| | | import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer; |
| | | import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; |
| | | import org.springframework.http.HttpHeaders; |
| | | import org.springframework.util.CollectionUtils; |
| | | import org.springframework.util.PatternMatchUtils; |
| | | import reactor.core.publisher.Mono; |
| | | |
| | | import java.util.List; |
| | | import java.util.concurrent.ThreadLocalRandom; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static org.springblade.core.loadbalancer.constant.LoadBalancerConstant.VERSION_NAME; |
| | | |
| | | /** |
| | | * LoadBalancer è´è½½è§å |
| | | * |
| | | * @author Chill |
| | | */ |
| | | @Slf4j |
| | | @RequiredArgsConstructor |
| | | public class GrayscaleLoadBalancer implements ReactorServiceInstanceLoadBalancer { |
| | | private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider; |
| | | private final BladeLoadBalancerProperties bladeLoadBalancerProperties; |
| | | |
| | | @Override |
| | | public Mono<Response<ServiceInstance>> choose(Request request) { |
| | | ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider |
| | | .getIfAvailable(NoopServiceInstanceListSupplier::new); |
| | | return supplier.get(request).next() |
| | | .map(serviceInstances -> getInstanceResponse(serviceInstances, request)); |
| | | } |
| | | |
| | | /** |
| | | * èªå®ä¹èç¹è§åè¿åç®æ èç¹ |
| | | */ |
| | | private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, Request request) { |
| | | // 注åä¸å¿æ å¯ç¨å®ä¾ è¿å空 |
| | | if (CollectionUtils.isEmpty(instances)) { |
| | | return new EmptyResponse(); |
| | | } |
| | | // æå®ipåè¿å满足ipçæå¡ |
| | | List<String> priorIpPattern = bladeLoadBalancerProperties.getPriorIpPattern(); |
| | | if (!priorIpPattern.isEmpty()) { |
| | | String[] priorIpPatterns = priorIpPattern.toArray(new String[0]); |
| | | List<ServiceInstance> priorIpInstances = instances.stream().filter( |
| | | (i -> PatternMatchUtils.simpleMatch(priorIpPatterns, i.getHost())) |
| | | ).collect(Collectors.toList()); |
| | | if (!priorIpInstances.isEmpty()) { |
| | | instances = priorIpInstances; |
| | | } |
| | | } |
| | | |
| | | // è·åç°åº¦çæ¬å· |
| | | DefaultRequestContext context = (DefaultRequestContext) request.getContext(); |
| | | RequestData requestData = (RequestData) context.getClientRequest(); |
| | | HttpHeaders headers = requestData.getHeaders(); |
| | | String versionName = headers.getFirst(VERSION_NAME); |
| | | |
| | | // 没ææå®ç°åº¦çæ¬åè¿åæ£å¼çæå¡ |
| | | if (StringUtils.isBlank(versionName)) { |
| | | List<ServiceInstance> noneGrayscaleInstances = instances.stream().filter( |
| | | i -> !i.getMetadata().containsKey(VERSION_NAME) |
| | | ).collect(Collectors.toList()); |
| | | return randomInstance(noneGrayscaleInstances); |
| | | } |
| | | |
| | | // æå®ç°åº¦çæ¬åè¿åæ è®°çæå¡ |
| | | List<ServiceInstance> grayscaleInstances = instances.stream().filter(i -> { |
| | | String versionNameInMetadata = i.getMetadata().get(VERSION_NAME); |
| | | return StringUtils.equalsIgnoreCase(versionNameInMetadata, versionName); |
| | | }).collect(Collectors.toList()); |
| | | return randomInstance(grayscaleInstances); |
| | | } |
| | | |
| | | /** |
| | | * éç¨éæºè§åè¿å |
| | | */ |
| | | private Response<ServiceInstance> randomInstance(List<ServiceInstance> instances) { |
| | | // è¥æ²¡æå¯ç¨èç¹åè¿å空 |
| | | if (instances.isEmpty()) { |
| | | return new EmptyResponse(); |
| | | } |
| | | |
| | | // æééæºèç¹è¿å |
| | | int randomIndex = ThreadLocalRandom.current().nextInt(instances.size()); |
| | | ServiceInstance instance = instances.get(randomIndex % instances.size()); |
| | | return new DefaultResponse(instance); |
| | | } |
| | | } |