Spring Cloud LoadBalancer 入门与实战

news2024/9/29 19:30:45

一、什么是 LoadBalancer?

LoadBalancer(负载均衡器) 是一种网络设备或软件机制,用于分发传入的网络流量负载(请求)到多个后端目标服务器上,从而实现系统资源的均衡利用和提高系统的可用性和新能。

1.1 负载均衡分类

负载均衡分为服务器端负载均衡和客户端负载均衡

1. 服务器端负载均衡指的是存放在服务器端的负载均衡器,例如 Nginx、HAProxy、F5 等。

2. 客户端负载均衡指的是嵌套在客户端的负载均衡器,例如 Ribbon、Spring Cloud LoadBalancer。

1.2 常见的负载均衡策略

  1. 询轮:按照顺序将每个新的请求分发给后端服务器,依次循环。这是一种最简单的负载均衡策略,适用于后端服务器的性能相近,且每个请求的处理时间大致相同的情况。

  2. 随机选择:随机选择一个后端服务器来处理每个新的请求。这种策略适用于后端服务器性能相似,且每个请求的处理时间相近的情况,但不保证请求的分发是均匀的。

  3. 最少连接:最少连接策略将请求分发给当前连接数最少的后端服务器。这可以确保负载均衡在后端服务器的连接负载上均衡。但需要维护连接计数。

  4. IP 哈希:IP 哈希策略使用客户端的 IP 地址来计算哈希值,然后将请求发送到与哈希值对应的后端服务器。这种策略可用于确保来自同一客户端的请求都被发送到同一台后端服务器,适用于需要会话保持的情况。

  5. 加权轮询:加权轮询给每个后端服务器分配一个权重值,然后按照权重值比例来分发请求。这可以用来处理后端服务器性能不均衡的情况,将更多的请求发给性能更高的服务器。

  6. 加权随机选择:加权随机选择与加权轮询类似,但是按照权重值来随机选择后端服务器。这也可以用来处理后端服务器性能不均衡的情况,但是分发更随机。

  7. 最短响应时间 :最短响应时间策略会测量每个后端服务器的响应时间,并将请求发送到响应时间最短的服务器。这中策略可以确保客户端获得最快的响应,适用于要求低延迟的应用。

二、默认负载均衡策略

Spring Cloud LoadBalancer 负载均衡策略默认的是轮询,这一点可以通过 Spring Cloud LoadBalancer 的配置类 LoadBalancerClientConfiguration 中发现,它的部分源码如下:

@ConditionalOnDiscoveryEnabled
public class LoadBalancerClientConfiguration {
    private static final int REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER = 193827465;

    public LoadBalancerClientConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean
    public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty("loadbalancer.client.name");
        return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }
    //...
}

 RoundRobinLoadBalancer 核心实现源码:

private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
        if (instances.isEmpty()) {
            if (log.isWarnEnabled()) {
                log.warn("No servers available for service: " + this.serviceId);
            }

            return new EmptyResponse();
        } else if (instances.size() == 1) {
            return new DefaultResponse((ServiceInstance)instances.get(0));
        } else {
            //当有多个实例时,& Integer.MAX_VALUE 摒弃负数,确保下标为正数
            int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;
            //取模进行轮询
            ServiceInstance instance = (ServiceInstance)instances.get(pos % instances.size());
            return new DefaultResponse(instance);
        }
    }

三、随机负载均衡策略

代码实现

设置局部负载均衡器:

//balancer-service:注册实例名称
//RandomLoadBalanceConfig.class 自定义负载均衡器类名
@LoadBalancerClient(name = "balancer-service",configuration = RandomLoadBalanceConfig.class)
public class RandomLoadBalanceConfig {
    @Bean
    public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty("loadbalancer.client.name");
        return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }
}

设置全局负载均衡器:

@SpringBootApplication
@EnableFeignClients
@LoadBalancerClients(defaultConfiguration= RandomLoadBalanceConfig.class)
public class ConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

}

