Ribbon负载均衡策略DynamicServerListLoadBalancer的ServerListFilter解读

2023-11-20

一 DynamicServerListLoadBalancer在类图中的位置

二 DynamicServerListLoadBalancer源码解读

1 关键代码请见注释

2 源码位置:ribbon-master\ribbon-loadbalancer\src\main\java\com\netflix\loadbalancer\DynamicServerListLoadBalancer.java

//继承于BaseLoadBalancer类,它是对基础负载均衡器的扩展。实现了下面两个功能
//服务实例在运行期间的动态更新
//对服务器实例清单的过滤功能,可以通过过滤器来选择地获取一批服务实例清单
public class DynamicServerListLoadBalancer<T extends Server> extends BaseLoadBalancer {
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicServerListLoadBalancer.class);

    boolean isSecure = false;
    boolean useTunnel = false;

    protected AtomicBoolean serverListUpdateInProgress = new AtomicBoolean(false);
    //服务列表操作对象
    //这里泛型T是一个Server的子类,代表了一个具体的服务实例扩展类
    volatile ServerList<T> serverListImpl;

    //对应过滤器
    volatile ServerListFilter<T> filter;

    //实际实现委托给updateListOfServers函数
    protected final ServerListUpdater.UpdateAction updateAction = new ServerListUpdater.UpdateAction() {
        @Override
        public void doUpdate() {
            updateListOfServers();
        }
    };
    
    //用于触发向Eureka Server去获取服务实例清单以及如何在获取到服务实例清单后更新本地的服务实例清单
    //这个对象实现的是对Server的更新,又被称为服务更新器
    protected volatile ServerListUpdater serverListUpdater;

    public DynamicServerListLoadBalancer() {
        super();
    }

    @Deprecated
    public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
            ServerList<T> serverList, ServerListFilter<T> filter) {
        this(
                clientConfig,
                rule,
                ping,
                serverList,
                filter,
                new PollingServerListUpdater()
        );
    }

    public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
                                         ServerList<T> serverList, ServerListFilter<T> filter,
                                         ServerListUpdater serverListUpdater) {
        super(clientConfig, rule, ping);
        this.serverListImpl = serverList;
        this.filter = filter;
        this.serverListUpdater = serverListUpdater;
        if (filter instanceof AbstractServerListFilter) {
            ((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
        }
        restOfInit(clientConfig);
    }

    public DynamicServerListLoadBalancer(IClientConfig clientConfig) {
        initWithNiwsConfig(clientConfig);
    }
    
    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        try {
            super.initWithNiwsConfig(clientConfig);
            String niwsServerListClassName = clientConfig.getPropertyAsString(
                    CommonClientConfigKey.NIWSServerListClassName,
                    DefaultClientConfigImpl.DEFAULT_SEVER_LIST_CLASS);

            ServerList<T> niwsServerListImpl = (ServerList<T>) ClientFactory
                    .instantiateInstanceWithClientConfig(niwsServerListClassName, clientConfig);
            this.serverListImpl = niwsServerListImpl;

            if (niwsServerListImpl instanceof AbstractServerList) {
                AbstractServerListFilter<T> niwsFilter = ((AbstractServerList) niwsServerListImpl)
                        .getFilterImpl(clientConfig);
                niwsFilter.setLoadBalancerStats(getLoadBalancerStats());
                this.filter = niwsFilter;
            }

            String serverListUpdaterClassName = clientConfig.getPropertyAsString(
                    CommonClientConfigKey.ServerListUpdaterClassName,
                    DefaultClientConfigImpl.DEFAULT_SERVER_LIST_UPDATER_CLASS
            );

            this.serverListUpdater = (ServerListUpdater) ClientFactory
                    .instantiateInstanceWithClientConfig(serverListUpdaterClassName, clientConfig);

            restOfInit(clientConfig);
        } catch (Exception e) {
            throw new RuntimeException(
                    "Exception while initializing NIWSDiscoveryLoadBalancer:"
                            + clientConfig.getClientName()
                            + ", niwsClientConfig:" + clientConfig, e);
        }
    }

    void restOfInit(IClientConfig clientConfig) {
        boolean primeConnection = this.isEnablePrimingConnections();
        // turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
        this.setEnablePrimingConnections(false);
        enableAndInitLearnNewServersFeature();

        updateListOfServers();
        if (primeConnection && this.getPrimeConnections() != null) {
            this.getPrimeConnections()
                    .primeConnections(getReachableServers());
        }
        this.setEnablePrimingConnections(primeConnection);
        LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
    }
    
    
    @Override
    public void setServersList(List lsrv) {
        super.setServersList(lsrv);
        List<T> serverList = (List<T>) lsrv;
        Map<String, List<Server>> serversInZones = new HashMap<String, List<Server>>();
        for (Server server : serverList) {
            // make sure ServerStats is created to avoid creating them on hot
            // path
            getLoadBalancerStats().getSingleServerStat(server);
            String zone = server.getZone();
            if (zone != null) {
                zone = zone.toLowerCase();
                List<Server> servers = serversInZones.get(zone);
                if (servers == null) {
                    servers = new ArrayList<Server>();
                    serversInZones.put(zone, servers);
                }
                servers.add(server);
            }
        }
        setServerListForZones(serversInZones);
    }

    protected void setServerListForZones(
            Map<String, List<Server>> zoneServersMap) {
        LOGGER.debug("Setting server list for zones: {}", zoneServersMap);
        getLoadBalancerStats().updateZoneServerMapping(zoneServersMap);
    }

    public ServerList<T> getServerListImpl() {
        return serverListImpl;
    }

    public void setServerListImpl(ServerList<T> niwsServerList) {
        this.serverListImpl = niwsServerList;
    }

    public ServerListFilter<T> getFilter() {
        return filter;
    }

    public void setFilter(ServerListFilter<T> filter) {
        this.filter = filter;
    }

    public ServerListUpdater getServerListUpdater() {
        return serverListUpdater;
    }

    public void setServerListUpdater(ServerListUpdater serverListUpdater) {
        this.serverListUpdater = serverListUpdater;
    }

    @Override
    public void forceQuickPing() {
        // no-op
    }

    public void enableAndInitLearnNewServersFeature() {
        LOGGER.info("Using serverListUpdater {}", serverListUpdater.getClass().getSimpleName());
        serverListUpdater.start(updateAction);
    }

    private String getIdentifier() {
        return this.getClientConfig().getClientName();
    }

    public void stopServerListRefreshing() {
        if (serverListUpdater != null) {
            serverListUpdater.stop();
        }
    }

    //
    @VisibleForTesting
    public void updateListOfServers() {
        List<T> servers = new ArrayList<T>();
        if (serverListImpl != null) {
            //实现从Eureka Server中获取服务可用实例列表
            servers = serverListImpl.getUpdatedListOfServers();
            LOGGER.debug("List of Servers for {} obtained from Discovery client: {}",
                    getIdentifier(), servers);

            if (filter != null) {
                //
                servers = filter.getFilteredListOfServers(servers);
                LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}",
                        getIdentifier(), servers);
            }
        }
        updateAllServerList(servers);
    }

    /**
     * Update the AllServer list in the LoadBalancer if necessary and enabled
     *
     * @param ls
     */
    protected void updateAllServerList(List<T> ls) {
        // other threads might be doing this - in which case, we pass
        if (serverListUpdateInProgress.compareAndSet(false, true)) {
            try {
                for (T s : ls) {
                    s.setAlive(true); // set so that clients can start using these
                                      // servers right away instead
                                      // of having to wait out the ping cycle.
                }
                setServersList(ls);
                super.forceQuickPing();
            } finally {
                serverListUpdateInProgress.set(false);
            }
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("DynamicServerListLoadBalancer:");
        sb.append(super.toString());
        sb.append("ServerList:" + String.valueOf(serverListImpl));
        return sb.toString();
    }
    
    @Override
    public void shutdown() {
        super.shutdown();
        stopServerListRefreshing();
    }


    @Monitor(name="LastUpdated", type=DataSourceType.INFORMATIONAL)
    public String getLastUpdate() {
        return serverListUpdater.getLastUpdate();
    }

    @Monitor(name="DurationSinceLastUpdateMs", type= DataSourceType.GAUGE)
    public long getDurationSinceLastUpdateMs() {
        return serverListUpdater.getDurationSinceLastUpdateMs();
    }

    @Monitor(name="NumUpdateCyclesMissed", type=DataSourceType.GAUGE)
    public int getNumberMissedCycles() {
        return serverListUpdater.getNumberMissedCycles();
    }

    @Monitor(name="NumThreads", type=DataSourceType.GAUGE)
    public int getCoreThreads() {
        return serverListUpdater.getCoreThreads();
    }
}

