当前位置: 首页 > news >正文

SpringBoot接口 - 如何实现接口限流之单实例

在以SpringBoot开发Restful接口时,当流量超过服务极限能力时,系统可能会出现卡死、崩溃的情况,所以就有了降级和限流。在接口层如何做限流呢? 本文主要回顾限流的知识点,并实践单实例限流的一种思路。

  • SpringBoot接口 - 如何实现接口限流之单实例
    • 准备知识点
      • 为什么要限流
      • 限流有哪些常见思路?
    • 实现思路
      • 定义RateLimit注解
      • 定义AOP
      • 自定义相关异常
      • 统一结果返回封装
      • controller接口
      • 接口测试
      • 上述实现方案的槽点
    • 示例源码

# 准备知识点

主要的知识点,请参考架构之高并发:限流, 这里小结下。

# 为什么要限流

每个系统都有服务的上线,所以当流量超过服务极限能力时,系统可能会出现卡死、崩溃的情况,所以就有了降级和限流。限流其实就是:当高并发或者瞬时高并发时,为了保证系统的稳定性、可用性,系统以牺牲部分请求为代价或者延迟处理请求为代价,保证系统整体服务可用。

# 限流有哪些常见思路?

  • 从算法上看

令牌桶(Token Bucket)、漏桶(leaky bucket)和计数器算法是最常用的三种限流的算法。

  • 单实例

应用级限流方式只是单应用内的请求限流,不能进行全局限流。

  1. 限流总资源数
  2. 限流总并发/连接/请求数
  3. 限流某个接口的总并发/请求数
  4. 限流某个接口的时间窗请求数
  5. 平滑限流某个接口的请求数
  6. Guava RateLimiter
  • 分布式

我们需要分布式限流接入层限流来进行全局限流。

  1. redis+lua实现中的lua脚本
  2. 使用Nginx+Lua实现的Lua脚本
  3. 使用 OpenResty 开源的限流方案
  4. 限流框架,比如Sentinel实现降级限流熔断

# 实现思路

主要思路:AOP拦截自定义的RateLimit注解,在AOP中通过Guava RateLimiter; Guava RateLimiter提供了令牌桶算法实现:平滑突发限流(SmoothBursty)和平滑预热限流(SmoothWarmingUp)实现。

# 定义RateLimit注解

package tech.pdai.ratelimit.guava.config.ratelimit;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author pdai*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {int limit() default 10;}

# 定义AOP

package tech.pdai.ratelimit.guava.config.ratelimit;import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;/*** @author pdai*/
@Slf4j
@Aspect
@Component
public class RateLimitAspect {private final ConcurrentHashMap<String, RateLimiter> EXISTED_RATE_LIMITERS = new ConcurrentHashMap<>();@Pointcut("@annotation(tech.pdai.ratelimit.guava.config.ratelimit.RateLimit)")public void rateLimit() {}@Around("rateLimit()")public Object around(ProceedingJoinPoint point) throws Throwable {MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();RateLimit annotation = AnnotationUtils.findAnnotation(method, RateLimit.class);// get rate limiterRateLimiter rateLimiter = EXISTED_RATE_LIMITERS.computeIfAbsent(method.getName(), k -> RateLimiter.create(annotation.limit()));// processif (rateLimiter!=null && rateLimiter.tryAcquire()) {return point.proceed();} else {throw new RuntimeException("too many requests, please try again later...");}}
}

# 自定义相关异常

package tech.pdai.ratelimit.guava.config.exception;import lombok.extern.slf4j.Slf4j;/*** business exception, besides normal exception.** @author pdai*/
@Slf4j
public class BusinessException extends RuntimeException {/*** Constructs a new exception with {@code null} as its detail message. The cause is not initialized, and may* subsequently be initialized by a call to {@link #initCause}.*/public BusinessException() {super();}/*** Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently* be initialized by a call to {@link #initCause}.** @param message the detail message. The detail message is saved for later retrieval by the {@link #getMessage()}*                method.*/public BusinessException(final String message) {super(message);}/*** Constructs a new exception with the specified detail message and cause.* <p>* Note that the detail message associated with {@code cause} is <i>not</i> automatically incorporated in this* exception's detail message.** @param message the detail message (which is saved for later retrieval by the {@link #getMessage()} method).* @param cause   the cause (which is saved for later retrieval by the {@link #getCause()} method). (A <tt>null</tt>*                value is permitted, and indicates that the cause is nonexistent or unknown.)* @since 1.4*/public BusinessException(final String message, final Throwable cause) {super(message, cause);}/*** Constructs a new exception with the specified cause and a detail message of* <tt>(cause==null ? null : cause.toString())</tt> (which typically contains the class and detail message of* <tt>cause</tt>). This constructor is useful for exceptions that are little more than wrappers for other* throwables (for example, {@link java.security.PrivilegedActionException}).** @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method). (A <tt>null</tt>*              value is permitted, and indicates that the cause is nonexistent or unknown.)* @since 1.4*/public BusinessException(final Throwable cause) {super(cause);}/*** Constructs a new exception with the specified detail message, cause, suppression enabled or disabled, and* writable stack trace enabled or disabled.** @param message            the detail message.* @param cause              the cause. (A {@code null} value is permitted, and indicates that the cause is nonexistent or*                           unknown.)* @param enableSuppression  whether or not suppression is enabled or disabled* @param writableStackTrace whether or not the stack trace should be writable* @since 1.7*/protected BusinessException(final String message, final Throwable cause, boolean enableSuppression,boolean writableStackTrace) {super(message, cause, enableSuppression, writableStackTrace);}}

异常的处理

package tech.pdai.ratelimit.guava.config.exception;import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import tech.pdai.ratelimit.guava.config.response.ResponseResult;
import tech.pdai.ratelimit.guava.config.response.ResponseStatus;/*** @author pdai*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {/*** handle business exception.** @param businessException business exception* @return ResponseResult*/@ResponseBody@ExceptionHandler(BusinessException.class)public ResponseResult<BusinessException> processBusinessException(BusinessException businessException) {log.error(businessException.getLocalizedMessage());return ResponseResult.fail(null, businessException.getLocalizedMessage()==null? ResponseStatus.HTTP_STATUS_500.getDescription():businessException.getLocalizedMessage());}/*** handle other exception.** @param exception exception* @return ResponseResult*/@ResponseBody@ExceptionHandler(Exception.class)public ResponseResult<Exception> processException(Exception exception) {log.error(exception.getLocalizedMessage(), exception);return ResponseResult.fail(null, ResponseStatus.HTTP_STATUS_500.getDescription());}
}

