Spring Cloud Alibaba之负载均衡组件 - Ribbon

2023-10-31

负载均衡

我们都知道在微服务架构中,微服务之间总是需要互相调用,以此来实现一些组合业务的需求。例如组装订单详情数据,由于订单详情里有用户信息,所以订单服务就得调用用户服务来获取用户信息。要实现远程调用就需要发送网络请求,而每个微服务都可能会存在有多个实例分布在不同的机器上,那么当一个微服务调用另一个微服务的时候就需要将请求均匀的分发到各个实例上,以此避免某些实例负载过高,某些实例又太空闲,所以在这种场景必须要有负载均衡器。

目前实现负载均衡主要的两种方式:

1、服务端负载均衡;例如最经典的使用Nginx做负载均衡器。用户的请求先发送到Nginx,然后再由Nginx通过配置好的负载均衡算法将请求分发到各个实例上,由于需要作为一个服务部署在服务端,所以该种方式称为服务端负载均衡。如图:
Spring Cloud Alibaba之负载均衡组件 - Ribbon

2、客户端侧负载均衡;之所以称为客户端侧负载均衡,是因为这种负载均衡方式是由发送请求的客户端来实现的,也是目前微服务架构中用于均衡服务之间调用请求的常用负载均衡方式。因为采用这种方式的话服务之间可以直接进行调用,无需再通过一个专门的负载均衡器,这样能够提高一定的性能以及高可用性。以微服务A调用微服务B举例,简单来说就是微服务A先通过服务发现组件获取微服务B所有实例的调用地址,然后通过本地实现的负载均衡算法选取出其中一个调用地址进行请求。如图:
Spring Cloud Alibaba之负载均衡组件 - Ribbon

我们来通过Spring Cloud提供的DiscoveryClient写一个非常简单的客户端侧负载均衡器,借此直观的了解一下该种负载均衡器的工作流程,该示例中采用的负载均衡策略为随机,代码如下:

package com.zj.node.contentcenter.discovery;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;

/**
 * 客户端侧负载均衡器
 *
 * @author 01
 * @date 2019-07-26
 **/
public class LoadBalance {

    @Autowired
    private DiscoveryClient discoveryClient;

    /**
     * 随机获取目标微服务的请求地址
     *
     * @return 请求地址
     */
    public String randomTakeUri(String serviceId) {
        // 获取目标微服务的所有实例的请求地址
        List<String> targetUris = discoveryClient.getInstances(serviceId).stream()
                .map(i -> i.getUri().toString())
                .collect(Collectors.toList());
        // 随机获取列表中的uri
        int i = ThreadLocalRandom.current().nextInt(targetUris.size());

        return targetUris.get(i);
    }
}

使用Ribbon实现负载均衡

什么是Ribbon:

  • Ribbon是Netflix开源的客户端侧负载均衡器
  • Ribbon内置了非常丰富的负载均衡策略算法

Ribbon虽然是个主要用于负载均衡的小组件,但是麻雀虽小五脏俱全,Ribbon还是有许多的接口组件的。如下表:
Spring Cloud Alibaba之负载均衡组件 - Ribbon

Ribbon默认内置了八种负载均衡策略,若想自定义负载均衡策略则实现上表中提到的IRule接口或AbstractLoadBalancerRule抽象类即可。内置的负载均衡策略如下:
Spring Cloud Alibaba之负载均衡组件 - Ribbon

  • 默认的策略规则为ZoneAvoidanceRule

Ribbon主要有两种使用方式,一是使用Feign,Feign内部已经整合了Ribbon,因此如果只是普通使用的话都感知不到Ribbon的存在;二是配合RestTemplate使用,这种方式则需要添加Ribbon依赖和@LoadBalanced注解。

这里主要演示一下第二种使用方式,由于项目中添加的Nacos依赖已包含了Ribbon所以不需要另外添加依赖,首先定义一个RestTemplate,代码如下:

package com.zj.node.contentcenter.configuration;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * bean 配置类
 *
 * @author 01
 * @date 2019-07-25
 **/
@Configuration
public class BeanConfig {

