国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【SpringCloud系列】開發(fā)環(huán)境下重寫Loadbalancer實現(xiàn)自定義負(fù)載均衡

這篇具有很好參考價值的文章主要介紹了【SpringCloud系列】開發(fā)環(huán)境下重寫Loadbalancer實現(xiàn)自定義負(fù)載均衡。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

前言

? ? ? ? spring-cloud-starter-netflix-ribbon已經(jīng)不再更新了,最新版本是2.2.10.RELEASE,最后更新時間是2021年11月18日,詳細(xì)信息可以看maven官方倉庫:https://search.maven.org/artifact/org.springframework.cloud/spring-cloud-starter-netflix-ribbon,SpringCloud官方推薦使用spring-cloud-starter-loadbalancer進(jìn)行負(fù)載均衡。我們在開發(fā)的時候,多人開發(fā)同一個微服務(wù),都注冊到同一個nacos,前端請求的時候,網(wǎng)關(guān)Gateway默認(rèn)輪訓(xùn)請求注冊中心的服務(wù),OpenFeign也會輪詢請求注冊中心的服務(wù),這樣就會導(dǎo)致前端有時會無法請求到我們本地寫的接口,而是請求到別人的服務(wù)中。所以我們可以重寫Loadbalancer默認(rèn)的負(fù)載均衡策略,實現(xiàn)自定義負(fù)載均衡策略,不管是Gateway還是OpenFeign都只能請求到我們自己本地的服務(wù)。

? ? ? ? 我的版本如下:

????????<spring-boot.version>2.7.3</spring-boot.version>
? ? ? ? <spring-cloud.version>2021.0.4</spring-cloud.version>
? ? ? ? <spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version>

一、添加負(fù)載方式配置

? ? ? ? 1、定義負(fù)載均衡方式的枚舉

public enum LoadBalancerTypeEnum {

    /**
     * 開發(fā)環(huán)境,獲取自己的服務(wù)
     */
    DEV,

    /**
     * 網(wǎng)關(guān),根據(jù)請求地址獲取對應(yīng)的服務(wù)
     */
    GATEWAY,

    /**
     * 輪循
     */
    ROUND_ROBIN,

    /**
     * 隨機(jī)
     */
    RANDOM;

}

? ? ? ? 2、添加配置類,默認(rèn)使用輪訓(xùn)方式

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * 負(fù)載均衡配置項
 */
@Data
@ConfigurationProperties(prefix = "spring.cloud.loadbalancer")
public class LoadBalanceProperties {

    private LoadBalancerTypeEnum type = LoadBalancerTypeEnum.ROUND_ROBIN;

}

二、參考默認(rèn)實現(xiàn)自定義

? ? ? ? 默認(rèn)的負(fù)載均衡策略是這個類:

org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer

? ? ? ? 我們參考這個類實現(xiàn)自己的負(fù)載均衡策略即可,RoundRobinLoadBalancer實現(xiàn)了ReactorServiceInstanceLoadBalancer這個接口,實現(xiàn)了choose這個方法,如下圖:

【SpringCloud系列】開發(fā)環(huán)境下重寫Loadbalancer實現(xiàn)自定義負(fù)載均衡,spring cloud,java,spring

? ? ? ? 在choose方法中調(diào)用了processInstanceResponse方法,processInstanceResponse方法中調(diào)用了getInstanceResponse方法,所以我們我們可以復(fù)制RoundRobinLoadBalancer整個類,只修改getInstanceResponse這個方法里的內(nèi)容就可以實現(xiàn)自定義負(fù)載均衡策略。

? ? ? ? 在自定義的類中,我們實現(xiàn)了四種負(fù)載均衡策略

? ? ? ? 1、getRoundRobinInstance方法是直接復(fù)制的RoundRobinLoadBalancer類中的實現(xiàn);

? ? ? ? 2、getRandomInstance方法參考o(jì)rg.springframework.cloud.loadbalancer.core.RandomLoadBalancer類中的實現(xiàn);