# 统一结果返回封装

package tech.pdai.ratelimit.guava.config.response;import java.io.Serializable;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class ResponseResult<T> {/*** response timestamp.*/private long timestamp;/*** response code, 200 -> OK.*/private String status;/*** response message.*/private String message;/*** response data.*/private T data;/*** response success result wrapper.** @param <T> type of data class* @return response result*/public static <T> ResponseResult<T> success() {return success(null);}/*** response success result wrapper.** @param data response data* @param <T>  type of data class* @return response result*/public static <T> ResponseResult<T> success(T data) {return ResponseResult.<T>builder().data(data).message(ResponseStatus.SUCCESS.getDescription()).status(ResponseStatus.SUCCESS.getResponseCode()).timestamp(System.currentTimeMillis()).build();}/*** response error result wrapper.** @param message error message* @param <T>     type of data class* @return response result*/public static <T extends Serializable> ResponseResult<T> fail(String message) {return fail(null, message);}/*** response error result wrapper.** @param data    response data* @param message error message* @param <T>     type of data class* @return response result*/public static <T> ResponseResult<T> fail(T data, String message) {return ResponseResult.<T>builder().data(data).message(message).status(ResponseStatus.FAIL.getResponseCode()).timestamp(System.currentTimeMillis()).build();}}

# controller接口

package tech.pdai.ratelimit.guava.controller;import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.pdai.ratelimit.guava.config.ratelimit.RateLimit;
import tech.pdai.ratelimit.guava.config.response.ResponseResult;/*** @author pdai*/
@Slf4j
@RestController
public class RateLimitTestController {@RateLimit@GetMapping("/limit")public ResponseResult<String> limit() {log.info("limit");return ResponseResult.success();}@RateLimit(limit = 5)@GetMapping("/limit1")public ResponseResult<String> limit1() {log.info("limit1");return ResponseResult.success();}@GetMapping("/nolimit")public ResponseResult<String> noRateLimiter() {log.info("no limit");return ResponseResult.success();}}

# 接口测试

@SneakyThrows
public static void test(int clientSize) {CountDownLatch downLatch = new CountDownLatch(clientSize);ExecutorService fixedThreadPool = Executors.newFixedThreadPool(clientSize);IntStream.range(0, clientSize).forEach(i ->fixedThreadPool.submit(() -> {RestTemplate restTemplate = new RestTemplate();restTemplate.getForObject("http://localhost:8080/limit1", ResponseResult.class);downLatch.countDown();}));downLatch.await();fixedThreadPool.shutdown();
}