    @Bean
    @LoadBalanced  // 加上这个注解表示使用Ribbon
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

然后使用RestTemplate调用其他服务的时候,只需要写服务名即可,不需要再写ip地址和端口号。如下示例:

public ShareDTO findById(Integer id) {
    // 获取分享详情
    Share share = shareMapper.selectByPrimaryKey(id);
    // 发布人id
    Integer userId = share.getUserId();
    // 调用用户中心获取用户信息
    UserDTO userDTO = restTemplate.getForObject(
            "http://user-center/users/{id}",  // 只需要写服务名
            UserDTO.class, userId
    );

    ShareDTO shareDTO = objectConvert.toShareDTO(share);
    shareDTO.setWxNickname(userDTO.getWxNickname());

    return shareDTO;
}

如果不太清楚RestTemplate的使用,可以参考如下文章:


自定义Ribbon负载均衡配置

在实际开发中,我们可能会遇到默认的负载均衡策略无法满足需求,从而需要更换其他的负载均衡策略。关于Ribbon负载均衡的配置方式主要有两种,在代码中配置或在配置文件中配置。

Ribbon支持细粒度的配置,例如我希望微服务A在调用微服务B的时候采用随机的负载均衡策略,而在调用微服务C的时候采用默认策略,下面我们就来实现一下这种细粒度的配置。

1、首先是通过代码进行配置,编写一个配置类用于实例化指定的负载均衡策略对象:

@Configuration
public class RibbonConfig {

    @Bean
    public IRule ribbonRule(){
        // 随机的负载均衡策略对象
        return new RandomRule();
    }
}

然后再编写一个用于配置Ribbon客户端的配置类,该配置类的目的是指定在调用user-center时采用RibbonConfig里配置的负载均衡策略,这样就可以达到细粒度配置的效果:

@Configuration
// 该注解用于自定义Ribbon客户端配置,这里声明为属于user-center的配置
@RibbonClient(name = "user-center", configuration = RibbonConfig.class)
public class UserCenterRibbonConfig {
}

需要注意的是RibbonConfig应该定义在主启动类之外,避免被Spring扫描到,不然会产生父子上下文扫描重叠的问题,从而导致各种奇葩的问题。而在Ribbon这里就会导致该配置类被所有的Ribbon客户端共享,即不管调用user-center还是其他微服务都会采用该配置类里定义的负载均衡策略,这样就会变成了一个全局配置了,违背了我们需要细粒度配置的目的。所以需要将其定义在主启动类之外:
Spring Cloud Alibaba之负载均衡组件 - Ribbon

关于这个问题可以参考官方文档的描述:

https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#_customizing_the_ribbon_client

2、使用配置文件进行配置就更简单了,不需要写代码还不会有父子上下文扫描重叠的坑,只需在配置文件中增加如下一段配置就可以实现以上使用代码配置等价的效果:

user-center:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

两种配置方式对比:
Spring Cloud Alibaba之负载均衡组件 - Ribbon

  • 关于优先级:细粒度配置文件配置 > 细粒度代码配置 > 全局配置文件配置 > 全局代码配置

最佳实践总结:

  • 尽量使用配置文件配置,配置文件满足不了需求的情况下再考虑使用代码配置
  • 在同一个微服务内尽量保持单一性,例如统一使用配置文件配置,尽量不要两种方式混用,以免增加定位问题的复杂度

以上介绍的是细粒度地针对某个特定Ribbon客户端的配置,下面我们再演示一下如何实现全局配置。很简单,只需要把注解改为@RibbonClients即可,代码如下:

@Configuration
// 该注解用于全局配置
@RibbonClients(defaultConfiguration = RibbonConfig.class)
public class GlobalRibbonConfig {
}

Ribbon默认是懒加载的,所以在第一次发生请求的时候会显得比较慢,我们可以通过在配置文件中添加如下配置开启饥饿加载:

ribbon:
  eager-load:
    enabled: true
    # 为哪些客户端开启饥饿加载,多个客户端使用逗号分隔(非必须)
    clients: user-center

支持Nacos权重

以上小节基本介绍完了负载均衡及Ribbon的基础使用,接下来的内容需要配合Nacos,若没有了解过Nacos的话可以参考以下文章:

在Nacos Server的控制台页面可以编辑每个微服务实例的权重,服务列表 -> 详情 -> 编辑;默认权重都为1,权重值越大就越优先被调用:
Spring Cloud Alibaba之负载均衡组件 - Ribbon

权重在很多场景下非常有用,例如一个微服务有很多的实例,它们被部署在不同配置的机器上,这时候就可以将配置较差的机器上所部署的实例权重设置得比较低,而部署在配置较好的机器上的实例权重设置得高一些,这样就可以将较大一部分的请求都分发到性能较高的机器上。

但是Ribbon内置的负载均衡策略都不支持Nacos的权重,所以我们就需要自定义实现一个支持Nacos权重配置的负载均衡策略。好在Nacos Client已经内置了负载均衡的能力,所以实现起来也比较简单,代码如下:

package com.zj.node.contentcenter.configuration;

import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties;
import org.springframework.cloud.alibaba.nacos.ribbon.NacosServer;

/**
 * 支持Nacos权重配置的负载均衡策略
 *
 * @author 01
 * @date 2019-07-27
 **/
@Slf4j
public class NacosWeightedRule extends AbstractLoadBalancerRule {

    @Autowired
    private  NacosDiscoveryProperties discoveryProperties;

    /**
     * 读取配置文件,并初始化NacosWeightedRule
     *
     * @param iClientConfig iClientConfig
     */
    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
        // do nothing
    }

    @Override
    public Server choose(Object key) {
        BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
        log.debug("lb = {}", loadBalancer);

        // 需要请求的微服务名称
        String name = loadBalancer.getName();
        // 获取服务发现的相关API
        NamingService namingService = discoveryProperties.namingServiceInstance();

        try {
            // 调用该方法时nacos client会自动通过基于权重的负载均衡算法选取一个实例
            Instance instance = namingService.selectOneHealthyInstance(name);
            log.info("选择的实例是:instance = {}", instance);

            return new NacosServer(instance);
        } catch (NacosException e) {
            return null;
        }
    }
}

然后在配置文件中配置一下就可以使用该负载均衡策略了:

user-center:
  ribbon:
    NFLoadBalancerRuleClassName: com.zj.node.contentcenter.configuration.NacosWeightedRule

思考:既然Nacos Client已经有负载均衡的能力,Spring Cloud Alibaba为什么还要去整合Ribbon呢?

个人认为,这主要是为了符合Spring Cloud标准。Spring Cloud Commons有个子项目 spring-cloud-loadbalancer ,该项目制定了标准,用来适配各种客户端负载均衡器(虽然目前实现只有Ribbon,但Hoxton就会有替代的实现了)。

Spring Cloud Alibaba遵循了这一标准,所以整合了Ribbon,而没有去使用Nacos Client提供的负载均衡能力。


同一集群优先调用

Spring Cloud Alibaba之服务发现组件 - Nacos一文中已经介绍过集群的概念以及作用,这里就不再赘述,加上上一小节中已经介绍过如何自定义负载均衡策略了,所以这里不再啰嗦而是直接上代码,实现代码如下:

package com.zj.node.contentcenter.configuration;

import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.client.naming.core.Balancer;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties;
import org.springframework.cloud.alibaba.nacos.ribbon.NacosServer;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 实现同一集群优先调用并基于随机权重的负载均衡策略
 *
 * @author 01
 * @date 2019-07-27
 **/
@Slf4j
public class NacosSameClusterWeightedRule extends AbstractLoadBalancerRule {