四、设置Nacos权重负载均衡器

Nacos 中支持两种负载均衡器,一种是权重负载均衡器,另一种是第三方 CMDB(地域就近访问)标签负载均衡器,我们可以将 Spring Cloud Loadbalancer 直接配置为 Nacos 的负载均衡器,它默认就是权重负载均衡策略。

它的配置有以下两步:

1. 创建 Nacos 负载均衡器

@LoadBalancerClient(name = "balancer-service",configuration = NacosLoadBalanceConfig.class)
public class NacosLoadBalanceConfig {
    @Resource
    private NacosDiscoveryProperties nacosDiscoveryProperties;
    @Bean
    public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty("loadbalancer.client.name");
        return new NacosLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name,nacosDiscoveryProperties);
    }
}

五、创建自定义负载均衡器 

5.1 创建自定义负载均衡器

自定义负载均衡器只需要参考官方负载均衡器写就可

public class CustomizeLoadBalance implements ReactorServiceInstanceLoadBalancer {
    private static final Logger log = LoggerFactory.getLogger(NacosLoadBalancer.class);
    private final String serviceId;
    private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
    private final NacosDiscoveryProperties nacosDiscoveryProperties;
    private static final String IPV4_REGEX = "((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}";
    private static final String IPV6_KEY = "IPv6";
    public static String ipv6;
    @Autowired
    private InetIPv6Utils inetIPv6Utils;

    @PostConstruct
    public void init() {
        String ip = this.nacosDiscoveryProperties.getIp();
        if (StringUtils.isNotEmpty(ip)) {
            ipv6 = Pattern.matches("((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}", ip) ? (String)this.nacosDiscoveryProperties.getMetadata().get("IPv6") : ip;
        } else {
            ipv6 = this.inetIPv6Utils.findIPv6Address();
        }

    }

    private List<ServiceInstance> filterInstanceByIpType(List<ServiceInstance> instances) {
        if (StringUtils.isNotEmpty(ipv6)) {
            List<ServiceInstance> ipv6InstanceList = new ArrayList();
            Iterator var3 = instances.iterator();

            while(var3.hasNext()) {
                ServiceInstance instance = (ServiceInstance)var3.next();
                if (Pattern.matches("((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}", instance.getHost())) {
                    if (StringUtils.isNotEmpty((CharSequence)instance.getMetadata().get("IPv6"))) {
                        ipv6InstanceList.add(instance);
                    }
                } else {
                    ipv6InstanceList.add(instance);
                }
            }

            if (ipv6InstanceList.size() == 0) {
                return (List)instances.stream().filter((instancex) -> {
                    return Pattern.matches("((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}", instancex.getHost());
                }).collect(Collectors.toList());
            } else {
                return ipv6InstanceList;
            }
        } else {
            return (List)instances.stream().filter((instancex) -> {
                return Pattern.matches("((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}", instancex.getHost());
            }).collect(Collectors.toList());
        }
    }

    public CustomizeLoadBalance(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId, NacosDiscoveryProperties nacosDiscoveryProperties) {
        this.serviceId = serviceId;
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
        this.nacosDiscoveryProperties = nacosDiscoveryProperties;
    }