? ? ? ? 3、getDevelopmentInstance方法是返回所有服務(wù)中和當(dāng)前機(jī)器ip一致的服務(wù),如果沒有,則輪訓(xùn)返回;

? ? ? ? 4、getGatewayDevelopmentInstance方法是返回所有服務(wù)中和網(wǎng)關(guān)請求頭中ip一致的服務(wù)。

import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.TypeReference;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.ip.IpUtils;
import com.ruoyi.common.loadbalance.config.LoadBalancerTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultRequest;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.RequestData;
import org.springframework.cloud.client.loadbalancer.RequestDataContext;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.http.HttpHeaders;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 自定義 SpringCloud 負(fù)載均衡算法
 * 負(fù)載均衡算法的默認(rèn)實現(xiàn)是 {@link org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer}
 *
 */
@Slf4j
public class CustomSpringCloudLoadBalancer implements ReactorServiceInstanceLoadBalancer {

    private final String serviceId;
    private final AtomicInteger position;
    private final LoadBalancerTypeEnum type;
    private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;

    public CustomSpringCloudLoadBalancer(String serviceId,
                                         LoadBalancerTypeEnum type,
                                         ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider) {
        this(serviceId, new Random().nextInt(1000), type, serviceInstanceListSupplierProvider);
    }