    @Autowired
    private NacosDiscoveryProperties discoveryProperties;

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
        // do nothing
    }

    @Override
    public Server choose(Object key) {
        // 获取配置文件中所配置的集群名称
        String clusterName = discoveryProperties.getClusterName();
        BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
        // 获取需要请求的微服务名称
        String serviceId = loadBalancer.getName();
        // 获取服务发现的相关API
        NamingService namingService = discoveryProperties.namingServiceInstance();

        try {
            // 获取该微服务的所有健康实例
            List<Instance> instances = namingService.selectInstances(serviceId, true);
            // 过滤出相同集群下的所有实例
            List<Instance> sameClusterInstances = instances.stream()
                    .filter(i -> Objects.equals(i.getClusterName(), clusterName))
                    .collect(Collectors.toList());

            // 相同集群下没有实例则需要使用其他集群下的实例
            List<Instance> instancesToBeChosen;
            if (CollectionUtils.isEmpty(sameClusterInstances)) {
                instancesToBeChosen = instances;
                log.warn("发生跨集群调用,name = {}, clusterName = {}, instances = {}",
                        serviceId, clusterName, instances);
            } else {
                instancesToBeChosen = sameClusterInstances;
            }

            // 基于随机权重的负载均衡算法,从实例列表中选取一个实例
            Instance instance = ExtendBalancer.getHost(instancesToBeChosen);
            log.info("选择的实例是:port = {}, instance = {}", instance.getPort(), instance);

            return new NacosServer(instance);
        } catch (NacosException e) {
            log.error("获取实例发生异常", e);
            return null;
        }
    }
}

class ExtendBalancer extends Balancer {

    /**
     * 由于Balancer类里的getHostByRandomWeight方法是protected的,
     * 所以通过这种继承的方式来实现调用,该方法基于随机权重的负载均衡算法,选取一个实例
     */
    static Instance getHost(List<Instance> hosts) {
        return getHostByRandomWeight(hosts);
    }
}

同样的,想要使用该负载均衡策略的话,在配置文件中配置一下即可:

user-center:
  ribbon:
    NFLoadBalancerRuleClassName: com.zj.node.contentcenter.configuration.NacosSameClusterWeightedRule

基于元数据的版本控制

在以上两个小节我们实现了基于Nacos权重的负载均衡策略及同一集群下优先调用的负载均衡策略,但在实际项目中,可能会面临多版本共存的问题,即一个微服务拥有不同版本的实例,并且这些不同版本的实例之间可能是互不兼容的。例如微服务A的v1版本实例无法调用微服务B的v2版本实例,只能够调用微服务B的v1版本实例。

而Nacos中的元数据就比较适合解决这种版本控制的问题,至于元数据的概念及配置方式已经在Spring Cloud Alibaba之服务发现组件 - Nacos一文中介绍过,这里主要介绍一下如何通过Ribbon去实现基于元数据的版本控制。

举个例子,线上有两个微服务,一个作为服务提供者一个作为服务消费者,它们都有不同版本的实例,如下:

  • 服务提供者有两个版本:v1、v2
  • 服务消费者也有两个版本:v1、v2

v1和v2是不兼容的。服务消费者v1只能调用服务提供者v1;消费者v2只能调用提供者v2。如何实现呢?下面我们来围绕该场景,实现微服务之间的版本控制。

综上,我们需要实现的主要有两点:

  • 优先选择同集群下,符合metadata的实例
  • 如果同集群下没有符合metadata的实例,就选择其他集群下符合metadata的实例

首先我们得在配置文件中配置元数据,元数据就是一堆的描述信息,以k - v形式进行配置,如下:

spring:
  cloud:
    nacos:
      discovery:
        # 指定nacos server的地址
        server-addr: 127.0.0.1:8848
        # 配置元数据
        metadata: 
          # 当前实例版本
          version: v1
          # 允许调用的提供者实例的版本
          target-version: v1

然后就可以写代码了,和之前一样,也是通过负载均衡策略实现,具体代码如下:

package com.zj.node.contentcenter.configuration;

import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import com.alibaba.nacos.client.utils.StringUtils;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.DynamicServerListLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties;
import org.springframework.cloud.alibaba.nacos.ribbon.NacosServer;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 基于元数据的版本控制负载均衡策略
 *
 * @author 01
 * @date 2019-07-27
 **/
@Slf4j
public class NacosFinalRule extends AbstractLoadBalancerRule {

