springcloud feign定製負載均衡

背景:開發,測試環境都使用了一個註冊中心,所以在開發,測試環境不想進行負載均衡,原因1:自己本地調試方便,原因2:不想測試環境負載到自己的機器上(代碼不一致)

 

註冊中心使用nacos

實現原理:使用ThreadLocal保存本機唯一信息,在進行負載時,從多個服務取相關信息和本機信息進行對比,如果一致就使用該服務進行調用,也就確保了所有請求都是在同一個機器上進行

 

1:feign層定製負載均衡

    

//ThreadLocal
public class RuleThreadLocal {

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(){
        @Override
        protected String initialValue() {
            return "";
        }
    };

    public static void setRule(String type){
        contextHolder.set(type);
    }

    public static String getRule(){
        return contextHolder.get();
    }

    /**
     * 清除上下文數據
     */
    public static void clearRule() {
        contextHolder.remove();
    }
}
 

  

// 自定義負載策略
@Slf4j
public class MyRibbonRule extends AbstractLoadBalancerRule {

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
    }

    @Override
    public Server choose(Object key) {
        Server server = null;
        try {

            ILoadBalancer lb = getLoadBalancer();

            int count = 0;
            while (server == null && count++ < 10) {
                List<Server> reachableServers = lb.getReachableServers();
                List<Server> allServers = lb.getAllServers();
                int upCount = reachableServers.size();
                int serverCount = allServers.size();

                if ((upCount == 0) || (serverCount == 0)) {
                    log.warn("No up servers available from load balancer: " + lb);
                    return null;
                }

                for (Server server1 : reachableServers) {
                    NacosServer nacosServer = (NacosServer) server1;
                    Map<String, String> metadata = nacosServer.getMetadata();
                    // 獲取服務器標識信息
                    String hostname = metadata.get("hostname");
                    String rule = getRule();
                    if (rule.equals(hostname)) {

                        if (server1.isAlive() && (server1.isReadyToServe())) {
                            return server1;
                        }
                    }
                }

                if (server == null) {
                    /* Transient. */
                    Thread.yield();
                    continue;
                }

                if (server.isAlive() && (server.isReadyToServe())) {
                    return (server);
                }

                // Next.
                server = null;
            }

            if (count >= 10) {
                log.warn("No available alive servers after 10 tries from load balancer: "
                        + lb);
            }
        } catch (Exception e) {
            log.error("rule error", e);
            throw new CommonException("rule error");
        } finally {
            RuleThreadLocal.clearRule();
        }

        return server;
    }

    private String getRule() {
       return RuleThreadLocal.getRule();
    }

}
 

// Ribbon配置文件,使用我們自己的負載策略
@Configuration
public class RibbonConfig {

    @Profile({"dev", "test"})
    @Bean
    public IRule ribbonRule() {
        return new MyRibbonRule();
    }
}
  

// 創建一個feign的請求攔截器
@AllArgsConstructor
public class RuleRequestInterceptor implements RequestInterceptor {

    String rule;

    @Override
    public void apply(RequestTemplate template) {
        // 在創建請求之前設置本機服務器信息
        RuleThreadLocal.setRule(rule);
    }
}

 

// 配置自定義攔截器
@Profile({"dev","test"})
@Bean
public RequestInterceptor requestInterceptor() {
    return new RuleRequestInterceptor(rule);
}

  bootstrap.yml 配置

spring:
  cloud:
    nacos:
      config:
        file-extension: properties
        server-addr: xxxx
        namespace: xxxx
      discovery:
        server-addr: xxxxx
        namespace: xxxxx
        metadata:
          hostname: ${USER:root} #指定服務器名稱,每個機器上應該是不一樣的

 


// RibbonClients配置,xxxx爲你自己的服務
@RibbonClients({
        @RibbonClient(value = "xxxx", configuration = RibbonConfig.class),
        @RibbonClient(value = "xxxx", configuration = RibbonConfig.class)
})
public class GateWayServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(GateWayServerApplication.class, args);
    }
}
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章