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

springcloud+nacos實(shí)現(xiàn)灰度發(fā)布

這篇具有很好參考價(jià)值的文章主要介紹了springcloud+nacos實(shí)現(xiàn)灰度發(fā)布。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

灰度發(fā)布

gateway網(wǎng)關(guān)實(shí)現(xiàn)灰度路由

灰度發(fā)布實(shí)體

package com.scm.boss.common.bean;

import lombok.Data;
import lombok.experimental.Accessors;

import java.io.Serializable;

/**
 * 灰度發(fā)布實(shí)體
 */
@Data
@Accessors(chain = true)
public class GrayBean implements Serializable {

    private static final long serialVersionUID = 1L;


    /**
     * 版本
     */
    private String preVersion;
}

灰度發(fā)布上下文信息

package com.scm.boss.common.utils;

import com.scm.boss.common.bean.GrayBean;

/**
 * 灰度信息上下文
 */
public class CurrentGrayUtils {

    private final static InheritableThreadLocal<GrayBean> CURRENT_GRE = new InheritableThreadLocal<>();


    public static GrayBean getGray() {
        GrayBean grayBean = CURRENT_GRE.get();
        return grayBean;
    }

    public static void setGray(GrayBean grayBean) {
        if(grayBean == null){
            clear();
        }else {
            CURRENT_GRE.set(grayBean);
        }
    }

    public static void clear() {
        CURRENT_GRE.remove();
    }

}

灰度過濾器設(shè)置灰度上下文信息

package com.scm.gateway.common.config;

import com.scm.boss.common.bean.GrayBean;
import com.scm.boss.common.constants.CommonConstants;
import com.scm.boss.common.utils.CurrentGrayUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.web.server.ServerWebExchange;
 
import reactor.core.publisher.Mono;

/**
 * 灰度發(fā)布版本標(biāo)識(shí)過濾器
 */
@Slf4j
public class GrayFilter implements GlobalFilter, Ordered {
 
	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		HttpHeaders httpHeaders = exchange.getRequest().getHeaders();
		String grayVersion = httpHeaders.getFirst(CommonConstants.GRAY_VERSION);
		if (StringUtils.isNotBlank(grayVersion)) {
			GrayBean grayBean = new GrayBean();
			grayBean.setPreVersion(grayVersion);
			CurrentGrayUtils.setGray(grayBean);
			//請(qǐng)求頭添加灰度版本號(hào),用于灰度請(qǐng)求
			exchange.getRequest().mutate()
					.header(CommonConstants.GRAY_VERSION, grayVersion)
					.build();
		}
		return chain.filter(exchange);
	}

	@Override
	public int getOrder() {
		return Integer.MIN_VALUE;
	}
}

灰度路由規(guī)則

package com.scm.gateway.common.config;

import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.google.common.base.Optional;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ZoneAvoidanceRule;
import com.scm.boss.common.bean.GrayBean;
import com.scm.boss.common.constants.CommonConstants;
import com.scm.boss.common.exception.ApiException;
import com.scm.boss.common.utils.CurrentGrayUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
public class GateWayGrayRouteRule extends ZoneAvoidanceRule {

    @Override
    public Server choose(Object key) {
        Optional<Server> server;
        try {
            // 根據(jù)灰度路由規(guī)則,過濾出符合規(guī)則的服務(wù) this.getServers()
            // 再根據(jù)負(fù)載均衡策略,過濾掉不可用和性能差的服務(wù),然后在剩下的服務(wù)中進(jìn)行輪詢  getPredicate().chooseRoundRobinAfterFiltering()
            server = getPredicate()
                    .chooseRoundRobinAfterFiltering(this.getServers(), key);
            //獲取請(qǐng)求頭中的版本號(hào)
            GrayBean grayBean = CurrentGrayUtils.getGray();
            if (null != grayBean && !StringUtils.isEmpty(grayBean.getPreVersion())) {
                log.info("灰度路由規(guī)則過濾后的服務(wù)實(shí)例:{}", server.isPresent() ? server.get().getHostPort() : null);
            }
        } finally {
            CurrentGrayUtils.clear();
        }

        return server.isPresent() ? server.get() : null;
    }