    public CustomSpringCloudLoadBalancer(String serviceId,
                                         int seedPosition,
                                         LoadBalancerTypeEnum type,
                                         ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider) {
        this.serviceId = serviceId;
        this.position = new AtomicInteger(seedPosition);
        this.type = type;
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
    }

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
        return supplier.get(request).next().map(serviceInstances -> processInstanceResponse(request, supplier, serviceInstances));
    }

    private Response<ServiceInstance> processInstanceResponse(Request request,
                                                              ServiceInstanceListSupplier supplier,
                                                              List<ServiceInstance> serviceInstances) {
        Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(request, serviceInstances);
        if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
            ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
        }
        return serviceInstanceResponse;
    }

    private Response<ServiceInstance> getInstanceResponse(Request request, List<ServiceInstance> instances) {
        if (instances.isEmpty()) {
            if (log.isWarnEnabled()) {
                log.warn("No servers available for service: " + serviceId);
            }
            return new EmptyResponse();
        }

        if (Objects.equals(type, LoadBalancerTypeEnum.ROUND_ROBIN)){
            return this.getRoundRobinInstance(instances);
        }else if (Objects.equals(type, LoadBalancerTypeEnum.RANDOM)){
            return this.getRandomInstance(instances);
        }else if (Objects.equals(type, LoadBalancerTypeEnum.DEV)){
            return this.getDevelopmentInstance(instances);
        }else if (Objects.equals(type, LoadBalancerTypeEnum.GATEWAY)){
            return this.getGatewayDevelopmentInstance(request, instances);
        }
        return this.getRoundRobinInstance(instances);
    }

    /**
     * 獲取網(wǎng)關(guān)本機(jī)實例
     *
     * @param instances 實例
     * @return {@link Response }<{@link ServiceInstance }>
     * @author : lwq
     * @date : 2022-12-15 14:22:13
     */
    private Response<ServiceInstance> getGatewayDevelopmentInstance(Request request, List<ServiceInstance> instances) {

        //把request轉(zhuǎn)為默認(rèn)的DefaultRequest,從request中拿到請求的ip信息,再選擇ip一樣的微服務(wù)
        DefaultRequest<RequestDataContext> defaultRequest = Convert.convert(new TypeReference<DefaultRequest<RequestDataContext>>() {}, request);
        RequestDataContext context = defaultRequest.getContext();
        RequestData clientRequest = context.getClientRequest();
        HttpHeaders headers = clientRequest.getHeaders();
        String requestIp = IpUtils.getIpAddressFromHttpHeaders(headers);
        log.debug("客戶端請求gateway的ip:{}", requestIp);

        //先取得和本地ip一樣的服務(wù),如果沒有則按默認(rèn)來取
        for (ServiceInstance instance : instances) {
            String currentServiceId = instance.getServiceId();
            String host = instance.getHost();
            log.debug("注冊服務(wù):{},ip:{}", currentServiceId, host);
            if (StringUtils.isNotEmpty(host) && StringUtils.equals(requestIp, host)) {
                return new DefaultResponse(instance);
            }
        }
        return getRoundRobinInstance(instances);
    }


    /**
     * 獲取本機(jī)實例
     *
     * @param instances 實例
     * @return {@link Response }<{@link ServiceInstance }>
     * @author : lwq
     * @date : 2022-12-15 14:22:13
     */
    private Response<ServiceInstance> getDevelopmentInstance(List<ServiceInstance> instances) {
        //獲取本機(jī)ip
        String hostIp = IpUtils.getHostIp();
        log.debug("本機(jī)Ip:{}", hostIp);

        //先取得和本地ip一樣的服務(wù),如果沒有則按默認(rèn)來取
        for (ServiceInstance instance : instances) {
            String currentServiceId = instance.getServiceId();
            String host = instance.getHost();
            log.debug("注冊服務(wù):{},ip:{}", currentServiceId, host);
            if (StringUtils.isNotEmpty(host) && StringUtils.equals(hostIp, host)) {
                return new DefaultResponse(instance);
            }
        }
        return getRoundRobinInstance(instances);
    }

    /**
     * 使用隨機(jī)算法
     * 參考{link {@link org.springframework.cloud.loadbalancer.core.RandomLoadBalancer}}
     *
     * @param instances 實例
     * @return {@link Response }<{@link ServiceInstance }>
     * @author : lwq
     * @date : 2022-12-15 13:32:11
     */
    private Response<ServiceInstance> getRandomInstance(List<ServiceInstance> instances) {
        int index = ThreadLocalRandom.current().nextInt(instances.size());
        ServiceInstance instance = instances.get(index);
        return new DefaultResponse(instance);
    }

    /**
     * 使用RoundRobin機(jī)制獲取節(jié)點(diǎn)
     *
     * @param instances 實例
     * @return {@link Response }<{@link ServiceInstance }>
     * @author : lwq
     * @date : 2022-12-15 13:28:31
     */
    private Response<ServiceInstance> getRoundRobinInstance(List<ServiceInstance> instances) {
        // 每一次計數(shù)器都自動+1,實現(xiàn)輪詢的效果
        int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;
        ServiceInstance instance = instances.get(pos % instances.size());
        return new DefaultResponse(instance);
    }

}

? ? ? ? 其中的工具類如下:

import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Objects;
import javax.servlet.http.HttpServletRequest;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.URLUtil;
import org.springframework.http.HttpHeaders;

/**
 * 獲取IP方法
 */
public class IpUtils{

    /**
     * 獲取IP地址
     *
     * @return 本地IP地址
     */
    public static String getHostIp(){
        try{
            return InetAddress.getLocalHost().getHostAddress();
        }catch (UnknownHostException e){
        }
        return "127.0.0.1";
    }

