spring cloud eureka 服务分组隔离

spring cloud eureka 服务分组隔离

  • spring cloud eureka 作为注册中心,feign 服务之间调用,原生不支持服务的隔离,

    比如以下场景: 服务A 调用服务 B(b1,b2),某些情况下只想让A请求到b1,实现服务之间的分组隔离

    或者 共用注册中心,配置中心,公共的服务模块,开发人员本机调试的时候不用启动大量的服务,导致开发机运行缓慢

实现思路

  • 其实要实现很简单,参考MQ消息分组消费的原理,只需要在服务注册的时候,将每个服务实例分组标示好,然后自定义实现负载均衡的策略,根据服务消费者的分组名找到对应分组的服务提供者,选择性的请求就可以

负载均衡

  • 客户端负载均衡,客户端负载均衡策略,由客户端实现,客户端自己决定需要调用那一个服务 ribbon就是属于客户端负载模式
  • 服务端负载模式,服务端收到请求,由服务端决定调用那一个服务,spring cloud getway就是属于服务端负载均衡模式,另外 还有典型的ngxin

Ribbon 负载均衡规则策略

  • IRule 接口

    public interface IRule{
        /*
         * choose one alive server from lb.allServers or
         * lb.upServers according to key
         * 
         * @return choosen Server object. NULL is returned if none
         *  server is available 
         */
    
        public Server choose(Object key);
        
        public void setLoadBalancer(ILoadBalancer lb);
        
        public ILoadBalancer getLoadBalancer();    
    }
    
    
  • AbstractLoadBalancerRule 抽象实现

    public abstract class AbstractLoadBalancerRule implements IRule, IClientConfigAware {
    
        private ILoadBalancer lb;
            
        @Override
        public void setLoadBalancer(ILoadBalancer lb){
            this.lb = lb;
        }
         /**
    	*ILoadBalancer 维护了所有服务实例的相关信息, com.netflix.loadbalancer.ILoadBalancer#getAllServers
        */
        @Override
        public ILoadBalancer  getLoadBalancer(){
            return lb;
        }      
    }
    
  • RandomRule 随机选择一个

    public Server choose(ILoadBalancer lb, Object key) {
            if (lb == null) {
                return null;
            }
            Server server = null;
    
            while (server == null) {
                if (Thread.interrupted()) {
                    return null;
                }
                List<Server> upList = lb.getReachableServers();
                List<Server> allList = lb.getAllServers();
    
                int serverCount = allList.size();
                if (serverCount == 0) {
                    /*
                     * No servers. End regardless of pass, because subsequent passes
                     * only get more restrictive.
                     */
                    return null;
                }
    			// 随机选择一个
                int index = rand.nextInt(serverCount);
                server = upList.get(index);
    
                if (server == null) {
                    /*
                     * The only time this should happen is if the server list were
                     * somehow trimmed. This is a transient condition. Retry after
                     * yielding.
                     */
                    Thread.yield();
                    continue;
                }
    
                if (server.isAlive()) {
                    return (server);
                }
    
                // Shouldn't actually happen.. but must be transient or a bug.
                server = null;
                Thread.yield();
            }
    
            return server;
    
        }
    
    
  • RoundRobinRule 照线性轮询

    public Server choose(ILoadBalancer lb, Object key) {
            if (lb == null) {
                log.warn("no load balancer");
                return null;
            }
    
            Server server = null;
            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;
                }
    			// 照线性轮询
                int nextServerIndex = incrementAndGetModulo(serverCount);
                server = allServers.get(nextServerIndex);
    
                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);
            }
            return server;
        }
    	
        private int incrementAndGetModulo(int modulo) {
            for (;;) {
                int current = nextServerCyclicCounter.get();
                int next = (current + 1) % modulo;
                if (nextServerCyclicCounter.compareAndSet(current, next))
                    return next;
            }
        }
    
    
  • RetryRule 重试机制的选择器

    public Server choose(ILoadBalancer lb, Object key) {
    		long requestTime = System.currentTimeMillis();
    		long deadline = requestTime + maxRetryMillis;
    
    		Server answer = null;
    
    		answer = subRule.choose(key);
    
    		if (((answer == null) || (!answer.isAlive()))
    				&& (System.currentTimeMillis() < deadline)) {
    
    			InterruptTask task = new InterruptTask(deadline
    					- System.currentTimeMillis());
    
    			while (!Thread.interrupted()) {
    				answer = subRule.choose(key);
    
    				if (((answer == null) || (!answer.isAlive()))
    						&& (System.currentTimeMillis() < deadline)) {
    					/* pause and retry hoping it's transient */
    					Thread.yield();
    				} else {
    					break;
    				}
    			}
    
    			task.cancel();
    		}
    
    		if ((answer == null) || (!answer.isAlive())) {
    			return null;
    		} else {
    			return answer;
    		}
    	}
    
  • WeightedResponseTimeRule 根据权重计算

     public Server choose(ILoadBalancer lb, Object key) {
            if (lb == null) {
                return null;
            }
            Server server = null;
    
            while (server == null) {
                // get hold of the current reference in case it is changed from the other thread
                List<Double> currentWeights = accumulatedWeights;
                if (Thread.interrupted()) {
                    return null;
                }
                List<Server> allList = lb.getAllServers();
    
                int serverCount = allList.size();
    
                if (serverCount == 0) {
                    return null;
                }
    
                int serverIndex = 0;
    
                // last one in the list is the sum of all weights
                double maxTotalWeight = currentWeights.size() == 0 ? 0 : currentWeights.get(currentWeights.size() - 1); 
                // No server has been hit yet and total weight is not initialized
                // fallback to use round robin
                if (maxTotalWeight < 0.001d) {
                    server =  super.choose(getLoadBalancer(), key);
                    if(server == null) {
                        return server;
                    }
                } else {
                    // generate a random weight between 0 (inclusive) to maxTotalWeight (exclusive)
                    double randomWeight = random.nextDouble() * maxTotalWeight;
                    // pick the server index based on the randomIndex
                    int n = 0;
                    for (Double d : currentWeights) {
                        if (d >= randomWeight) {
                            serverIndex = n;
                            break;
                        } else {
                            n++;
                        }
                    }
    
                    server = allList.get(serverIndex);
                }
    
                if (server == null) {
                    /* Transient. */
                    Thread.yield();
                    continue;
                }
    
                if (server.isAlive()) {
                    return (server);
                }
    
                // Next.
                server = null;
            }
            return server;
        }
    
  • ZoneAvoidanceRule 复合判断server所在区域的性能和server的可用性选择服务器 (默认规则)

     public Server choose(Object key) {
            ILoadBalancer lb = getLoadBalancer();
            Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
            if (server.isPresent()) {
                return server.get();
            } else {
                return null;
            }       
        }
    
  • BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择-一个并发 量最小的服务

     public Server choose(Object key) {
            if (loadBalancerStats == null) {
                return super.choose(key);
            }
            List<Server> serverList = getLoadBalancer().getAllServers();
            int minimalConcurrentConnections = Integer.MAX_VALUE;
            long currentTime = System.currentTimeMillis();
            Server chosen = null;
            for (Server server: serverList) {
                ServerStats serverStats = loadBalancerStats.getSingleServerStat(server);
                if (!serverStats.isCircuitBreakerTripped(currentTime)) {
                    int concurrentConnections = serverStats.getActiveRequestsCount(currentTime);
                    if (concurrentConnections < minimalConcurrentConnections) {
                        minimalConcurrentConnections = concurrentConnections;
                        chosen = server;
                    }
                }
            }
            if (chosen == null) {
                return super.choose(key);
            } else {
                return chosen;
            }
        }
    