三 ServerListFilter源码解读

//主要用于实现服务实例列表的过滤,通过传入的服务实例清单,根据一些规则返回过滤后的服务实例清单
public interface ServerListFilter<T extends Server> {

    public List<T> getFilteredListOfServers(List<T> servers);

}

四 过滤器类图

五 AbstractServerListFilter源码解读

//抽象过滤器
AbstractServerListFilterpublic abstract class AbstractServerListFilter<T extends Server> implements ServerListFilter<T> {

    //存储了关于负载均衡器的一些属性和统计信息
    private volatile LoadBalancerStats stats;
    
    public void setLoadBalancerStats(LoadBalancerStats stats) {
        this.stats = stats;
    }
    
    public LoadBalancerStats getLoadBalancerStats() {
        return stats;
    }

}

六 ZoneAffinityServerListFilter源码解读

//基于“区域感知(Zone Affinity)”的方式实现服务实例的过滤
//根据提供服务的实例所处的区域(Zone)与消费者自身所处的区域(Zone)进行比较,过滤掉那些不是同一个区域的实例
public class ZoneAffinityServerListFilter<T extends Server> extends
        AbstractServerListFilter<T> implements IClientConfigAware {

    private volatile boolean zoneAffinity = DefaultClientConfigImpl.DEFAULT_ENABLE_ZONE_AFFINITY;
    private volatile boolean zoneExclusive = DefaultClientConfigImpl.DEFAULT_ENABLE_ZONE_EXCLUSIVITY;
    private DynamicDoubleProperty activeReqeustsPerServerThreshold;
    private DynamicDoubleProperty blackOutServerPercentageThreshold;
    private DynamicIntProperty availableServersThreshold;
    private Counter overrideCounter;
    private ZoneAffinityPredicate zoneAffinityPredicate = new ZoneAffinityPredicate();
    
    private static Logger logger = LoggerFactory.getLogger(ZoneAffinityServerListFilter.class);
    
    String zone;
        
    public ZoneAffinityServerListFilter() {      
    }
    
    public ZoneAffinityServerListFilter(IClientConfig niwsClientConfig) {
        initWithNiwsConfig(niwsClientConfig);
    }
    
    @Override
    public void initWithNiwsConfig(IClientConfig niwsClientConfig) {
        String sZoneAffinity = "" + niwsClientConfig.getProperty(CommonClientConfigKey.EnableZoneAffinity, false);
        if (sZoneAffinity != null){
            zoneAffinity = Boolean.parseBoolean(sZoneAffinity);
            logger.debug("ZoneAffinity is set to {}", zoneAffinity);
        }
        String sZoneExclusive = "" + niwsClientConfig.getProperty(CommonClientConfigKey.EnableZoneExclusivity, false);
        if (sZoneExclusive != null){
            zoneExclusive = Boolean.parseBoolean(sZoneExclusive);
        }
        if (ConfigurationManager.getDeploymentContext() != null) {
            zone = ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone);
        }
        activeReqeustsPerServerThreshold = DynamicPropertyFactory.getInstance().getDoubleProperty(niwsClientConfig.getClientName() + "." + niwsClientConfig.getNameSpace() + ".zoneAffinity.maxLoadPerServer", 0.6d);
        logger.debug("activeReqeustsPerServerThreshold: {}", activeReqeustsPerServerThreshold.get());
        blackOutServerPercentageThreshold = DynamicPropertyFactory.getInstance().getDoubleProperty(niwsClientConfig.getClientName() + "." + niwsClientConfig.getNameSpace() + ".zoneAffinity.maxBlackOutServesrPercentage", 0.8d);
        logger.debug("blackOutServerPercentageThreshold: {}", blackOutServerPercentageThreshold.get());
        availableServersThreshold = DynamicPropertyFactory.getInstance().getIntProperty(niwsClientConfig.getClientName() + "." + niwsClientConfig.getNameSpace() + ".zoneAffinity.minAvailableServers", 2);
        logger.debug("availableServersThreshold: {}", availableServersThreshold.get());
        overrideCounter = Monitors.newCounter("ZoneAffinity_OverrideCounter");

        Monitors.registerObject("NIWSServerListFilter_" + niwsClientConfig.getClientName());
    }
    
    private boolean shouldEnableZoneAffinity(List<T> filtered) {    
        if (!zoneAffinity && !zoneExclusive) {
            return false;
        }
        if (zoneExclusive) {
            return true;
        }
        LoadBalancerStats stats = getLoadBalancerStats();
        if (stats == null) {
            return zoneAffinity;
        } else {
            logger.debug("Determining if zone affinity should be enabled with given server list: {}", filtered);
            //得到一些基础指标(实例数量、断路器断开数、活动请求数、实例平均负载)
            ZoneSnapshot snapshot = stats.getZoneSnapshot(filtered);
            double loadPerServer = snapshot.getLoadPerServer();
            int instanceCount = snapshot.getInstanceCount();            
            int circuitBreakerTrippedCount = snapshot.getCircuitTrippedCount();
            //任何一个条件符合,就不启用“区域感知”过滤的服务实例清单
            //三个条件是
            //故障百分比(断路器断开数/实例数量)>=0.8
            //实例平均负载>=0.6
            //可用实例数(实例数量-断路器断开数)<2
            if (((double) circuitBreakerTrippedCount) / instanceCount >= blackOutServerPercentageThreshold.get()
                    || loadPerServer >= activeReqeustsPerServerThreshold.get()
                    || (instanceCount - circuitBreakerTrippedCount) < availableServersThreshold.get()) {
                logger.debug("zoneAffinity is overriden. blackOutServerPercentage: {}, activeReqeustsPerServer: {}, availableServers: {}",
                        new Object[] {(double) circuitBreakerTrippedCount / instanceCount,  loadPerServer, instanceCount - circuitBreakerTrippedCount});
                return false;
            } else {
                return true;
            }
            
        }
    }
        
    @Override
    public List<T> getFilteredListOfServers(List<T> servers) {
        if (zone != null && (zoneAffinity || zoneExclusive) && servers !=null && servers.size() > 0){
            //服务实例列表过滤结果
            List<T> filteredServers = Lists.newArrayList(Iterables.filter(
                    servers, this.zoneAffinityPredicate.getServerOnlyPredicate()));  //zoneAffinityPredicate实现服务实例与消费者的Zone比较
            //根据shouldEnableZoneAffinity结果判定是否启动区域感知
            if (shouldEnableZoneAffinity(filteredServers)) {
                return filteredServers;
            } else if (zoneAffinity) {
                overrideCounter.increment();
            }
        }
        return servers;
    }

    @Override
    public String toString(){
        StringBuilder sb = new StringBuilder("ZoneAffinityServerListFilter:");
        sb.append(", zone: ").append(zone).append(", zoneAffinity:").append(zoneAffinity);
        sb.append(", zoneExclusivity:").append(zoneExclusive);
        return sb.toString();       
    }
}