    @Autowired
    private NacosDiscoveryProperties discoveryProperties;

    private static final String TARGET_VERSION = "target-version";
    private static final String VERSION = "version";

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
        // do nothing
    }

    @Override
    public Server choose(Object key) {
        // 获取配置文件中所配置的集群名称
        String clusterName = discoveryProperties.getClusterName();
        // 获取配置文件中所配置的元数据
        String targetVersion = discoveryProperties.getMetadata().get(TARGET_VERSION);

        DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
        // 需要请求的微服务名称
        String serviceId = loadBalancer.getName();
        // 获取该微服务的所有健康实例
        List<Instance> instances = getInstances(serviceId);

        List<Instance> metadataMatchInstances = instances;
        // 如果配置了版本映射,那么代表只调用元数据匹配的实例
        if (StringUtils.isNotBlank(targetVersion)) {
            // 过滤与版本元数据相匹配的实例,以实现版本控制
            metadataMatchInstances = filter(instances,
                    i -> Objects.equals(targetVersion, i.getMetadata().get(VERSION)));

            if (CollectionUtils.isEmpty(metadataMatchInstances)) {
                log.warn("未找到元数据匹配的目标实例!请检查配置。targetVersion = {}, instance = {}",
                        targetVersion, instances);
                return null;
            }
        }

        List<Instance> clusterMetadataMatchInstances = metadataMatchInstances;
        // 如果配置了集群名称,需筛选同集群下元数据匹配的实例
        if (StringUtils.isNotBlank(clusterName)) {
            // 过滤出相同集群下的所有实例
            clusterMetadataMatchInstances = filter(metadataMatchInstances,
                    i -> Objects.equals(clusterName, i.getClusterName()));

            if (CollectionUtils.isEmpty(clusterMetadataMatchInstances)) {
                clusterMetadataMatchInstances = metadataMatchInstances;
                log.warn("发生跨集群调用。clusterName = {}, targetVersion = {}, clusterMetadataMatchInstances = {}", clusterName, targetVersion, clusterMetadataMatchInstances);
            }
        }

        // 基于随机权重的负载均衡算法,选取其中一个实例
        Instance instance = ExtendBalancer.getHost(clusterMetadataMatchInstances);

        return new NacosServer(instance);
    }

    /**
     * 通过过滤规则过滤实例列表
     */
    private List<Instance> filter(List<Instance> instances, Predicate<Instance> predicate) {
        return instances.stream()
                .filter(predicate)
                .collect(Collectors.toList());
    }

    private List<Instance> getInstances(String serviceId) {
        // 获取服务发现的相关API
        NamingService namingService = discoveryProperties.namingServiceInstance();
        try {
            // 获取该微服务的所有健康实例
            return namingService.selectInstances(serviceId, true);
        } catch (NacosException e) {
            log.error("发生异常", e);
            return Collections.emptyList();
        }
    }
}