    public Mono<Response<ServiceInstance>> choose(Request request) {
        ServiceInstanceListSupplier supplier = (ServiceInstanceListSupplier)this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
        return supplier.get(request).next().map(this::getInstanceResponse);
    }

    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> serviceInstances) {
        if (serviceInstances.isEmpty()) {
            log.warn("No servers available for service: " + this.serviceId);
            return new EmptyResponse();
        } else {
            try {
                String clusterName = this.nacosDiscoveryProperties.getClusterName();
                List<ServiceInstance> instancesToChoose = serviceInstances;
                if (StringUtils.isNotBlank(clusterName)) {
                    List<ServiceInstance> sameClusterInstances = (List)serviceInstances.stream().filter((serviceInstance) -> {
                        String cluster = (String)serviceInstance.getMetadata().get("nacos.cluster");
                        return StringUtils.equals(cluster, clusterName);
                    }).collect(Collectors.toList());
                    if (!CollectionUtils.isEmpty(sameClusterInstances)) {
                        instancesToChoose = sameClusterInstances;
                    }
                } else {
                    log.warn("A cross-cluster call occurs,name = {}, clusterName = {}, instance = {}", new Object[]{this.serviceId, clusterName, serviceInstances});
                }
                //获取request 对象
                ServletRequestAttributes attributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                HttpServletRequest request= attributes.getRequest();
                String ipAddress=request.getRemoteAddr();
                System.out.println(ipAddress);
                int hash=ipAddress.hashCode();
                //自定义负载据衡器策略,通过ip hash后,取模获取下标
                int index=(hash&Integer.MAX_VALUE)%serviceInstances.size();
                System.out.println(index);
                //获取服务实例
                ServiceInstance instance=serviceInstances.get(index);
                return new DefaultResponse(instance);
            } catch (Exception var5) {
                log.warn("NacosLoadBalancer error", var5);
                return null;
            }
        }
    }
}

5.2 封装并设置自定义负载均衡器

@LoadBalancerClient(name = "balancer-service" ,configuration = CoustomizeHashLoadBalanceConfig.class)
public class CoustomizeHashLoadBalanceConfig {
    @Resource
    private NacosDiscoveryProperties nacosDiscoveryProperties;
    @Bean
    public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty("loadbalancer.client.name");
        return  new CustomizeLoadBalance(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name,nacosDiscoveryProperties);
    }
}

六、缓存

Spring Cloud LoadBalancer 在获取实例时有两种选择:

  1. 即时获取:每次从注册中心得到最新的健康实例,效果好、开销大
  2. 缓存服务列表:每次得到服务列表之后,缓存一段时间,这样既能保证性能,同时也能兼容一定的及时性。

Spring Cloud LoadBalancer 中默认开启了缓存服务列表的功能。

Spring Cloud LoadBalancer 默认缓存的重要特性有两项:

  1. 缓存的过期时间为 35s
  2. 缓存保存个数为 256 个
spring:
  cloud:
    loadbalancer:
      cache:
        ttl: 10
        capacity: 128
        #不开启缓存
        enabled:false

注意:尽管在不开启缓存对于开发和测试很有用,但其效率远低于将缓存开启,因此建议在生产环境中始终启用缓存

七、执行原理

OpenFeign 底层是通过 HTTP 客户端对象 RestTemplate 实现接口请求的,而负载均衡器的作用只是在请求客户端发送请求之前,得到一个服务的地址给到 RestTemplate 对象,而 Spring Cloud LoadBalancer 的整体类图如下:

通过查看 Spring Cloud LoadBalancer 源码可以发现,@LoadBalancer 注解由 spring-cloud-commons 实现,查看实现逻辑我们发现 spring-cloud-commons 存在自动配置类 LoadBalancerAutoConfiguration,当满足条件时,将自动创建 LoadBalancerInterceptor 并注入到RestTemplate 中,部分源码如下: 

    @Configuration(
        proxyBeanMethods = false
    )
    @Conditional({RetryMissingOrDisabledCondition.class})
    static class LoadBalancerInterceptorConfig {
        LoadBalancerInterceptorConfig() {
        }

        @Bean
        public LoadBalancerInterceptor loadBalancerInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
            return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
        }

        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
            return (restTemplate) -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
        }
    }

LoadBalancerInterceptor 又实现了 ClientHttpRequestInterceptor 接口,并实现 Intercept 方法,用于实现负载均衡的拦截处理,实现源码:

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    private LoadBalancerClient loadBalancer;
    private LoadBalancerRequestFactory requestFactory;

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
        this.loadBalancer = loadBalancer;
        this.requestFactory = requestFactory;
    }

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
        this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
    }

    public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
        URI originalUri = request.getURI();
        String serviceName = originalUri.getHost();
        Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
        return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
    }
}