    /**
     * 灰度路由過濾服務(wù)實(shí)例
     *
     * 如果設(shè)置了期望版本, 則過濾出所有的期望版本 ,然后再走默認(rèn)的輪詢 如果沒有一個(gè)期望的版本實(shí)例,則不過濾,降級(jí)為原有的規(guī)則,進(jìn)行所有的服務(wù)輪詢。(灰度路由失效) 如果沒有設(shè)置期望版本
     * 則不走灰度路由,按原有輪詢機(jī)制輪詢所有
     */
    protected List<Server> getServers() {
        // 獲取spring cloud默認(rèn)負(fù)載均衡器
        // 獲取所有待選的服務(wù)
        List<Server> allServers = getLoadBalancer().getReachableServers();
        if (CollectionUtils.isEmpty(allServers)) {
            log.error("沒有可用的服務(wù)實(shí)例");
            throw new ApiException("沒有可用的服務(wù)實(shí)例");
        }
        //獲取請(qǐng)求頭中的版本號(hào)
        GrayBean grayBean = CurrentGrayUtils.getGray();
        // 如果沒有設(shè)置要訪問的版本,則不過濾,返回所有,走原有默認(rèn)的輪詢機(jī)制
        if (null == grayBean || StringUtils.isEmpty(grayBean.getPreVersion())
                || !CommonConstants.GRAY_VERSION_VALUE.equals(grayBean.getPreVersion())) {
            //這里需要過濾掉灰度服務(wù)實(shí)例
            List<Server> list = allServers.stream().filter(f -> {
                // 獲取服務(wù)實(shí)例在注冊(cè)中心上的元數(shù)據(jù)
                Map<String, String> metadata = ((NacosServer) f).getMetadata();
                // 如果注冊(cè)中心上服務(wù)的版本標(biāo)簽和期望訪問的版本一致,則灰度路由匹配成功
                if (null != metadata && StringUtils.isNotBlank(metadata.get(CommonConstants.GRAY_VERSION))
                        && CommonConstants.GRAY_VERSION_VALUE.equals(metadata.get(CommonConstants.GRAY_VERSION))) {
                    return false;
                }
                return true;
            }).collect(Collectors.toList());

            return list;
        }

        // 開始灰度規(guī)則匹配過濾
        List<Server> filterServer = new ArrayList<>();
        for (Server server : allServers) {
            // 獲取服務(wù)實(shí)例在注冊(cè)中心上的元數(shù)據(jù)
            Map<String, String> metadata = ((NacosServer) server).getMetadata();
            // 如果注冊(cè)中心上服務(wù)的版本標(biāo)簽和期望訪問的版本一致,則灰度路由匹配成功
            if (null != metadata && grayBean.getPreVersion().equals(metadata.get(CommonConstants.GRAY_VERSION))) {
                filterServer.add(server);
            }
        }
        // 如果沒有匹配到期望的版本實(shí)例服務(wù),為了保證服務(wù)可用性,讓灰度規(guī)則失效,走原有的輪詢所有可用服務(wù)的機(jī)制
        if (CollectionUtils.isEmpty(filterServer)) {
            log.error("灰度路由規(guī)則失效,沒有找到期望的版本實(shí)例");
            throw new ApiException("灰度路由規(guī)則失效,沒有找到期望的版本實(shí)例");
        }
        return filterServer;
    }
}

gateway網(wǎng)關(guān)需要引入的pom

 <dependencies>
        <!-- Nacos注冊(cè)中心 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- Nacos配置中心 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

        <!-- gateway -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.scm</groupId>
            <artifactId>scm-common-boss</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-netflix-ribbon</artifactId>
        </dependency>
    </dependencies>

常量

package com.scm.boss.common.constants;

public interface CommonConstants {
	 /**
	     * 灰度請(qǐng)求頭參數(shù)
	     */
	    String GRAY_VERSION = "grayVersion";
	    /**
	     * 灰度版本值
	     */
	    String GRAY_VERSION_VALUE = "V1";
    }

微服務(wù)feign調(diào)用灰度

服務(wù)路由規(guī)則

package com.scm.cloud.config;

import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.google.common.base.Optional;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ZoneAvoidanceRule;
import com.scm.boss.common.bean.GrayBean;
import com.scm.boss.common.constants.CommonConstants;
import com.scm.boss.common.exception.ApiException;
import com.scm.boss.common.utils.CurrentGrayUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.context.annotation.Scope;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
/**
 * 灰度路由規(guī)則,需要是多例每一個(gè)服務(wù)單獨(dú)一個(gè)實(shí)例,否則會(huì)出現(xiàn)服務(wù)亂調(diào)用問題
 */
@Scope("prototype")
public class GrayRouteRule extends ZoneAvoidanceRule {

    @Override
    public Server choose(Object key) {
        // 根據(jù)灰度路由規(guī)則,過濾出符合規(guī)則的服務(wù) this.getServers()
        // 再根據(jù)負(fù)載均衡策略,過濾掉不可用和性能差的服務(wù),然后在剩下的服務(wù)中進(jìn)行輪詢  getPredicate().chooseRoundRobinAfterFiltering()
        Optional<Server> server = getPredicate()
                .chooseRoundRobinAfterFiltering(this.getServers(), key);
        //獲取請(qǐng)求頭中的版本號(hào)
        GrayBean grayBean = CurrentGrayUtils.getGray();
        if (null != grayBean && !StringUtils.isEmpty(grayBean.getPreVersion())) {
            log.info("灰度路由規(guī)則過濾后的服務(wù)實(shí)例:{}", server.isPresent() ? server.get().getHostPort() : null);
        }
        return server.isPresent() ? server.get() : null;
    }