七 DefaultNIWSServerListFilter源码解读

//完全继承自ZoneAffinityServerListFilter,是默认的NIWS(Netflix Internal Web Service)过滤器
public class DefaultNIWSServerListFilter<T extends Server> extends ZoneAffinityServerListFilter<T> {
}

八 ServerListSubsetFilter源码解读

//继承自ZoneAffinityServerListFilter,非常适用于大规模附录器集群(上百或更多)的系统。因为
//它可以产生一个“区域感知”结果的子集列表,同时它还能通过比较服务实例的通信
//失败数量和并发连接来判定该服务是否健康来选择性地从服务实例列表中剔除那些相
//对不健康的实例。
public class ServerListSubsetFilter<T extends Server> extends ZoneAffinityServerListFilter<T> implements IClientConfigAware, Comparator<T>{

    private Random random = new Random();
    private volatile Set<T> currentSubset = Sets.newHashSet();
    private DynamicIntProperty sizeProp = new DynamicIntProperty(DefaultClientConfigImpl.DEFAULT_PROPERTY_NAME_SPACE + ".ServerListSubsetFilter.size", 20);
    private DynamicFloatProperty eliminationPercent =
            new DynamicFloatProperty(DefaultClientConfigImpl.DEFAULT_PROPERTY_NAME_SPACE + ".ServerListSubsetFilter.forceEliminatePercent", 0.1f);
    private DynamicIntProperty eliminationFailureCountThreshold =
            new DynamicIntProperty(DefaultClientConfigImpl.DEFAULT_PROPERTY_NAME_SPACE + ".ServerListSubsetFilter.eliminationFailureThresold", 0);
    private DynamicIntProperty eliminationConnectionCountThreshold =
            new DynamicIntProperty(DefaultClientConfigImpl.DEFAULT_PROPERTY_NAME_SPACE + ".ServerListSubsetFilter.eliminationConnectionThresold", 0);
    
    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        super.initWithNiwsConfig(clientConfig);
        sizeProp = new DynamicIntProperty(clientConfig.getClientName() + "." + clientConfig.getNameSpace() + ".ServerListSubsetFilter.size", 20);
        eliminationPercent =
                new DynamicFloatProperty(clientConfig.getClientName() + "." + clientConfig.getNameSpace() + ".ServerListSubsetFilter.forceEliminatePercent", 0.1f);
        eliminationFailureCountThreshold = new DynamicIntProperty( clientConfig.getClientName()  + "." + clientConfig.getNameSpace()
                + ".ServerListSubsetFilter.eliminationFailureThresold", 0);
        eliminationConnectionCountThreshold = new DynamicIntProperty(clientConfig.getClientName() + "." + clientConfig.getNameSpace()
                + ".ServerListSubsetFilter.eliminationConnectionThresold", 0);
    }
        
   
    @Override
    public List<T> getFilteredListOfServers(List<T> servers) {
        List<T> zoneAffinityFiltered = super.getFilteredListOfServers(servers);
       //获取“区域感知”过滤结果,作为候选的服务实例清单
        Set<T> candidates = Sets.newHashSet(zoneAffinityFiltered);   //例如1,2,3
        Set<T> newSubSet = Sets.newHashSet(currentSubset);           //例如1,2,4
        LoadBalancerStats lbStats = getLoadBalancerStats();
        for (T server: currentSubset) {     //例如1,2,4
            //剔除有问题的服务实例
             if (!candidates.contains(server)) {      //4不在candidates中
                newSubSet.remove(server);                   //最终结果是1,2
            } else {
                ServerStats stats = lbStats.getSingleServerStat(server);
                // 不健康指标如下,剔除不健康的实例
                //服务实例的并连接数超过客户端的配置值
                //服务实例的失败数超过客户端的配置值
                if (stats.getActiveRequestsCount() > eliminationConnectionCountThreshold.get()
                        || stats.getFailureCount() > eliminationFailureCountThreshold.get()) {
                    newSubSet.remove(server);
                    // also remove from the general pool to avoid selecting them again
                    candidates.remove(server);
                }
            }
        }
        int targetedListSize = sizeProp.get();    //客户端默认实例子集数量,默认为20
        int numEliminated = currentSubset.size() - newSubSet.size();      //已被剔除的数量
        int minElimination = (int) (targetedListSize * eliminationPercent.get());   //20*10%=2
        int numToForceEliminate = 0;
        if (targetedListSize < newSubSet.size()) {
            // 还需要缩减的数量数
            numToForceEliminate = newSubSet.size() - targetedListSize;
        } else if (minElimination > numEliminated) {
            // 还需要缩减的数量数
            numToForceEliminate = minElimination - numEliminated;
        }
        
        if (numToForceEliminate > newSubSet.size()) {
            numToForceEliminate = newSubSet.size();
        }

        if (numToForceEliminate > 0) {
            List<T> sortedSubSet = Lists.newArrayList(newSubSet);        
            // 排序,最不健康的排在前面
            Collections.sort(sortedSubSet, this);
            //找到最不健康的
            List<T> forceEliminated = sortedSubSet.subList(0, numToForceEliminate);
            //删除最不健康的
            newSubSet.removeAll(forceEliminated);
            candidates.removeAll(forceEliminated);
        }
        
        //完成剔除后,清单已经少了至少10%(默认值)的服务实例,
        //最后通过随机的方式从候选清单中选出一批实例加入清单,
        //以保持服务实例子集和原来一致,默认值为20
        if (newSubSet.size() < targetedListSize) {
            int numToChoose = targetedListSize - newSubSet.size();
            candidates.removeAll(newSubSet);  //删除重合的
            if (numToChoose > candidates.size()) {
               //如果候选者不够数量,需要将候选者全部加入
                candidates = Sets.newHashSet(zoneAffinityFiltered);
                candidates.removeAll(newSubSet);    //删除重合的 
            }
            //选出一定数量的候选者
            List<T> chosen = randomChoose(Lists.newArrayList(candidates), numToChoose);
            for (T server: chosen) {
                newSubSet.add(server);  //加入候选者
            }
        }
        currentSubset = newSubSet;       
        return Lists.newArrayList(newSubSet);            
    }

    private List<T> randomChoose(List<T> servers, int toChoose) {
        int size = servers.size();
        if (toChoose >= size || toChoose < 0) {
            return servers;
        }
        for (int i = 0; i < toChoose; i++) {
            int index = random.nextInt(size);
            T tmp = servers.get(index);
            servers.set(index, servers.get(i));
            servers.set(i, tmp);
        }
        return servers.subList(0, toChoose);        
    }

    @Override
    public int compare(T server1, T server2) {
        LoadBalancerStats lbStats = getLoadBalancerStats();
        ServerStats stats1 = lbStats.getSingleServerStat(server1);
        ServerStats stats2 = lbStats.getSingleServerStat(server2);
        int failuresDiff = (int) (stats2.getFailureCount() - stats1.getFailureCount());
        if (failuresDiff != 0) {
            return failuresDiff;
        } else {
            return (stats2.getActiveRequestsCount() - stats1.getActiveRequestsCount());
        }
    }
}