其中有一个 LoadBalancerClient 负载均衡客户端,用于进行负载均衡逻辑,从服务列表中选择出一个服务进行地址进行调用,默认实现为 BlockingLoadBalancerClient,源码如下:

    public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
        String hint = this.getHint(serviceId);
        LoadBalancerRequestAdapter<T, TimedRequestContext> lbRequest = new LoadBalancerRequestAdapter(request, this.buildRequestContext(request, hint));
        Set<LoadBalancerLifecycle> supportedLifecycleProcessors = this.getSupportedLifecycleProcessors(serviceId);
        supportedLifecycleProcessors.forEach((lifecycle) -> {
            lifecycle.onStart(lbRequest);
        });
        ServiceInstance serviceInstance = this.choose(serviceId, lbRequest);
        if (serviceInstance == null) {
            supportedLifecycleProcessors.forEach((lifecycle) -> {
                lifecycle.onComplete(new CompletionContext(Status.DISCARD, lbRequest, new EmptyResponse()));
            });
            throw new IllegalStateException("No instances available for " + serviceId);
        } else {
            return this.execute(serviceId, serviceInstance, lbRequest);
        }
    }

public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
        DefaultResponse defaultResponse = new DefaultResponse(serviceInstance);
        Set<LoadBalancerLifecycle> supportedLifecycleProcessors = this.getSupportedLifecycleProcessors(serviceId);
        Request lbRequest = request instanceof Request ? (Request)request : new DefaultRequest();
        supportedLifecycleProcessors.forEach((lifecycle) -> {
            lifecycle.onStartRequest(lbRequest, new DefaultResponse(serviceInstance));
        });

        try {
            T response = request.apply(serviceInstance);
            Object clientResponse = this.getClientResponse(response);
            supportedLifecycleProcessors.forEach((lifecycle) -> {
                lifecycle.onComplete(new CompletionContext(Status.SUCCESS, lbRequest, defaultResponse, clientResponse));
            });
            return response;
        } catch (IOException var9) {
            supportedLifecycleProcessors.forEach((lifecycle) -> {
                lifecycle.onComplete(new CompletionContext(Status.FAILED, var9, lbRequest, defaultResponse));
            });
            throw var9;
        } catch (Exception var10) {
            supportedLifecycleProcessors.forEach((lifecycle) -> {
                lifecycle.onComplete(new CompletionContext(Status.FAILED, var10, lbRequest, defaultResponse));
            });
            ReflectionUtils.rethrowRuntimeException(var10);
            return null;
        }
    }

    public ServiceInstance choose(String serviceId) {
        return this.choose(serviceId, ReactiveLoadBalancer.REQUEST);
    }

    //通过不同的负载均衡器选择不同的服务
    public <T> ServiceInstance choose(String serviceId, Request<T> request) {
        //获取负载均衡器
        ReactiveLoadBalancer<ServiceInstance> loadBalancer = this.loadBalancerClientFactory.getInstance(serviceId);
        if (loadBalancer == null) {
            return null;
        } else {
            //根据负载均衡器得到一个请求实例
            Response<ServiceInstance> loadBalancerResponse = (Response)Mono.from(loadBalancer.choose(request)).block();
            return loadBalancerResponse == null ? null : (ServiceInstance)loadBalancerResponse.getServer();
        }
    }

 小结:OpenFeign 通过拦截器调用 RestTemplate 实现和服务的交互,RestTemplate 通过HttpClient 进行请求,而在交互之前通过调用 Spring Cloud LoadBalancer中的LoadBalancerClient中的choose 方法 ,根据服务 id 和负载均衡策略得到某个服务地址,再进行调用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1913652.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序中wx.navigateBack()页面栈返回上一页时执行上一页的方法或修改上一页的data属性值

let pages getCurrentPages();let prevPage pages[pages.length - 2]; // 获取上一个页面实例对象console.log(prevPage) //打印信息// 在 wx.navigateBack 的 success 回调中执行需要的方法wx.navigateBack({delta: 1, // 返回上一页success: function() {//修改上一页的属性…