    /**
     * 灰度路由過濾服務(wù)實(shí)例
     *
     * 如果設(shè)置了期望版本, 則過濾出所有的期望版本 ,然后再走默認(rèn)的輪詢 如果沒有一個(gè)期望的版本實(shí)例,則不過濾,降級(jí)為原有的規(guī)則,進(jìn)行所有的服務(wù)輪詢。(灰度路由失效) 如果沒有設(shè)置期望版本
     * 則不走灰度路由,按原有輪詢機(jī)制輪詢所有
     */
    protected List<Server> getServers() {
        // 獲取spring cloud默認(rèn)負(fù)載均衡器
        // 獲取所有待選的服務(wù)
        List<Server> allServers = getLoadBalancer().getReachableServers();
        if (CollectionUtils.isEmpty(allServers)) {
            log.error("沒有可用的服務(wù)實(shí)例");
            throw new ApiException("沒有可用的服務(wù)實(shí)例");
        }

        //獲取請(qǐng)求頭中的版本號(hào)
        GrayBean grayBean = CurrentGrayUtils.getGray();
        // 如果沒有設(shè)置要訪問的版本,則不過濾,返回所有,走原有默認(rèn)的輪詢機(jī)制
        if (null == grayBean || StringUtils.isEmpty(grayBean.getPreVersion())
                || !CommonConstants.GRAY_VERSION_VALUE.equals(grayBean.getPreVersion())) {
            //這里需要過濾掉灰度服務(wù)實(shí)例
            List<Server> list = allServers.stream().filter(f -> {
                // 獲取服務(wù)實(shí)例在注冊(cè)中心上的元數(shù)據(jù)
                Map<String, String> metadata = ((NacosServer) f).getMetadata();
                // 如果注冊(cè)中心上服務(wù)的版本標(biāo)簽和期望訪問的版本一致,則灰度路由匹配成功
                if (null != metadata && StringUtils.isNotBlank(metadata.get(CommonConstants.GRAY_VERSION))
                        && CommonConstants.GRAY_VERSION_VALUE.equals(metadata.get(CommonConstants.GRAY_VERSION))) {
                    return false;
                }
                return true;
            }).collect(Collectors.toList());
            return list;
        }

        // 開始灰度規(guī)則匹配過濾
        List<Server> filterServer = new ArrayList<>();
        for (Server server : allServers) {
            // 獲取服務(wù)實(shí)例在注冊(cè)中心上的元數(shù)據(jù)
            Map<String, String> metadata = ((NacosServer) server).getMetadata();
            // 如果注冊(cè)中心上服務(wù)的版本標(biāo)簽和期望訪問的版本一致,則灰度路由匹配成功
            if (null != metadata && grayBean.getPreVersion().equals(metadata.get(CommonConstants.GRAY_VERSION))) {
                filterServer.add(server);
            }
        }
        // 如果沒有匹配到期望的版本實(shí)例服務(wù),為了保證服務(wù)可用性,讓灰度規(guī)則失效,走原有的輪詢所有可用服務(wù)的機(jī)制
        if (CollectionUtils.isEmpty(filterServer)) {
            log.error("灰度路由規(guī)則失效,沒有找到期望的版本實(shí)例,version={}", grayBean.getPreVersion());
            throw new ApiException("灰度路由規(guī)則失效,沒有找到期望的版本實(shí)例");
        }
        return filterServer;
    }
}

需要傳遞灰度版本號(hào),所以需要把灰度版本請(qǐng)求參數(shù)傳遞下去,以及解決Hystrix的線程切換導(dǎo)致參數(shù)無法傳遞下的問題

使用TransmittableThreadLocal可以跨線程傳遞

package com.scm.cloud.config;

import com.scm.cloud.security.DefaultSecurityInterceptor;
import com.scm.cloud.security.SecurityInterceptor;
import com.scm.cloud.webmvc.WebMvcCommonConfigurer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.PostConstruct;

/**
 * 配置
 * @date 2023/7/13 18:12
 * @author luohao
 */
@Configuration
@Slf4j
public class CommonConfiguration {

    /**
     * 低優(yōu)先級(jí)
     */
    private final static int LOWER_PRECEDENCE = 10000;