九 ZonePreferenceServerListFilter源码解读

//Spring Cloud整合时新增的过滤器。若使用Spring Cloud整合 Eureka和Ribbon时会默认使用该过滤器。
//它通过配置或者Eureka实例元数据所属区域(Zone)来过滤出同区域的服务实例。
    @Override
    public List<Server> getFilteredListOfServers(List<Server> servers) {
        //获得区域感知的服务实例列表
        List<Server> output = super.getFilteredListOfServers(servers);
        if (this.zone != null && output.size() == servers.size()) {
            List<Server> local = new ArrayList<>();
            //遍历服务列表,取出根据消费者预设的区域Zone来进行过滤
            for (Server server : output) {
                if (this.zone.equalsIgnoreCase(server.getZone())) {
                    local.add(server);
                }
            }
            //返回通过消费者配置的Zone过滤后的结果
            if (!local.isEmpty()) {
                return local;
            }
        }
        //返回父类获取的结果
        return output;
    }

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Ribbon负载均衡策略DynamicServerListLoadBalancer的ServerListFilter解读 的相关文章

  • ubuntu系统中jupyterhub安装R内核集成rstudio

    需求 最后公司需要将原来用的Jupyter单用户版本改成Jupyterhub多用户版本 方便公司统一管理用户 并且因为平时工作会用到python和R的IDE 正好Jupyterhub可以满足需求 网上搜了很多 基本是三种方式 一种是通过k8

