从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(六)开发篇-如何解决微服务开发环境请求实例转发到别人机器问题

从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(六)开发篇-如何解决微服务开发环境请求实例转发到别人机器问题_第1张图片

当前公司内微服务开发一般有两种模式

1.开发启动所有服务,注册中心,网关,认证服务等都在自己机器启动

 优点:可以保证自己发送的请求(经过网关的请求或者通过fegin调用的请求)都能达到自己机器的服务

缺点:如果服务过多,电脑有可能会承受不住

2.开发只启动自己需要开发的服务,注册中心,网关,认证服务等都在一个公用的服务器启动,所有人启动的服务都注册到了这个公用服务上

优点:只需要启动自己开发的服务,方便快捷,节省电脑资源
缺点:由于自己开发的服务不一定只有自己启动了,有可能有别人同时启动了该服务,所以当从前端请求或者通过fegin请求时,可能请求会发送到了别人机器启动的服务上

模式2会有请求发送到别人机器对应服务的情况,具体情况明细图如下

从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(六)开发篇-如何解决微服务开发环境请求实例转发到别人机器问题_第2张图片

图1 

说明:

图1 通过网关请求后台服务A ,由于两个开发人员都启动了服务A并且注册到了公用注册中心上,使用默认负载均衡策略得话,此时请求到的不一定是小李的还是小张的电脑的服务A 

从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(六)开发篇-如何解决微服务开发环境请求实例转发到别人机器问题_第3张图片

图2 

说明:

图二 通过fegin请求后台服务A ,由于两个开发人员都启动了服务A并且注册到了公用注册中心上,使用默认负载均衡策略得话,所以此时请求到的不一定是小李的还是小张的电脑的服务A

解决方案,由于上面问题都是在负载均衡获取实例时随机去到了一个实例,所以解决方式就是重写负载均衡路由指定获取特定的实例

网关端重写路由解决方案解决图1问题

从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(六)开发篇-如何解决微服务开发环境请求实例转发到别人机器问题_第4张图片

 代码,添加如下这几个类

从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(六)开发篇-如何解决微服务开发环境请求实例转发到别人机器问题_第5张图片

CustomRibbonRule.java
public class CustomRibbonRule extends RoundRobinRule {

    private String version;


    public void setVersion(String version) {
        this.version = version;
    }


    @Override
    public Server choose(ILoadBalancer lb, Object key) {


        List targetList = null;
        List upList = lb.getReachableServers();
        if (StrUtil.isEmpty(version)) {
            version = ReqeustHeaderContextHolder.getVersion();
        }
        if (StrUtil.isNotEmpty(version)) {
            //取指定版本号的实例
            targetList = upList.stream().filter(
                    server -> version.equals(
                            ((NacosServer) server).getMetadata().get("version")
                    )
            ).collect(Collectors.toList());
        }

        if (CollUtil.isEmpty(targetList)) {
            //只取无版本号的实例
            targetList = upList.stream().filter(
                    server -> {
                        String metadataVersion = ((NacosServer) server).getMetadata().get("version");
                        return StrUtil.isEmpty(metadataVersion);
                    }
            ).collect(Collectors.toList());
        }

        if (CollUtil.isNotEmpty(targetList)) {
            return getServer(targetList);
        }
        return super.choose(lb, key);


    }

    private Server getServer(List upList) {
        int nextInt = RandomUtil.randomInt(upList.size());
        return upList.get(nextInt);
    }
}

RibbonConfiguration.java

@Configuration
public class RibbonConfiguration {

    @Value("${spring.cloud.nacos.discovery.metadata.version:#{null}}")
    private String version;

    @Bean
    public IRule defaultLBStrategy() {
        CustomRibbonRule customIsolationRule = new CustomRibbonRule();
        customIsolationRule.setVersion(version);
        return customIsolationRule;
    }



}
ReqeustHeaderContextHolder.java
public class ReqeustHeaderContextHolder {

    private static final FastThreadLocal fastThreadLocal = new FastThreadLocal();


    public static void putVersion(String version){
        fastThreadLocal.set(version);
    }

    public static String getVersion(){
        return Objects.isNull(fastThreadLocal.get())?null:(String) fastThreadLocal.get();
    }