    /**
     * 使用TransmittableThreadLocal可以跨線程傳遞
     */
    @PostConstruct
    public void init(){
        new GlobalHystrixConcurrencyStrategy();
    }


    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcCommonConfigurer();
    }

    /**
     * 優(yōu)先級(jí)
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    @Order(value = LOWER_PRECEDENCE)
    public SecurityInterceptor securityInterceptor(){
        return new DefaultSecurityInterceptor();
    }

}

bean重復(fù)則覆蓋

package com.scm.cloud.config;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;

/**
 * @author xiewu
 * @date 2022/12/29 10:41
 */
public class EnvironmentPostProcessorConfig implements EnvironmentPostProcessor {

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        application.setAllowBeanDefinitionOverriding(true);
    }
}

feign調(diào)用攔截器

package com.scm.cloud.config;

import com.scm.boss.common.bean.CurrentUserBean;
import com.scm.boss.common.bean.DealerApiDetailBean;
import com.scm.boss.common.bean.GrayBean;
import com.scm.boss.common.constants.CommonConstants;
import com.scm.boss.common.utils.CurrentGrayUtils;
import com.scm.boss.common.utils.CurrentUserUtils;
import com.scm.boss.common.utils.CurrentDealerApiDetailUtils;
import feign.Feign;
import feign.Logger;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;


@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@Slf4j
@Configuration
public class FeignConfig {

    @Bean
    public RequestInterceptor requestInterceptor() {
        return new RequestInterceptor() {
            @Override
            public void apply(RequestTemplate requestTemplate) {
                GrayBean grayBean = CurrentGrayUtils.getGray();
                if (null != grayBean) {
                    requestTemplate.header(CommonConstants.GRAY_VERSION, grayBean.getPreVersion());
                }

                DealerApiDetailBean dealerApiDetailBean = CurrentDealerApiDetailUtils.getDealerApiConditionNull();
                if (dealerApiDetailBean != null){
                    requestTemplate.header(CommonConstants.DEALER_ID, dealerApiDetailBean.getDealerId());
                    requestTemplate.header(CommonConstants.DEALER_PROJECT_ID, dealerApiDetailBean.getDealerProjectId());
                }
                CurrentUserBean currentUser = CurrentUserUtils.getCurrentUserConditionNull();
                if (currentUser == null){
                    return;
                }
                requestTemplate.header(CommonConstants.SUPPLIER_ID, currentUser.getSupplierId() == null ? null : currentUser.getId().toString());
                requestTemplate.header(CommonConstants.ACCOUNT_NO, currentUser.getAccountNo());
                requestTemplate.header(CommonConstants.REQUEST_SOURCE, currentUser.getType());
                requestTemplate.header(CommonConstants.ID, currentUser.getId() == null ? null : currentUser.getId().toString());
            }
        };
    }


    /**
     * Feign 客戶端的日志記錄,默認(rèn)級(jí)別為NONE
     * Logger.Level 的具體級(jí)別如下:
     * NONE:不記錄任何信息
     * BASIC:僅記錄請(qǐng)求方法、URL以及響應(yīng)狀態(tài)碼和執(zhí)行時(shí)間
     * HEADERS:除了記錄 BASIC級(jí)別的信息外,還會(huì)記錄請(qǐng)求和響應(yīng)的頭信息
     * FULL:記錄所有請(qǐng)求與響應(yīng)的明細(xì),包括頭信息、請(qǐng)求體、元數(shù)據(jù)
     */
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }

    /**
     * Feign支持文件上傳
     *
     * @param messageConverters
     * @return
     */
    @Bean
    @Primary
    @Scope("prototype")
    public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
        return new SpringFormEncoder(new SpringEncoder(messageConverters));
    }
}

Hystrix并發(fā)策略

package com.scm.cloud.config;

import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
import com.scm.boss.common.bean.CurrentUserBean;
import com.scm.boss.common.bean.DealerApiDetailBean;
import com.scm.boss.common.bean.GrayBean;
import com.scm.boss.common.utils.CurrentGrayUtils;
import com.scm.boss.common.utils.CurrentUserUtils;
import com.scm.boss.common.utils.CurrentDealerApiDetailUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Callable;

@Slf4j
public class GlobalHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {

    private HystrixConcurrencyStrategy delegate;

    public GlobalHystrixConcurrencyStrategy() {
        this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
        if (this.delegate instanceof GlobalHystrixConcurrencyStrategy) {
            return;
        }
        HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
        HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy();
        HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance().getCommandExecutionHook();
        HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();

        HystrixPlugins.reset();

        HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
        // Registers existing plugins except the new MicroMeter Strategy plugin.
        HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
        HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
        HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
        HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
        log.info("Construct HystrixConcurrencyStrategy:[{}] for application,",GlobalHystrixConcurrencyStrategy.class.getName());
    }