测试结果

2021-10-01 15:22:47.171  INFO 30092 --- [nio-8080-exec-4] t.p.r.g.c.RateLimitTestController        : limit1
2021-10-01 15:22:47.171  INFO 30092 --- [nio-8080-exec-8] t.p.r.g.c.RateLimitTestController        : limit1
2021-10-01 15:22:47.171  INFO 30092 --- [nio-8080-exec-5] t.p.r.g.c.RateLimitTestController        : limit1
2021-10-01 15:22:47.187  INFO 30092 --- [nio-8080-exec-9] t.p.r.g.c.RateLimitTestController        : limit1
2021-10-01 15:22:47.187  INFO 30092 --- [nio-8080-exec-2] t.p.r.g.c.RateLimitTestController        : limit1
2021-10-01 15:22:47.187  INFO 30092 --- [io-8080-exec-10] t.p.r.g.c.RateLimitTestController        : limit1
2021-10-01 15:22:47.202 ERROR 30092 --- [nio-8080-exec-7] t.p.r.g.c.e.GlobalExceptionHandler       : too many requests, please try again later...
2021-10-01 15:22:47.202 ERROR 30092 --- [nio-8080-exec-6] t.p.r.g.c.e.GlobalExceptionHandler       : too many requests, please try again later...
2021-10-01 15:22:47.221 ERROR 30092 --- [nio-8080-exec-1] t.p.r.g.c.e.GlobalExceptionHandler       : too many requests, please try again later...
2021-10-01 15:22:47.222 ERROR 30092 --- [nio-8080-exec-5] t.p.r.g.c.e.GlobalExceptionHandler       : too many requests, please try again later...
2021-10-01 15:22:47.225 ERROR 30092 --- [nio-8080-exec-6] t.p.r.g.c.e.GlobalExceptionHandler       : too many requests, please try again later...
2021-10-01 15:22:47.225 ERROR 30092 --- [nio-8080-exec-8] t.p.r.g.c.e.GlobalExceptionHandler       : too many requests, please try again later...
2021-10-01 15:22:47.225 ERROR 30092 --- [nio-8080-exec-3] t.p.r.g.c.e.GlobalExceptionHandler       : too many requests, please try again later...
2021-10-01 15:22:47.225 ERROR 30092 --- [io-8080-exec-12] t.p.r.g.c.e.GlobalExceptionHandler       : too many requests, please try again later...
2021-10-01 15:22:47.225 ERROR 30092 --- [io-8080-exec-14] t.p.r.g.c.e.GlobalExceptionHandler       : too many requests, please try again later...
2021-10-01 15:22:47.225 ERROR 30092 --- [io-8080-exec-13] t.p.r.g.c.e.GlobalExceptionHandler       : too many requests, please try again later...
2021-10-01 15:22:47.225 ERROR 30092 --- [io-8080-exec-15] t.p.r.g.c.e.GlobalExceptionHandler       : too many requests, please try again later...
2021-10-01 15:22:47.240 ERROR 30092 --- [io-8080-exec-11] t.p.r.g.c.e.GlobalExceptionHandler       : too many requests, please try again later...
2021-10-01 15:22:47.240 ERROR 30092 --- [nio-8080-exec-4] t.p.r.g.c.e.GlobalExceptionHandler       : too many requests, please try again later...
2021-10-01 15:22:47.256 ERROR 30092 --- [nio-8080-exec-2] t.p.r.g.c.e.GlobalExceptionHandler       : too many requests, please try again later...

# 上述实现方案的槽点

注意

必须要说明一下,上述实现方式只是单实例下一种思路而已,如果细细的看,上面的代码存在一些槽点。

  1. 首先, EXISTED_RATE_LIMITERS.computeIfAbsent(method.getName(), k -> RateLimiter.create(annotation.limit())) 这行代码中 method.getName()表明是对方法名进行限流的,其实并不合适,应该需要至少加上类名;
  2. 其次, 如果首次运行时访问的请求是一次性涌入的,即EXISTED_RATE_LIMITERS还是空的时候并发请求@RateLimit接口,那么RateLimiter.create(annotation.limit())是会重复创建并加入到EXISTED_RATE_LIMITERS的,这是明显的bug;
  3. 再者, 上述实现方式按照方法名去限定请求量,对于很多情况下至少需要支持按照IP和方法名,或者其它自定义的方式进行限流。
  4. 其它一些场景支持的参数抽象和封装等

相关文章:

SpringBoot接口 - 如何实现接口限流之单实例