自定义规则 ZoneAvoidanceRuleSupport

  • 自定义实现 IRule , 这里我们继承 ZoneAvoidanceRule,
public class ZoneAvoidanceRuleSupport extends ZoneAvoidanceRule {

    @Autowired
    private Environment env;
    @Override
    public Server choose(Object key) {
        //获取服务调用者的groupName
        String appGroup = env.getProperty("eureka.instance.app-group-name");
		
        ILoadBalancer lb = getLoadBalancer();
        //获取所有的服务实例
        List<Server> allServers = lb.getAllServers();
        // 根据分组groupName 过滤,找到对应分组的服务
        List<Server> collect = allServers.stream().filter(server -> {
            DiscoveryEnabledServer discoveryEnabledServer=(DiscoveryEnabledServer)server;
            String serverGroup = discoveryEnabledServer.getInstanceInfo().getAppGroupName();
            if (StrUtil.isBlank(appGroup)){
                return StrUtil.isBlank(serverGroup);
            }else {
                return appGroup.equals(serverGroup);
            }
        }).collect(Collectors.toList());
        // 调用父类的实现,找到一个可用的服务
        Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(collect, key);
        if (server.isPresent()) {
            return server.get();
        } else {
            return null;
        }

    }
}

配置分组名

  • 服务提供者和服务消费者同时配置相同分组
  • 服务消费者也可以在启动参数中传递分组名
eureka:
  client:
    serviceUrl:
      defaultZone: http://172.16.1.248:11001/eureka/
# 配置实例的分组名
  instance:
    app-group-name: LUJIA

指定 ribbon 负载规则

  • 服务消费者指定
queenOA-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.queen.oa.api.feign.youpin.ZoneAvoidanceRuleSupport

ok,启动服务,就可以根据不同的分组之间的隔离调用。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值