    @Override
    public <T> Callable<T> wrapCallable(Callable<T> callable) {
        final CurrentUserBean user = CurrentUserUtils.getCurrentUserConditionNull();
        final DealerApiDetailBean dealerApiDetailBean = CurrentDealerApiDetailUtils.getDealerApiConditionNull();
        final GrayBean grayBean = CurrentGrayUtils.getGray();

        if (callable instanceof HeaderCallable) {
            return callable;
        }
        Callable<T> wrappedCallable = this.delegate != null
                ? this.delegate.wrapCallable(callable) : callable;
        if (wrappedCallable instanceof HeaderCallable) {
            return wrappedCallable;
        }
        return new HeaderCallable<T>(wrappedCallable,user,dealerApiDetailBean, grayBean);
    }
}

Hystrix并發(fā)參數(shù)線程中傳遞參數(shù)

package com.scm.cloud.config;

import com.scm.boss.common.bean.CurrentUserBean;
import com.scm.boss.common.bean.DealerApiDetailBean;
import com.scm.boss.common.bean.GrayBean;
import com.scm.boss.common.utils.CurrentGrayUtils;
import com.scm.boss.common.utils.CurrentUserUtils;
import com.scm.boss.common.utils.CurrentDealerApiDetailUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Callable;

@Slf4j
public class HeaderCallable<V> implements Callable<V> {

    private final Callable<V> delegate;
    private final CurrentUserBean currentUserBean;
    private final DealerApiDetailBean dealerApiDetailBean;
    private final GrayBean grayBean;

    public HeaderCallable(Callable<V> delegate, CurrentUserBean currentUserBean, DealerApiDetailBean dealerApiDetailBean, GrayBean grayBean) {
        this.delegate = delegate;
        this.currentUserBean = currentUserBean;
        this.dealerApiDetailBean = dealerApiDetailBean;
        this.grayBean = grayBean;
    }

    @Override
    public V call() throws Exception {
        try {
            CurrentUserUtils.setCurrentUser(currentUserBean);
            CurrentDealerApiDetailUtils.setDealerApi(dealerApiDetailBean);
            CurrentGrayUtils.setGray(grayBean);
            return this.delegate.call();
        } catch (Exception e) {
            //這里無法抓取到delegate.call()方法的異常,因?yàn)槭蔷€程池異步請(qǐng)求的
            throw e;
        } finally {
            CurrentUserUtils.clear();
            CurrentGrayUtils.clear();
            CurrentDealerApiDetailUtils.clear();
        }
    }
}

LoadBalancerFeignClient

package com.scm.cloud.config;

import feign.Client;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class PersonBeanConfiguration {

    /**
     * 創(chuàng)建FeignClient
     */
    @Bean
    @ConditionalOnMissingBean
    public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
                              SpringClientFactory clientFactory) {
        return new LoadBalancerFeignClient(new Client.Default(null, null),
                cachingFactory, clientFactory);
    }
}

攔截器HandlerInterceptor

package com.scm.cloud.webmvc;

import com.alibaba.fastjson.JSONArray;
import com.scm.boss.common.bean.CurrentUserBean;
import com.scm.boss.common.bean.DealerApiDetailBean;
import com.scm.boss.common.bean.GrayBean;
import com.scm.boss.common.bean.RouteAttrPermVO;
import com.scm.boss.common.constants.CommonConstants;
import com.scm.boss.common.constants.PlatformTypeEnum;
import com.scm.boss.common.constants.UserTypeEnum;
import com.scm.boss.common.utils.CurrentDealerApiDetailUtils;
import com.scm.boss.common.utils.CurrentGrayUtils;
import com.scm.boss.common.utils.CurrentUserUtils;
import com.scm.boss.common.utils.FieldListUtils;
import com.scm.redis.template.RedisRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 攔截器
 * @date 2023/7/13 18:09
 * @author luohao
 */
@Slf4j
public class GlobalHandlerInterceptor implements HandlerInterceptor {

    private RedisRepository redisRepository;