Oracle基础以及一些‘方言’(二)

1、Oracle的查询语法结构 Oracle 的单表查询的语法结构&#xff1a; SELECT 1 FROM 2 WHERE 3 GROUP BY 4 HAVING 5 ORDER BY 6 其每个关键词的功能与MySQL中的功能已知&#xff0c;不过分页查询的关键词 limit 并不在Oracle的语法结构中。伪列&#xff1a; 在 Oracle 的表的使…

三品PLM管理系统软件:制造企业工程变更管理的革新者

在当今快速变化的市场环境中&#xff0c;制造企业面临着前所未有的挑战。客户需求的不断变化、供应链的波动、设计过程中的不确定性以及产品生命周期的缩短&#xff0c;都要求企业能够迅速响应并适应这些变化。工程变更管理作为企业响应市场变化、提升产品竞争力的关键环节&…

Loadlibrary failed with error 87:参数错误

问题描述&#xff1a; win10 系统在安装 Photoshop 2022 版后&#xff0c;点击桌面图标提示&#xff1a;Loadlibrary failed with error 87&#xff1a;参数错误&#xff0c;反复出现&#xff0c;反复确定&#xff0c;直至软件关闭。 解决方法&#xff1a; 1. 找到 C:\Window…

Kafka安装使用指南

Kafka是一种高吞吐量的分布式发布订阅消息系统。 Kafka启动方式有Zookeeper和Kraft&#xff0c;两种方式只能选择其中一种启动&#xff0c;不能同时使用。 【Kafka安装】 Kafka下载 https://downloads.apache.org/kafka/3.7.1/kafka_2.13-3.7.1.tgz Kafka解压 tar -xzf kafka_…

服务器数据恢复—raid5阵列热备盘没有激活导致阵列崩溃的数据恢复案例

服务器存储数据恢复环境&#xff1a; 一台EMC存储中有一组raid5磁盘阵列&#xff0c;划分1个lun供小型机使用&#xff0c;上层采用ZFS文件系统。 服务器存储故障&#xff1a; 一台有一组raid5磁盘阵列的存储在运行过程中突然崩溃。管理员检查发现存储中的raid5阵列有两块硬盘离…

大模型时代:人工智能与大数据平台的深度融合

在当今的大数据时代&#xff0c;数据已经成为驱动业务增长和创新的关键因素。与此同时&#xff0c;随着人工智能技术的不断进步&#xff0c;AI在大规模数据处理和分析方面的能力日益强大。因此&#xff0c;将人工智能与大数据平台相结合&#xff0c;可以为企业带来巨大的商业价…

✈️一文带你入门【NestJS】

✈️引言 在现代Web开发领域&#xff0c;框架和技术的迭代速度令人咋舌。其中&#xff0c;NestJS作为一款基于Node.js的后端框架&#xff0c;以其卓越的设计理念和强大的功能集&#xff0c;迅速吸引了众多开发者的眼球。本文将带你深入了解NestJS的起源、发展&#xff0c;以及…

LangChain教程 – 如何构建自定义知识聊天机器人

您可能已经了解到过去几个月发布的大量 AI 应用程序。您甚至可能已经开始使用其中的一些。 ChatPDF和CustomGPT AI等 AI 工具已经对人们变得非常有用——这是有充分理由的。您需要滚动浏览 50 页文档才能找到简单答案的日子已经一去不复返了。相反&#xff0c;您可以依靠 AI 来…

mysql 9 新特性

mysql9新特性 新特性Audit Log NotesC API NotesCharacter Set SupportCompilation NotesComponent NotesConfiguration NotesData Dictionary NotesData Type NotesDeprecation and Removal NotesEvent Scheduler NotesJavaScript ProgramsOptimizer NotesPerformance Schema …

Linux初始化新的git仓库