class ExtendBalancer extends Balancer {
    /**
     * 由于Balancer类里的getHostByRandomWeight方法是protected的,
     * 所以通过这种继承的方式来实现调用,该方法基于随机权重的负载均衡算法,选取一个实例
     */
    static Instance getHost(List<Instance> hosts) {
        return getHostByRandomWeight(hosts);
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring Cloud Alibaba之负载均衡组件 - Ribbon 的相关文章

  • 唯一索引或主键违规:“PRIMARY KEY ON PUBLIC.xxx”; SQL语句

    每当我的应用程序启动时 我都会收到以下错误消息 Caused by org h2 jdbc JdbcSQLException Unique index or primary key violation PRIMARY KEY ON PUBL
  • 如何在日期选择器中设置不在当前月份的单元格的样式

    我目前正在为我的 JavaFX 应用程序制作注册表 问题是 当日期选择器中的单元格不在页面的月份上时 我想让该单元格变灰 让我们看看我当前的日期选择器 我的日期选择器 正如您所看到的 我希望下个月的日期 27 日 28 日 30 日以及 1
  • 如何测试 JUnit 测试的 Comparator?

    我需要测试 Compare 方法 但我对如何测试感到困惑 我可以看看该怎么做吗 public class MemberComparator implements Comparator
  • 线程自动利用多个CPU核心?

    假设我的应用程序运行 2 个线程 例如渲染线程和游戏更新线程 如果它在具有多核 CPU 当今典型 的移动设备上运行 我是否可以期望线程在可能的情况下自动分配给不同的核心 我知道底层操作系统内核 Android linux内核 决定调度 我的
  • manifest.mf 文件的附加内容的约定?

    Java JAR 中的 MANIFEST MF 文件是否有任何超出 MANIFEST MF 约定的约定 JAR规范 http download oracle com javase 1 4 2 docs guide jar jar html
  • JNI 不满意链接错误

    我想创建一个简单的 JNI 层 我使用Visual studio 2008创建了一个dll Win 32控制台应用程序项目类型 带有DLL作为选项 当我调用本机方法时 出现此异常 Exception occurred during even
  • 如何查找 Android 设备中的所有文件并将它们放入列表中?

    我正在寻求帮助来列出 Android 外部存储设备中的所有文件 我想查找所有文件夹 包括主文件夹的子文件夹 有办法吗 我已经做了一个基本的工作 但我仍然没有得到想要的结果 这不起作用 这是我的代码 File files array file
  • Java8无符号算术

    据广泛报道 Java 8 具有对无符号整数的库支持 然而 似乎没有文章解释如何使用它以及有多少可能 有些函数 例如 Integer CompareUnsigned 很容易找到 并且似乎可以实现人们所期望的功能 但是 我什至无法编写一个简单的
  • CXF Swagger2功能添加安全定义

    我想使用 org apache cxf jaxrs swagger Swagger2Feature 将安全定义添加到我的其余服务中 但是我看不到任何相关方法或任何有关如何执行此操作的资源 下面是我想使用 swagger2feature 生成
  • 使用 ANTLR 为 java 源代码生成抽象语法树

    如何使用 ANTLR 从 java src 代码生成 AST 有什么帮助吗 好的 步骤如下 前往ANTLR站点 http www antlr org 并下载最新版本 下载Java g和JavaTreeParser g文件来自here htt
  • 如何在 Java 中禁用 System.out 以提高速度

    我正在用 Java 编写一个模拟重力的程序 其中有一堆日志语句 到 System out 我的程序运行速度非常慢 我认为日志记录可能是部分原因 有什么方法可以禁用 System out 以便我的程序在打印时不会变慢 或者我是否必须手动检查并
  • OnClick 事件中的 finish() 如何工作?

    我有一个Activity一键退出Activity 通过layout xml我必须设置OnClick事件至cmd exit调用 this finish 效果很好 public void cmd exit View editLayout thi
  • Microsoft Graph 身份验证 - 委派权限

    我可以使用 Microsoft Graph 访问资源无需用户即可访问 https developer microsoft com en us graph docs concepts auth v2 service 但是 此方法不允许我访问需
  • 序列化对象以进行单元测试

    假设在单元测试中我需要一个对象 其中所有 50 个字段都设置了一些值 我不想手动设置所有这些字段 因为这需要时间而且很烦人 不知何故 我需要获得一个实例 其中所有字段都由一些非空值初始化 我有一个想法 如果我要调试一些代码 在某个时候我会得
  • 在具有相同属性名称的不同数据类型上使用 ModelMapper

    我有两节课说Animal AnimalDto我想用ModelMapper将 Entity 转换为 DTO 反之亦然 但是对于具有相似名称的一些属性 这些类应该具有不同的数据类型 我该如何实现这一目标 动物 java public class
  • Java中接口作为方法参数

    前几天去面试 被问到了这样的问题 问 反转链表 给出以下代码 public class ReverseList interface NodeList int getItem NodeList nextNode void reverse No
  • 归并排序中的递归:两次递归调用

    private void mergesort int low int high line 1 if low lt high line 2 int middle low high 2 line 3 mergesort low middle l
  • 尝试使用 Ruby Java Bridge (RJB) gem 时出现错误“无法创建 Java VM”

    我正在尝试实现 Ruby Java Bridge RJB gem 来与 JVM 通信 以便我可以运行 Open NLP gem 我在 Windows 8 上安装并运行了 Java 所有迹象 至少我所知道的 都表明 Java 已安装并可运行
  • 将多模块 Maven 项目导入 Eclipse 时出现问题 (STS 2.5.2)

    我刚刚花了最后一个小时查看 Stackoverflow com 上的线程 尝试将 Maven 项目导入到 Spring ToolSuite 2 5 2 中 Maven 项目有多个模块 当我使用 STS 中的 Import 向导导入项目时 所
  • 应用程序关闭时的倒计时问题

    我制作了一个 CountDownTimer 代码 我希望 CountDownTimer 在完成时重新启动 即使应用程序已关闭 但它仅在应用程序正在运行或重新启动应用程序时重新启动 因此 如果我在倒计时为 00 10 分钟 秒 时关闭应用程序

随机推荐

  • Qt项目实战2:图片查看器QImageViewer

    在博文Qt学习笔记2 QMainWindow和QWidget的区别中介绍了使用空的Qt项目创建带有菜单栏 工具栏的界面 这里 使用一个简单的图片查看器项目 来熟悉一下Qt的图片显示和基本操作 该项目实现的主要功能 实现图片的打开 关闭 居中
  • Head First Design Mode(12)-状态模式

    该系列文章系个人读书笔记及总结性内容 任何组织和个人不得转载进行商业活动 状态模式 状态模式和策略模式是 双胞胎 在出生时才分开 策略模式是围绕可以互换的算法来创建成功业务的 状态模式则是通过改变对象内部的状态来帮助对象控制自己的行为 状态
  • 麻雀虽小五脏俱全,中小企业的知识管理须重视

    编者按 在知识资产越来越重要的市场发展环境下 做好企业知识管理对中小企业来说十分重要 本文从企业知识管理的重要性说起 分析了现代中小企业面临的知识管理困境 并进一步提出天翎KMS是如何帮助企业突破这种困境的 概要 1 知识管理的重要性 2
  • 表白墙 -- 前后端代码详解

    表白墙 前后端代码详解 一 前端 二 后端实现 2 1 需求 2 2 创建项目及初始化 2 3 实现提交数据 存档 2 3 1 实现 doPost 2 3 2 构造请求 修改 html 文件 2 3 3 验证 2 4 实现获取数据 读档 2
  • ubuntu-tensorflow环境配置

    前言 刚接触ubuntu和深度学习框架的时候 都可能不清楚 为什么要用ubuntu系统 windows不是很好用吗 其实很多的开发是基于linux系统的 不一定是ubuntu 但是ubuntu是大家比较熟悉的 开源的系统 pc上认为wind
  • 全桥逆变电路

    全桥逆变电路知识汇总 每一部分详细可以点击对应的下方链接 单元一 全桥逆变电路的驱动部分 驱动部分 单元二 全桥逆变电路MOS IGBT管搭建 桥式电路 单元三 阻抗匹配电路 阻抗匹配 对全桥逆变电路知识感兴趣的可以咨询博主QQ 28593
  • HDU6703 Fishing Master

    比赛的时候想的是尽量先炖鱼 果然还是赛后牛逼 反应过来无论怎样总时间内一定有所有的炖鱼时间 所以只需要尽量缩减我们的钓鱼时间即可 一 在炖鱼时间内可以把所有鱼都钓上来 那ans k t 1 n 二 不能在炖鱼时间内把所有鱼都钓上来 即每次钓
  • 小学生学Arduino---------点阵(三)动态的显示与清除

    学习目标 1 理解 整数值 的概念与使用 2 理解 N 1 指令的意义 3 掌握 反复执行多次 指令的使用 4 掌握屏幕模块的清除功能指令 5 理解 反复执行 指令与 反复执行多次 指令的嵌套使用 6 搭建电路图 7 编写程序 效果 整数包
  • 物联网LoRa系列-7:LoRa终端模组ASR6505详解

    1 ASR6505简介 在2018年 Semtech公司推出了在综合性能上优于SX1278 SX1276射频收发器的SX1268 SX1262射频收发器 各大厂商也纷纷开始根据新的射频收发器来升级方案 意在取代性能无法适应市场的SX1278
  • stm32f103串口接收队列,DMA循环模式+空闲中断

    串口通讯最主要的就是要不丢数据 不丢帧 基本设想就是建立一个大的串口缓冲区 串口接收到的数据使用循环队列的方式全部往这个缓冲区放 不过这种方式需要把串口缓冲区弄大一点 防止数据覆盖 在stm32中 利用DMA 空闲中断很容易做到这一点 只需
  • Iterm2使用指南

    Iterm2使用指南 1 安装iterm2 官网直接下载安装 官网 2 个性化设置 iterm2 gt preferences gt profiles 点击新建一个profile 下面可以设置透明度 模糊度 背景图片 设置窗口的主题配色 下
  • jlpt报名系统显示网络错误和服务器忙,【JLPT】12月日语等级考试报名经验与技巧...

    2017年12月日语能力考 JLPT 报名日期即将到来 N1考试 8月28日 N2考试 8月29日 其他级别考试 8月31日 这次日语考试报名的 抢椅子 大战即将展开 为了能够顺利地抢到考位 未名天日语小编还是建议大家提前做好一切报名前的准
  • CSDN-markdown编辑器指导文件

    这里写自定义目录标题 欢迎使用Markdown编辑器 新的改变 功能快捷键 合理的创建标题 有助于目录的生成 如何改变文本的样式 插入链接与图片 如何插入一段漂亮的代码片 生成一个适合你的列表 创建一个表格 设定内容居中 居左 居右 Sma
  • SpringBoot+Vue+Uniapp搭建的Java版本的ChatGPT智能Ai对话系统

    chatgpt java system 介绍 SpringBoot Vue Uniapp搭建的Java版本的ChatGPT智能Ai对话系统 小程序和H5包含智能Ai对话 精美Ai壁纸 知识付费商城 积分 会员 分享等公功能 后端管理包括系统
  • 虚拟存储器

    文章目录 虚拟存储器 一 虚拟存储器概述 二 页式虚拟存储器 例题分析 三 段式与段页式虚拟存储器 虚拟存储器 一 虚拟存储器概述 1 引入原因 高速的主存容量满足不了要求 虚拟存储器 在操作系统及辅助硬件的管理下 由主存和大容量外存所构成
  • R语言入门——笔记(二)--包(package)的使用及RStudio的使用,加载包和数据集

    一 1 包 package 的使用 包是扩展R基本功能的机制 集成了众多函数 如果想使用某些函数而当前R中不存在 就可以找对应函数的包直接使用 去哪里找包 如何使用包 CRAN install packages Bioconductor G
  • unity3d GUI.Button 自定义字体大小及透明背景

    代码 public class MyButton MonoBehaviour public Texture buttonTexture Use this for initialization void Start Update is cal
  • 【通信原理课设--基于MATLAB/Simulink的2ASK数字带通传输系统建模与仿真】课程设计报告大纲

    1 摘要 研究现状 对本设计模型的介绍 2 目录 自动生成即可 3 概述 3 1 课程设计背景 该设计的应用现状 针对应用部分 3 2 国内外研究背景 对该设计的研究状况 针对学术研究 3 3 报告组织形式 介绍本报告的框架结构 4 系统模
  • python图像处理Pillow学习笔记

    Pillow学习笔记 1 安装pillow pip install pillow 2 图像处理简介 图像处理RGB色彩模式 R red 0 255 G green 0 255 B blue 0 255 像素阵列 每个点就是一个像素每个点都由
  • Spring Cloud Alibaba之负载均衡组件 - Ribbon

    负载均衡 我们都知道在微服务架构中 微服务之间总是需要互相调用 以此来实现一些组合业务的需求 例如组装订单详情数据 由于订单详情里有用户信息 所以订单服务就得调用用户服务来获取用户信息 要实现远程调用就需要发送网络请求 而每个微服务都可能会