    public GlobalHandlerInterceptor(RedisRepository redisRepository) {
        this.redisRepository = redisRepository;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
    {
        extractedHeadersGre(request);
        extractedHeaders(request);
        extractedHeadersApi(request);
        extractedPermissionFields(request);
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }

    /**
     * 灰度發(fā)布
     * @param request
     */
    private void extractedHeadersGre(HttpServletRequest request) {
        String grayVersion = request.getHeader(CommonConstants.GRAY_VERSION);
        if (StringUtils.isNotBlank(grayVersion)) {
            GrayBean grayBean = new GrayBean();
            grayBean.setPreVersion(grayVersion);
            CurrentGrayUtils.setGray(grayBean);
        }
    }

    /**
     * 第三方經(jīng)銷商調(diào)用
     * @param request
     */
    private void extractedHeadersApi(HttpServletRequest request) {
        DealerApiDetailBean dealerApiDetailBean = new DealerApiDetailBean();
        dealerApiDetailBean.setDealerId(request.getHeader(CommonConstants.DEALER_ID))
                .setDealerProjectId(request.getHeader(CommonConstants.DEALER_PROJECT_ID));
        CurrentDealerApiDetailUtils.setDealerApi(dealerApiDetailBean);
    }

    private void extractedHeaders(HttpServletRequest request) {
        CurrentUserBean currentUserBean = new CurrentUserBean();
        currentUserBean.setAccountNo(request.getHeader(CommonConstants.ACCOUNT_NO));
        currentUserBean.setType(request.getHeader(CommonConstants.REQUEST_SOURCE));
        currentUserBean.setStatus(request.getHeader(CommonConstants.STATUS) == null ? null : Integer.valueOf(request.getHeader(CommonConstants.STATUS)));
        currentUserBean.setId(request.getHeader(CommonConstants.ID) == null ? null : Integer.valueOf(request.getHeader(CommonConstants.ID)));
        if (UserTypeEnum.SUPPLIER_USER.getCode().equals(currentUserBean.getType())) {
            currentUserBean.setSupplierId(request.getHeader(CommonConstants.SUPPLIER_ID) == null ? null : Integer.valueOf(request.getHeader(CommonConstants.SUPPLIER_ID)));
        }
        CurrentUserUtils.setCurrentUser(currentUserBean);
    }

    /**
     * 獲取接口無權(quán)限字段
     * @date 2023/7/13 16:41
     * @author luohao
     */
    private void extractedPermissionFields(HttpServletRequest request){
        String requestMapping = request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE).toString();
        CurrentUserBean currentUser = CurrentUserUtils.getCurrentUser();
        if(Objects.isNull(currentUser) || Objects.isNull(currentUser.getAccountNo())){
            return;
        }
        String key;
        if(currentUser.getType().equals(PlatformTypeEnum.APPLY_CHAIN.getCode().toString())){
            key = CommonConstants.SUPPLY_CHAIN_ATTR;
        }else if(currentUser.getType().equals(PlatformTypeEnum.DEALER.getCode().toString())){
            key = CommonConstants.DEALER_ATTR;
        }else{
            return;
        }
        String redisKey = new StringBuilder(key).append(currentUser.getAccountNo()).toString();
        List<RouteAttrPermVO> spuEditDTO = JSONArray.parseArray(redisRepository.get(redisKey), RouteAttrPermVO.class);
        if(CollectionUtils.isEmpty(spuEditDTO)){
            return;
        }
        List<String> nonPermAttrs = spuEditDTO.stream().filter(i -> i.getUrl().equals(requestMapping)).map(RouteAttrPermVO::getAttrName).collect(Collectors.toList());
        FieldListUtils.setFieldList(nonPermAttrs);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        CurrentUserUtils.clear();
        FieldListUtils.clear();
    }


}

WebMvcConfigurer

package com.scm.cloud.webmvc;

import com.scm.redis.template.RedisRepository;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

/**
 * WebMvc
 * @date 2023/7/13 18:11
 * @author luohao
 */
public class WebMvcCommonConfigurer implements WebMvcConfigurer {

    @Resource
    private RedisRepository redisRepository;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new GlobalHandlerInterceptor(redisRepository)).addPathPatterns("/**").excludePathPatterns("/info","/actuator/**");
    }
}

特殊數(shù)據(jù)權(quán)限過濾

package com.scm.cloud.webmvc;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.ObjectSerializer;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializeWriter;
import com.scm.boss.common.utils.FieldListUtils;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.io.IOException;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 特殊數(shù)據(jù)權(quán)限過濾
 * @date 2023/7/12 14:54
 * @author luohao
 */
@Component
@RestControllerAdvice
public class BaseGlobalResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(final Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if(ObjectUtils.isEmpty(body)){
            return body;
        }
        List<String> fieldList = FieldListUtils.getFieldList();
        if(CollectionUtils.isEmpty(fieldList)){
            return body;
        }
        SerializeConfig config = new SerializeConfig();
        config.put( Date.class, new DateJsonSerializer());
        return objectEval(JSONObject.parseObject(JSON.toJSONString(body,config)), fieldList);
    }

    /**
     * 權(quán)限數(shù)據(jù)處理
     * @param body
     * @param nonPermAttrs
     * @return
     */
    public Object objectEval(Object body, List<String> nonPermAttrs) {
        if (Objects.nonNull(body) && body instanceof Map) {
            Map<String, Object> map = (Map<String, Object>) body;
            map.keySet().forEach(key -> {
                Object o = map.get(key);
                if (Objects.nonNull(o) && o instanceof Map) {
                    map.put(key, objectEval(o, nonPermAttrs));
                } else if (Objects.nonNull(o) && o instanceof List){
                    map.put(key, objectEval(o, nonPermAttrs));
                }else {
                    List<String> collect = nonPermAttrs.stream().filter(i -> i.equals(key)).collect(Collectors.toList());
                    if (CollectionUtils.isNotEmpty(collect)){
                        map.put(key, null);
                    }
                }
            });
        } else if (Objects.nonNull(body) && body instanceof List) {
            final List<Object> dataList = (List<Object>) body;
            dataList.forEach(i -> objectEval(i,nonPermAttrs));
        }
        return body;
    }
}