1.在git服务器上找到项目常部署的git地址可以根据其他项目的git地址确认 例如ssh://git192.168.10.100/opt/git/repository.git 用户名&#xff1a;git&#xff08;前面的是用户&#xff09; 服务器地址&#xff1a;192.168.10.100 git仓库路径&#xff1a;/opt/git/ 2.在服务器…

js 图片放大镜

写购物项目的时候&#xff0c;需要放大图片&#xff0c;这里用js写了一个方法&#xff0c;鼠标悬浮的时候放大当前图片 这个是class写法 <!--* Descripttion: * Author: 苍狼一啸八荒惊* LastEditTime: 2024-07-10 09:41:34* LastEditors: 夜空苍狼啸 --><!DOCTYPE …

IP 地址与 CDN 性能优化

内容分发网络&#xff08;CDN&#xff09;就是通过内容分配到离用户最优的服务器来提高访问速度。而IP地址如何分配与管理就是CND技术的基础。本文将来探讨介绍CDN中的IP地址分配与管理&#xff0c;以及如何通过CDN优化网络性能。 首先我们来了解CDN的基本原理 CDN是一种分布式…

数据库之DML

1&#xff0c;创建表 mysql> create table student(-> id int primary key,-> name varchar(20) not null,-> grade float-> );插入记录 mysql> insert into student values(1,monkey,98.5); Query OK, 1 row affected (0.01 sec)一次性插入多条记录 mysql…

百问网全志D1h开发板MIPI屏适配

MIPI屏适配 100ASK-D1-H_DualDisplay-DevKit V11 1. 显示适配 1.1 修改设备树 1.1.1 修改内核设备树 进入目录&#xff1a; cd /home/ubuntu/tina-d1-h/device/config/chips/d1-h/configs/nezha/linux-5.4修改board.dts: &lcd0 {lcd_used <1>;lcd…

MP | 基于kmer的泛基因组分析方法及应用

2024年5月24日&#xff0c;中国农业大学分子设计育种前沿科学中心作物杂种优势与利用教育部重点实验室郭伟龙与姚颖垠团队在《Molecular Plant》发表了题为《A k-mer-based pangenome approach for cataloging seed-storage-protein genes in wheat to facilitate genotype-to-…

成都云飞浩容文化传媒有限公司怎么样?

在电商行业风起云涌的今天&#xff0c;成都云飞浩容文化传媒有限公司以其独特的视角和专业的服务&#xff0c;成为了这一领域的佼佼者。今天&#xff0c;就让我们一起走进云飞浩容&#xff0c;探索其背后的故事和成功的秘诀。 一、专注电商&#xff0c;用心服务 成都云飞浩容文…

HarmonyOS Next应用开发之系统概述

一、鸿蒙系统概述 鸿蒙系统可以分为华为鸿蒙系统&#xff08;HUAWEI HarmonyOS&#xff09;和开源鸿蒙系统&#xff08;OpenHarmony&#xff09;&#xff0c;华为鸿蒙系统是基于OpenHarmony基础之上开发的商业版操作系统。他们二者的关系可以用下图来表示&#xff1a; 1.1、…

翰德恩咨询赋能材料行业上市公司,共筑IPD管理体系新篇章

赋能背景概览 坐落于江苏的某材料行业领军企业&#xff0c;作为国内无机陶瓷膜元件及成套设备领域的佼佼者&#xff0c;以其庞大的生产规模、丰富的产品系列及卓越的研发实力&#xff0c;屹立行业之巅二十余年。公司不仅在新材料研发、技术创新、工艺设计、设备制造及整体解决…

智能眼镜火热发展 AI+AR或将成为主流趋势?

日前&#xff0c;The Verge 发布消息称&#xff0c;AI 智能音频眼镜 Ray-Ban Meta 的销量可能已突破 100 万。Meta 在博客中也指出&#xff0c;Ray-Ban Meta 取得了超预期的市场表现&#xff0c;眼镜的销售速度比生产速度还要快&#xff0c;目前团队正着手于推出更多新款式。Ra…