随机推荐

  • 公司后台管理系统搭建(Vue3+Vite+Element Plus+TypeScript+Pinia)

    前言 此次项目搭建选用 Vue3 Vite 并使用 pnpm 管理依赖包 本文将从下载到项目创建记录项目全过程 一 项目搭建 1 使用 npm 下载 pnpm 使用 pnpm 依赖包将被存放在一个统一的位置 因此可以节省大量的硬盘空间以及提
  • 自定义ViewGroup实现流式布局

    目录 1 View的绘制流程 2 自定义ViewGroup构造函数的作用 3 onMeasure 方法 3 1 View的度量方式 3 2 onMeasure方法参数的介绍 3 3 自定义ViewGroup onMeasure 方法的实现
  • HiveSQL原理和优化详解

    Hive SQL 编译成MapReduce过程 编译 SQL 的任务是在上节中介绍的 COMPILER 编译器组件 中完成的 Hive将SQL转化为MapReduce任务 整个编译过程分为六个阶段 词法 语法解析 Antlr 定义 SQL
  • javascript相关

    1 扁平数据结构转Tree 打平的数据内容如下 let arr id 1 name 部门1 pid 0 id 2 name 部门2 pid 1 id 3 name 部门3 pid 1 id 4 name 部门4 pid 3 id 5 nam
  • vscode编辑器插件总结

    之前一直用webstorm webstorm确实太重了 后来无意中发现了vscode 高颜值吸引了我哈哈哈 就一直用着 很喜欢VScode的插件功能 想要什么插件就搜索 比如搜索angular 只要点击一下某款插件 插件的介绍和用法都会在右
  • feign的Fallback机制

    对接口使用 FeignClient后声明feign客户端后 可以使用属性fallback指定异常处理类 这个类必须实现 FeignClient作用的接口 且被注入到容器中 FeignClient name service provider1
  • 浪潮

    这是一篇旧闻 是我2011年8月6日发在豆瓣上的 前几天重玩豆瓣 看到了 很多怀念 我感到了生命的浪潮 读西哲史有感 o 不会吧 浑浑噩噩的大学生活居然过去一半了啊 当年读 此间的少年 满以为大学就是乔峰 慕容复PK 令狐冲 杨康宿舍里面切
  • 测试分为什么,白盒,黑盒,单元,集成测试?

    一 为什么测试的概念这么多 一个软件项目就好比一部复杂的汽车 有很多零件 当每个零件生产完成后 就要测试零件是否存在质量问题 零件组成复杂的汽车后 我们还要测试汽车 比如著名的中保研 测试刹车 测试气囊 测试防撞 顾客从4s店购买汽车 要带
  • Vue学习(五)登陆页面之重置和发起登陆请求及弹窗提示

    Vue学习 五 登陆页面之重置和发起登陆请求及弹窗提示 表单重置 根据预验证结果决定是否发出登陆请求 编写代码 启动api服务器 弹窗提示 表单重置 直接调用element ui给我们写好的函数就可以了 获取当前表单的实例对象 通过这个实例
  • java中实现多态的机制是什么_java中实现多态的机制是什么? java什么是多态?

    学习java刚刚入门的小伙伴们 不知道大家在初次接触java中的多态一概念的时候 是否能清晰的讲出实现多态的机制是什么吗 什么才是java中的多态呢 多态性是指的面向对象程序设计代码重用的一个重要的机制 对于Java多态性 应该都不是第一次
  • Android Framework——进程间通讯学习,从Binder使用看起

    前言 Binder 是安卓中非常重要的进程间通讯工具 通过Binder 安卓在ServiceManager中对外提供了一系列的服务 学习Binder 将很好地为我们学习framework开个好头 Android 使用多进程 Android
  • 5分钟带你看懂Jeesite10大功能要点

    jeesite内容丰富 集成了大量优秀的组件 是一个值得研究的框架 它有 1 shiro安全权限控制 2 mybatis查询缓存接口扩展 3 ecache分布式缓存整合 4 页面资源缓存优化 5 多数据源灵活切换 6 mybatismapp
  • EasyExcel轻松读取Excel文件!

    EasyExcel是一个Java库 用于快速 简单地读写Excel文件 要使用EasyExcel 您首先需要将其添加为项目的依赖 如果使用Maven 可以添加以下依赖项
  • 算法:二分查找之第一个错误的版本

    方法一 class Solution public int firstBadVersion int n int left 1 int right n while left lt right int mid right left 2 left
  • 【React】 4课 react初识组件

    首先我们如1课创建一个文件夹在文件夹中安装react环境需要的配置文件 npm init y npm i babel standalone D npm i react react dom D 安装配置文件教程链接 https blog cs
  • 4个开源的Java代码静态分析工具

    1 PMD PMD是一款采用BSD协议发布的Java程序代码检查工具 该工具可以做到检查Java代码中是否含有未使用的变量 是否含有空的抓取块 是否含有不必要的对象等 该软件功能强大 扫描效率高 是Java程序员debug的好帮手 PMD支
  • Lex和Yacc应用教程(四).语法树的应用

    Lex和Yacc应用方法 四 语法树的应用 草木瓜 20070515 一 序 不论什么语言 语法结构总是那几种 可以想象任何程序体都可以解释成一棵语法树 语法树的本质是递归 很显然Yacc文法的核心思想也是递归 本文就通过具体实例 使用Ya
  • cesium加载影像的问题解决

    我用gdal把web墨卡托转为经纬度 再切分片时 发现对不上影像 经过两天排查 发现竟然是前端写错 viewer scene imageryLayers addImageryProvider new Cesium UrlTemplateIm
  • vscode乱码

    vscode中文乱码怎么解决 vscode是一款跨平台源代码编辑器 能够在桌面上运行 并且能够用途windows macOS以及Linux 但是有不少小伙伴们在使用vscode时 输入输出的却是中文代码 也不知道如何解决 那么今天小编就来告
  • Ribbon负载均衡策略DynamicServerListLoadBalancer的ServerListFilter解读

    一 DynamicServerListLoadBalancer在类图中的位置 二 DynamicServerListLoadBalancer源码解读 1 关键代码请见注释 2 源码位置 ribbon master ribbon loadba