class DateJsonSerializer implements ObjectSerializer {

    @Override
    public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
        SerializeWriter out = serializer.getWriter();
        if (object == null) {
            serializer.getWriter().writeNull();
            return;
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sdf.setTimeZone( TimeZone.getTimeZone("Etc/GMT-8"));
        out.write("\"" + sdf.format( (Date) object ) + "\"");
    }
}

微服務(wù)的spring.factories配置

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.scm.cloud.config.FeignConfig,\
com.scm.cloud.config.PersonBeanConfiguration,\
com.scm.cloud.webmvc.BaseGlobalResponseBodyAdvice,\
com.scm.cloud.config.CommonConfiguration,\
com.scm.cloud.config.GrayRouteRule
org.springframework.boot.env.EnvironmentPostProcessor = com.scm.cloud.config.EnvironmentPostProcessorConfig

微服務(wù)的pom文件

<dependencies>
        <!-- Nacos注冊(cè)中心 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- Nacos配置中心 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <!-- feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>com.scm</groupId>
            <artifactId>scm-starter-redis</artifactId>
            <version>${project.version}</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

文章來源地址http://www.zghlxwxcb.cn/news/detail-651159.html

到了這里,關(guān)于springcloud+nacos實(shí)現(xiàn)灰度發(fā)布的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

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

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