    public static void clear(){
        fastThreadLocal.remove();
    }
}
OrderedGatewayFilter.java
public class OrderedGatewayFilter implements GatewayFilter, Ordered {

    private final GatewayFilter delegate;

    private final int order;

    public OrderedGatewayFilter(GatewayFilter delegate, int order) {
        this.delegate = delegate;
        this.order = order;
    }

    public GatewayFilter getDelegate() {
        return delegate;
    }

    @Override
    public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        try{
            if(CollectionUtil.isNotEmpty(exchange.getRequest().getHeaders().get("version"))){
                String version = exchange.getRequest().getHeaders().get("version").get(0);
                ReqeustHeaderContextHolder.putVersion(version);
            }
            return this.delegate.filter(exchange, chain);
        }finally {
            ReqeustHeaderContextHolder.clear();
        }

    }

    @Override
    public int getOrder() {
        return this.order;
    }

    @Override
    public String toString() {
        return new StringBuilder("[").append(delegate).append(", order = ").append(order)
                .append("]").toString();
    }

}

fegin端重写路由解决方案解决图2问题

从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(六)开发篇-如何解决微服务开发环境请求实例转发到别人机器问题_第6张图片

服务fegin的负载均衡路由,注册时将自己机器的服务A加上特定key属性注册到注册中心内,获取时优先获取有指定key的实例,没有找到再随机获取一个实例

代码:

各个应用部分可以添加如下类或者抽取出共通类

从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(六)开发篇-如何解决微服务开发环境请求实例转发到别人机器问题_第7张图片

CustomRibbonRule.java
public class CustomRibbonRule extends RoundRobinRule {

    private String version;


    public void setVersion(String version) {
        this.version = version;
    }


    @Override
    public Server choose(ILoadBalancer lb, Object key) {

        List targetList = null;
        List upList = lb.getReachableServers();
        if (StrUtil.isNotEmpty(version)) {
            //取指定版本号的实例
            targetList = upList.stream().filter(
                    server -> version.equals(
                            ((NacosServer) server).getMetadata().get("version")
                    )
            ).collect(Collectors.toList());
        }

        if (CollUtil.isEmpty(targetList)) {
            //只取无版本号的实例
            targetList = upList.stream().filter(
                    server -> {
                        String metadataVersion = ((NacosServer) server).getMetadata().get("version");
                        return StrUtil.isEmpty(metadataVersion);
                    }
            ).collect(Collectors.toList());
        }

        if (CollUtil.isNotEmpty(targetList)) {
            return getServer(targetList);
        }
        return super.choose(lb, key);

    }

    private Server getServer(List upList) {
        int nextInt = RandomUtil.randomInt(upList.size());
        return upList.get(nextInt);
    }
}

RibbonConfiguration.java
@Configuration
public class RibbonConfiguration {

    @Value("${spring.cloud.nacos.discovery.metadata.version:#{null}}")
    private String version;

    @Bean
    public IRule defaultLBStrategy() {
        CustomRibbonRule customIsolationRule = new CustomRibbonRule();
        customIsolationRule.setVersion(version);
        return customIsolationRule;
    }
}

 然后对应yml里面添加自己对应的version

从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(六)开发篇-如何解决微服务开发环境请求实例转发到别人机器问题_第8张图片

 大功告成,看看效果

首先在需要走自己机器的服务上加上对应version,都启动后如下图

从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(六)开发篇-如何解决微服务开发环境请求实例转发到别人机器问题_第9张图片

 从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(六)开发篇-如何解决微服务开发环境请求实例转发到别人机器问题_第10张图片

我们通过postman 模拟前端请求网关访问看看是不是走的我们指定的xiaoli 的实例

从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(六)开发篇-如何解决微服务开发环境请求实例转发到别人机器问题_第11张图片

可以看到我们网关自定义的负载路由去到了version=xiaoli的实例 

 再来看看fegin调用的时候是不是也是走的我们指定version的实例

从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(六)开发篇-如何解决微服务开发环境请求实例转发到别人机器问题_第12张图片

 从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(六)开发篇-如何解决微服务开发环境请求实例转发到别人机器问题_第13张图片

 可以看order调用商品服务的时候也是找了商品服务version为xiaoli的实例,整明两个问题都解决了

你可能感兴趣的:(mini-cloud,微服务,负载均衡,java)