在以SpringBoot开发Restful接口时&#xff0c;当流量超过服务极限能力时&#xff0c;系统可能会出现卡死、崩溃的情况&#xff0c;所以就有了降级和限流。在接口层如何做限流呢&#xff1f; 本文主要回顾限流的知识点&#xff0c;并实践单实例限流的一种思路。 SpringBoot接口 …...

【花雕学AI】深度挖掘ChatGPT角色扮演的一个案例—CHARACTER play : 莎士比亚

CHARACTER play : 莎士比亚 : 52岁&#xff0c;男性&#xff0c;剧作家&#xff0c;诗人&#xff0c;喜欢文学&#xff0c;戏剧&#xff0c;爱情 : 1、问他为什么写《罗密欧与朱丽叶》 AI: 你好&#xff0c;我是莎士比亚&#xff0c;一位英国的剧作家和诗人。我很高兴你对我的…...

腾讯云物联网开发平台 LoRaWAN 透传接入 更新版

前言 之前有一篇文章介绍LoRaWAN透传数据&#xff0c;不过还是用物模型云端数据解析脚本&#xff0c;不是真正的透传。腾讯云物联网开发平台也支持对LoRaWAN原始数据的透传、转发。今天来介绍下。腾讯云 IoT Explorer 是腾讯云主推的一站式物联网开发平台&#xff0c;IoT 小能手…...

4.6--计算机网络之TCP篇之TCP的基本认识--(复习+深入)---好好沉淀,加油呀

1.TCP 头格式有哪些&#xff1f; 序列号&#xff1a; 在建立连接时由计算机生成的随机数作为其初始值&#xff0c;通过 SYN 包传给接收端主机&#xff0c;每发送一次数据&#xff0c;就「累加」一次该「数据字节数」的大小。 用来解决网络包乱序问题。 确认应答号&#xff1a; …...

一文吃透Elasticsearch

本文已经收录到Github仓库&#xff0c;该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点&#xff0c;欢迎star~ Github地址 如果访问不了Github&#xff0c…...

CPU占用率高怎么办?正确解决方法在这里!

案例&#xff1a;CPU占用率高怎么解决 【各位朋友&#xff0c;我的电脑现在运行太慢了&#xff0c;同事说可能是CPU占用率太高了&#xff0c;但对本电脑小白来说&#xff0c;完全不知道怎么处理&#xff0c;大家有什么好的方法可以解决这个问题吗&#xff1f;】 在计算机中&a…...

ChatGPT实现用C语言写一个学生成绩管理系统

随着ChatGPT爆火&#xff0c;大家都在使用ChatGPT来帮助自己提高效率&#xff0c;对于程序员来说使用它来写代码怎么样呢&#xff1f;今天尝试让ChatGPT&#xff0c;写了一个学生成绩管理系统。 问题是&#xff1a;使用C语言写一个学生成绩管理系统&#xff0c;要求使用链表&a…...

Swagger文档注释

本文以DRF框架为例使用 为什么要接口文档注释 一. 方便后端调试与后续接口更新&#xff1b; 二. 对于大型前后端分离项目&#xff0c;前后端人员是分开开发的&#xff0c;甚至前端的人你都不知道远在何处&#xff0c;这时候接口文档的重要性就太重要了。 三. 接口注释文档常用…...

pdf怎么转换ppt格式,两个方法转换

PDF作为一种常用的文件格式&#xff0c;被大众所熟悉。虽然PDF具备的稳定性&#xff0c;安全性&#xff0c;以及很强的兼容性可以让我们更方便顺畅的阅读PDF文件&#xff0c;但若是有需要展示PDF文件内容的时候&#xff0c;其优点就没有那么凸显了&#xff0c;这时还是将pdf转换…...

深度学习编译器相关的优秀论文合集-附下载地址

公司排名不分先后 目前在AI芯片编译器领域&#xff0c;有很多大公司在进行研究和开发。以下是一些主要的公司和它们在该领域的研究时间&#xff1a; 英伟达&#xff08;NVIDIA&#xff09;&#xff1a;英伟达是一家全球知名的图形处理器制造商&#xff0c;其在AI芯片编译器领域…...

vue全局使用svg

1、安装依赖 npm install svg-sprite-loader2、配置选项 在vue.config.js的chainWebpack里配置下面代码 解释&#xff1a;config.module.rule是一个方法&#xff0c;用来获取某个对象的规则。.exclude.add&#xff08;文件a&#xff09;是往禁用组添加文件a&#xff0c;就是对文…...

每天一点C++——杂记