相關(guān)文章

  • 【springcloud 微服務(wù)】Spring Cloud Alibaba Nacos使用詳解

    目錄 一、前言 二、nacos介紹 2.1??什么是 Nacos 2.2 nacos 核心能力 2.2.1 服務(wù)發(fā)現(xiàn)和服務(wù)健康監(jiān)測

    2024年01月22日
    瀏覽(26)
  • SpringCloud全鏈路灰度發(fā)布

    SpringCloud全鏈路灰度發(fā)布

    日升時(shí)奮斗,日落時(shí)自省? 目錄 1、實(shí)現(xiàn)框架 2、負(fù)載均衡模塊 3、封裝負(fù)載均衡器 4、網(wǎng)關(guān)模塊 5、服務(wù)模塊 5.1、注冊(cè)為灰度服務(wù)實(shí)例 5.2、設(shè)置負(fù)載均衡器 5.3、傳遞灰度標(biāo)簽 Spring Cloud全鏈路灰色發(fā)布實(shí)現(xiàn)構(gòu)架: 灰度發(fā)布的具體實(shí)現(xiàn)?: 前端程序在灰度測試用戶Header頭中打上

    2024年01月21日
    瀏覽(27)
  • 基于Spring Gateway路由判斷器實(shí)現(xiàn)各種灰度發(fā)布場景

    基于Spring Gateway路由判斷器實(shí)現(xiàn)各種灰度發(fā)布場景

    以前使用APISIX實(shí)現(xiàn)過灰度發(fā)布《jenkins與apisix整合,實(shí)現(xiàn)自動(dòng)化部署與負(fù)載均衡、灰度發(fā)布(藍(lán)綠發(fā)布)》 同樣可以使用Spring Gateway實(shí)現(xiàn)類似灰度功能。本文使用前文的示例代碼《Spring Cloud 2022.x版本使用gateway和nacos實(shí)現(xiàn)動(dòng)態(tài)路由和負(fù)載均衡》來演示效果 app1和app2兩個(gè)工程都增

    2024年02月10日
    瀏覽(16)
  • Spring Cloud集成Nacos實(shí)現(xiàn)本地配置優(yōu)先 | Spring Cloud 9

    Nacos Config Starter 實(shí)現(xiàn)了 org.springframework.cloud.bootstrap.config.PropertySourceLocator 接口,并將優(yōu)先級(jí)設(shè)置為最高。 在 Spring Cloud 應(yīng)用啟動(dòng)階段,會(huì)主動(dòng)從 Nacos Server 端獲取對(duì)應(yīng)的數(shù)據(jù),并將獲取到的數(shù)據(jù)轉(zhuǎn)換成 PropertySource 且注入到 Environment 的 PropertySources 屬性中,所以使用 @Value 注解

    2024年02月15日
    瀏覽(29)
  • Spring Cloud集成Nacos實(shí)現(xiàn)服務(wù)配置中心 | Spring Cloud 7

    先我們來看一下,微服務(wù)架構(gòu)下關(guān)于配置文件的一些問題: 配置文件相對(duì)分散。在一個(gè)微服務(wù)架構(gòu)下,配置文件會(huì)隨著微服務(wù)的增多變的越來越多,而且分散在各個(gè)微服務(wù)中,不好統(tǒng)一配置和管理。 配置文件無法區(qū)分環(huán)境,開發(fā)環(huán)境、測試環(huán)境、線上環(huán)境。微服務(wù)項(xiàng)目可能會(huì)

    2024年02月14日
    瀏覽(88)
  • Spring Cloud Gateway集成Nacos實(shí)現(xiàn)負(fù)載均衡

    Spring Cloud Gateway集成Nacos實(shí)現(xiàn)負(fù)載均衡

    ??Nacas可以用于實(shí)現(xiàn)Spring Cloud Gateway中網(wǎng)關(guān)動(dòng)態(tài)路由功能,也可以基于Nacos來實(shí)現(xiàn)對(duì)后端服務(wù)的負(fù)載均衡,前者利用Nacos配置中心功能,后者利用Nacos服務(wù)注冊(cè)功能。 接下來我們來看下Gateway集成Nacos實(shí)現(xiàn)負(fù)載均衡的架構(gòu)圖 一. 環(huán)境準(zhǔn)備 1. 版本環(huán)境 Jdk: java.version1.8/java.version Spr

    2024年02月10日
    瀏覽(98)
  • Java之SpringCloud Alibaba【七】【Spring Cloud微服務(wù)網(wǎng)關(guān)Gateway組件】

    Java之SpringCloud Alibaba【七】【Spring Cloud微服務(wù)網(wǎng)關(guān)Gateway組件】

    Java之SpringCloud Alibaba【一】【Nacos一篇文章精通系列】 跳轉(zhuǎn) Java之SpringCloud Alibaba【二】【微服務(wù)調(diào)用組件Feign】 跳轉(zhuǎn) Java之SpringCloud Alibaba【三】【微服務(wù)Nacos-config配置中心】 跳轉(zhuǎn) Java之SpringCloud Alibaba【四】【微服務(wù) Sentinel服務(wù)熔斷】 跳轉(zhuǎn) Java之SpringCloud Alibaba【五】【微服務(wù)

    2024年02月06日
    瀏覽(34)
  • Spring Cloud Gateway + Nacos 實(shí)現(xiàn)服務(wù)上下線無縫切換

    Spring Cloud Gateway + Nacos 實(shí)現(xiàn)服務(wù)上下線無縫切換

    大家好,我是不才陳某~ 最近知識(shí)星球的球友在學(xué)習(xí)星球中的《精盡Spring Cloud Alibaba》專欄提到一個(gè)問題,相信也有很多人在線上環(huán)境遇到過,或許也因此被批過:一個(gè)集群中有某個(gè)服務(wù)突然下線,但是網(wǎng)關(guān)還是會(huì)去請(qǐng)求這個(gè)實(shí)例,所以線上就報(bào)錯(cuò)了,報(bào)錯(cuò)信息如下圖: 究其

    2024年02月15日
    瀏覽(15)
  • Spring Cloud Sentinel整合Nacos實(shí)現(xiàn)配置持久化

    Spring Cloud Sentinel整合Nacos實(shí)現(xiàn)配置持久化

    sentinel配置相關(guān)配置后無法持久化,服務(wù)重啟之后就沒了,所以整合nacos,在nacos服務(wù)持久化,sentinel實(shí)時(shí)與nacos通信獲取相關(guān)配置。 使用上一章節(jié)Feign消費(fèi)者服務(wù)實(shí)現(xiàn)整合。 版本信息: nacos:1.4.1 Sentinel 控制臺(tái) 1.7.2 spring-boot:2.3.3.RELEASE spring.cloud.version:Hoxton.SR8 spring.cloud.alibaba.v

    2024年02月08日
    瀏覽(23)
  • 【SpringCloud】11、Spring Cloud Gateway使用Sentinel實(shí)現(xiàn)服務(wù)限流

    1、關(guān)于 Sentinel Sentinel 是阿里巴巴開源的一個(gè)流量防衛(wèi)防護(hù)組件,可以為微服務(wù)架構(gòu)提供強(qiáng)大的流量防衛(wèi)能力,包括流量控制、熔斷降級(jí)等功能。Spring Cloud Gateway 與 Sentinel 結(jié)合,可以實(shí)現(xiàn)強(qiáng)大的限流功能。 Sentinel 具有以下特性: 豐富的應(yīng)用場景:Sentinel 承接了阿里巴巴近

    2024年02月01日
    瀏覽(23)

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包