    /**
     * 獲取客戶端IP
     *
     * @param httpHeaders 請求頭
     * @return IP地址
     */
    public static String getIpAddressFromHttpHeaders(HttpHeaders httpHeaders){
        if (httpHeaders == null){
            return "unknown";
        }
        //前端請求自定義請求頭,轉(zhuǎn)發(fā)到哪個服務(wù)
        List<String> ipList = httpHeaders.get("forward-to");
        String ip = CollectionUtil.get(ipList, 0);
        //請求自帶的請求頭
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ipList = httpHeaders.get("x-forwarded-for");
            ip = CollectionUtil.get(ipList, 0);
        }
        //從referer獲取請求的ip地址
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            //從referer中獲取請求的ip地址
            List<String> refererList = httpHeaders.get("referer");
            String referer = CollectionUtil.get(refererList, 0);
            URL url = URLUtil.url(referer);
            if (Objects.nonNull(url)){
                ip = url.getHost();
            }else {
                ip = "unknown";
            }
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ipList = httpHeaders.get("Proxy-Client-IP");
            ip = CollectionUtil.get(ipList, 0);
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ipList = httpHeaders.get("X-Forwarded-For");
            ip = CollectionUtil.get(ipList, 0);
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ipList = httpHeaders.get("WL-Proxy-Client-IP");
            ip = CollectionUtil.get(ipList, 0);
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ipList = httpHeaders.get("X-Real-IP");
            ip = CollectionUtil.get(ipList, 0);
        }
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip);
    }
}

????????getIpAddressFromHttpHeaders方法中,是從請求頭總拿到了自定義的請求頭forward-to,要想實現(xiàn)此功能,就需要前端發(fā)送請求的時候攜帶這個請求頭,例如

? ? ? ? 在若依的request.js中可以這么寫:?config.headers['forward-to'] = '192.168.0.145'

【SpringCloud系列】開發(fā)環(huán)境下重寫Loadbalancer實現(xiàn)自定義負(fù)載均衡,spring cloud,java,spring

三、配置負(fù)載均衡策略

? ? ? ?默認(rèn)的配置在這里:

org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientConfiguration

? ? ? ? 我們參考這個配置,實現(xiàn)自己的配置。啟用上面寫好的配置類LoadBalanceProperties,然后傳到自定義的負(fù)載均衡策略類CustomSpringCloudLoadBalancer,其他的復(fù)制就可以。

import com.ruoyi.common.loadbalance.core.CustomSpringCloudLoadBalancer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

/**
 * 自定義負(fù)載均衡客戶端配置
 *
 */
@SuppressWarnings("all")
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(LoadBalanceProperties.class)
public class CustomLoadBalanceClientConfiguration {

    @Bean
    @ConditionalOnBean(LoadBalancerClientFactory.class)
    public ReactorLoadBalancer<ServiceInstance> customLoadBalancer(LoadBalanceProperties loadBalanceProperties,
                                                                   Environment environment,
                                                                   LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new CustomSpringCloudLoadBalancer(name, loadBalanceProperties.getType(),
                loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class));
    }

}

? ? ? ? 然后使用LoadBalancerClients注解加載一下配置

import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;

/**
 * 自定義負(fù)載均衡自動配置
 *
 */
@LoadBalancerClients(defaultConfiguration = CustomLoadBalanceClientConfiguration.class)
public class CustomLoadBalanceAutoConfiguration {

}

四、使用

????????將以上代碼獨(dú)立成一個模塊,然后再其他微服務(wù)中的pom文件中引入,然后添加對應(yīng)的配置就可以實現(xiàn)自定義負(fù)載均衡了

? ? ? ? 1、在微服務(wù)中配置如下即可實現(xiàn)調(diào)用其他服務(wù)時,調(diào)用自己本地開發(fā)環(huán)境的微服務(wù)

? ??spring.cloud.loadbalancer.type=dev

? ? ? ? 2、在網(wǎng)關(guān)中配置如下即可實現(xiàn)調(diào)用固定某個服務(wù)

?spring.cloud.loadbalancer.type=gateway

寫在最后的話

? ? ? ? 最開始只有想法,但是不知道怎么實現(xiàn),百度也沒找到合適的方案。所以就開始看源碼,研究了一下,然后照著源碼寫,測試了一下真的就實現(xiàn)了。所以,多看看源碼還是有好處的。文章來源地址http://www.zghlxwxcb.cn/news/detail-727356.html

到了這里,關(guān)于【SpringCloud系列】開發(fā)環(huán)境下重寫Loadbalancer實現(xiàn)自定義負(fù)載均衡的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包