结构体的深拷贝和浅拷贝 浅拷贝就是只拷贝指针&#xff0c;并不拷贝指针所指向的内容&#xff0c;深拷贝则会对指针的内容进行拷贝。浅拷贝会在一些场景下出现问题&#xff0c;看下面的例子&#xff1a; struct s {char * name;int age; };如果我定义 一个对象s1&#xff0c;…...

Document Imaging SDK 11.6 for .NET Crack

Document Imaging SDK for .NET View, Convert, Annotate, Process,Edit, Scan, OCR, Print 基本上被认为是一种导出 PDF 解决方案&#xff0c;能够为用户和开发人员提供完整且创新的 PDF 文档处理属性。它具有提供简单集成的能力&#xff0c;可用于增强用户 .NET 的文档成像程…...

数据挖掘(3.1)--频繁项集挖掘方法

目录 1.Apriori算法 Apriori性质 伪代码 apriori算法 apriori-gen(Lk-1)【候选集产生】 has_infrequent_subset(c,Lx-1)【判断候选集元素】 例题 求频繁项集&#xff1a; 对于频繁项集L{B,C,E}&#xff0c;可以得到哪些关联规则&#xff1a; 2.FP-growth算法 FP-tre…...

2023年信息安全推荐证书

随着网络安全行业的不断升温&#xff0c;相关的认证数量也不断增加&#xff0c;对于在网络安全行业发展的人才来说&#xff0c;提升职业竞争力最有效的办法之一&#xff0c;就是取得权威认证。 那么如何从繁多的适合网络安全从业者的证书中选择含金量高、发展潜力大的证书&…...

基于ArcGIS、ENVI、InVEST、FRAGSTATS等多技术融合提升环境、生态、水文、土地、土壤、农业、大气等领域应用

【自选】 时间地点&#xff1a;2023年7月22日-28日【乌鲁木齐】时间地点&#xff1a;2023年8月12日-18日【福建泉州】 【六天实践教学、提供全部资料】 专题一、空间数据获取与制图 1.1 软件安装与应用讲解 1.2 空间数据介绍 1.3海量空间数据下载 1.4 ArcGIS软件快速入门…...

基于ZC序列的帧同步

Zadoff-Chu序列是一种特殊的序列&#xff0c;具有良好的自相关性和很低的互相关性&#xff0c;这种性能可以被用来产生同步信号&#xff0c;作为对时间和频率的相关运算在TD-LTE系统中&#xff0c;Zadoff-Chu(ZC)序列主要应用于上行RS序列生成、PRACH前导序列生成以及主同步信号…...

配置NFS服务器-debian

NFS(Network Files System)是网络文件系统的英文缩写&#xff0c;由Sun公司于1980年开发&#xff0c;用于在UNIX操作系统间实现磁盘文件共享。在Linux操作系统出现后&#xff0c;NFS被Linux继承&#xff0c;并成为文件服务的一种标准。 通过网络&#xff0c;NFS可以在不同文件…...

正点原子STEMWIN死机

在用正点原子STM32F4开发板&#xff0c;搭配对应的button历程时&#xff0c;发现运行一会&#xff0c;button都无法使用了&#xff0c;以为是emwin死机了&#xff0c;但是看到Led还在闪烁&#xff0c;排除系统死机问题。那就是emwin的任务没有运行起来&#xff0c;但是打断点后…...

PMP考试中的固定答题套路

1、掌握PMBOK 编制的逻辑&#xff08;整范进&#xff0c;成质资&#xff0c;沟风采&#xff0c;相&#xff09; 2、事实求是&#xff0c;项目经理该怎么做就怎么做&#xff0c;不能违背职业道德。 3、PM 做事流程&#xff08;5 步法&#xff09;&#xff1a; ①收集信息&…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

HDFS分布式存储 zookeeper

hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架&#xff0c;允许使用简单的变成模型跨计算机对大型集群进行分布式处理&#xff08;1.海量的数据存储 2.海量数据的计算&#xff09;Hadoop核心组件 hdfs&#xff08;分布式文件存储系统&#xff09;&a…...

《C++ 模板》

目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板&#xff0c;就像一个模具&#xff0c;里面可以将不同类型的材料做成一个形状&#xff0c;其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式&#xff1a;templa…...

并发编程 - go版

1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...

Python 实现 Web 静态服务器(HTTP 协议)

目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1&#xff09;下载安装包2&#xff09;配置环境变量3&#xff09;安装镜像4&#xff09;node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1&#xff09;使用 http-server2&#xff09;详解 …...