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

springboot系列--web相关知识探索二

一、映射

指的是与请求处理方法关联的URL路径,通过在Spring MVC的控制器类(使用@RestController注解修饰的类)上使用注解(如 @RequestMapping、@GetMapping)来指定请求映射路径,可以将不同的HTTP请求映射到相应的处理方法上。说白了就是将具体的请求映射到具体的接口当中。

二、springboot对Rest风格的支持 

springboot默认情况下是支持Rest风格,但是唯独对表单类型的请求例外。表单情况下提交请求,无论你的method是put还是delete都会变成get请求。如果想要在表单类型下使用rest风格请求方式,需要把表单method属性设置为post,隐藏域设置为_method=put,这个时候请求才会进入到put类型的接口当中。

隐藏域:<input type="hidden" name="_method" value="delete">

 原理:

表单提交以后,会被springboot中的OrderedHiddenHttpMethodFilter组件拦截,当然前提是开启了这个组件,不然表单也无法使用rest风格请求。OrderedHiddenHttpMethodFilter是继承HiddenHttpMethodFilter,请求会打到这个类的doFilterInternal方法上。该方法原理如下:

spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true   #开启页面表单的Rest功能

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {HttpServletRequest requestToUse = request;// 只有post请求才会打到这里,所以表单请求必须是postif ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {// 请求需要带this.methodParam对应的是_method,paramValue 对应的是具体的请求类型String paramValue = request.getParameter(this.methodParam);// 不为空的时候才进去处理if (StringUtils.hasLength(paramValue)) {String method = paramValue.toUpperCase(Locale.ENGLISH);// ALLOWED_METHODS 包含delete、put、patch等类型,也就是说隐藏域所带的值必须是这几个if (ALLOWED_METHODS.contains(method)) {// 这里就是对原始请求进行包装,将隐藏域的值作为新的请求类型。包装了原生请求的HttpMethodRequestWrapper类重写了getMethod方法,返回的是传入的methodrequestToUse = new HttpMethodRequestWrapper(request, method);}}}filterChain.doFilter((ServletRequest)requestToUse, response);}static {ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));}
// 包装了原生请求的类    
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {private final String method;// 将外部的method重新赋值进来public HttpMethodRequestWrapper(HttpServletRequest request, String method) {super(request);this.method = method;}// 重写了getMethod,返回的是外部赋予的值public String getMethod() {return this.method;}}

表单提交指定了隐藏域需要带_method为key,值为put、patch、delete等参数,才能修改成符合rest风格请求。这是由于key="_method",是在底层源码写死了的。

public class HiddenHttpMethodFilter extends OncePerRequestFilter {private static final List<String> ALLOWED_METHODS;public static final String DEFAULT_METHOD_PARAM = "_method";// 此处写死了隐藏域key必须为_methodprivate String methodParam = "_method";public HiddenHttpMethodFilter() {}public void setMethodParam(String methodParam) {Assert.hasText(methodParam, "'methodParam' must not be empty");this.methodParam = methodParam;}protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {HttpServletRequest requestToUse = request;if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {String paramValue = request.getParameter(this.methodParam);if (StringUtils.hasLength(paramValue)) {String method = paramValue.toUpperCase(Locale.ENGLISH);if (ALLOWED_METHODS.contains(method)) {requestToUse = new HttpMethodRequestWrapper(request, method);}}}filterChain.doFilter((ServletRequest)requestToUse, response);}static {ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));}private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {private final String method;public HttpMethodRequestWrapper(HttpServletRequest request, String method) {super(request);this.method = method;}public String getMethod() {return this.method;}}
}

 如果想要修改,可以重写这个组件。

//自定义filter@Beanpublic HiddenHttpMethodFilter hiddenHttpMethodFilter(){HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();// 这个时候,key就必须等于_test了methodFilter.setMethodParam("_test");return methodFilter;}

 三、请求映射原理

springmvc中所有的请求都是会先经过DispatcherServlet(前端控制器)这个类,这个类的其中一个父类HttpServlet,这个类有几个方法,doGet、doPost、doPut、doDelete,就是最原始处理get、put等请求的方法,HttpServlet是一个抽象类,这些方法是在FrameworkServlet中实现的。

里面每一个doGet、doPost等方法都调用了processRequest这个方法。

FrameworkServlet类中的这个方法是一个抽象方法,

protected abstract void doService(HttpServletRequest var1, HttpServletResponse var2) throws Exception;

所以需要看它的子类DispatcherServlet里面的doService方法。这个方法调用了doDispatch这个方法,每个请求进来都是调用到它。

    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {this.logRequest(request);Map<String, Object> attributesSnapshot = null;if (WebUtils.isIncludeRequest(request)) {attributesSnapshot = new HashMap();Enumeration<?> attrNames = request.getAttributeNames();label104:while(true) {String attrName;do {if (!attrNames.hasMoreElements()) {break label104;}attrName = (String)attrNames.nextElement();} while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));attributesSnapshot.put(attrName, request.getAttribute(attrName));}}request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());if (this.flashMapManager != null) {FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);if (inputFlashMap != null) {request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));}request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);}RequestPath previousRequestPath = null;if (this.parseRequestPath) {previousRequestPath = (RequestPath)request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);ServletRequestPathUtils.parseAndCache(request);}try {// 这行代码之前,其实大部分逻辑都是初始化设置各种值,doDispatch就是做各种转发this.doDispatch(request, response);} finally {if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {this.restoreAttributesAfterInclude(request, attributesSnapshot);}ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);}}

下面是doDispatch源码以及一些与原理

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// 找到当前请求使用哪个Handler(Controller的方法)处理mappedHandler = getHandler(processedRequest);//HandlerMapping:处理器映射。
 @Nullableprotected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {// 循环遍历每个处理器映射器if (this.handlerMappings != null) {Iterator var2 = this.handlerMappings.iterator();while(var2.hasNext()) {// 处理请求的处理器映射器RequestMappingHandlerMappingHandlerMapping mapping = (HandlerMapping)var2.next();// 通过url获取到具体的处理器映射器HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;}


1、this.handlerMappings != null中handlerMappings就是springboot所有的处理器映射器。

2、其中RequestMappingHandlerMapping:保存了所有@RequestMapping 和handler的映射规则。其实就是保存了@RequestMapping中配置的各种url和对应的controller

3、 所有的请求映射都在HandlerMapping中。然后通过HandlerMapping找到请求url对应的具体的controller。

4、SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;

springboot底层通过url获取到具体的controller原理,需要从

HandlerExecutionChain handler = mapping.getHandler(request);

这个方法进入

 

    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {// 获取@GetMapping("/test1")上的请求路径/test1String lookupPath = this.initLookupPath(request);//mappingRegistry保存了所有的请求路径对应的controller信息,加锁是防止并发获取this.mappingRegistry.acquireReadLock();HandlerMethod var4;try {// 根据url和请求信息,找到controller中对应能够处理的方法,其实就是具体的那个接口的信息HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);var4 = handlerMethod != null ? handlerMethod.createWithResolvedBean() : null;} finally {this.mappingRegistry.releaseReadLock();}return var4;}// 这个方法主要是获取url请求路径protected String initLookupPath(HttpServletRequest request) {if (this.usesPathPatterns()) {request.removeAttribute(UrlPathHelper.PATH_ATTRIBUTE);RequestPath requestPath = ServletRequestPathUtils.getParsedRequestPath(request);String lookupPath = requestPath.pathWithinApplication().value();return UrlPathHelper.defaultInstance.removeSemicolonContent(lookupPath);} else {// 请求进来是在这里处理的return this.getUrlPathHelper().resolveAndCacheLookupPath(request);}}/**String lookupPath:注解上的请求路径HttpServletRequest request:具体请求**/@Nullableprotected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {List<AbstractHandlerMethodMapping<T>.Match> matches = new ArrayList();// 通过url找到多个个能够处理的接口或者说方法。比如说/test1可能会有put请求,也有get请求。post请求等等List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);if (directPathMatches != null) {// 找到了多个接口,就是添加到这个方法里,这个方法会根据你请求进来的方式(post、get或put等等)匹配到最优的处理方法,也就是精确找到你这个进来的请求方式所对应的处理接口。并且把匹配到的放到第一位且一般只有一个,具体不展开叙述this.addMatchingMappings(directPathMatches, matches, request);}if (matches.isEmpty()) {this.addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);}if (matches.isEmpty()) {return this.handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);} else {// 这里就是从上面保存符合条件的集合中获取第一个,一般只有一个,如果有两个,例如/test1 get请求,写了两个方法且请求方式,路径都一样,就matches.size() > 1就为true,然后会进入里面的处理逻辑并报错AbstractHandlerMethodMapping<T>.Match bestMatch = (Match)matches.get(0);// 就是这里if (matches.size() > 1) {Comparator<AbstractHandlerMethodMapping<T>.Match> comparator = new MatchComparator(this.getMappingComparator(request));matches.sort(comparator);bestMatch = (Match)matches.get(0);if (this.logger.isTraceEnabled()) {this.logger.trace(matches.size() + " matching mappings: " + matches);}if (CorsUtils.isPreFlightRequest(request)) {Iterator var7 = matches.iterator();while(var7.hasNext()) {AbstractHandlerMethodMapping<T>.Match match = (Match)var7.next();if (match.hasCorsConfig()) {return PREFLIGHT_AMBIGUOUS_MATCH;}}} else {AbstractHandlerMethodMapping<T>.Match secondBestMatch = (Match)matches.get(1);if (comparator.compare(bestMatch, secondBestMatch) == 0) {Method m1 = bestMatch.getHandlerMethod().getMethod();Method m2 = secondBestMatch.getHandlerMethod().getMethod();String uri = request.getRequestURI();// 有两个相同的url且请求方式一致的处理方法throw new IllegalStateException("Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");}}}request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());this.handleMatch(bestMatch.mapping, lookupPath, request);// 这里就是获取到具体的处理方法,也就是接口对应的方法,例如/test1 对应的是com.example.estest.controller.MvcTestController#test()这个接口,会返回com.example.estest.controller.MvcTestController#test()这个东西的具体信息return bestMatch.getHandlerMethod();}}

 

 

1、另外SpringBoot自动配置了默认配置了一个处理器映射器就是 RequestMappingHandlerMapping用于处理@GetMapping、@PusMapping、@RequestMapping等注解的映射

2、如果需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping。可以参考WebMvcAutoConfiguration类下的requestMappingHandlerMapping组件定义。

        @Bean@Primarypublic RequestMappingHandlerMapping requestMappingHandlerMapping(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, @Qualifier("mvcConversionService") FormattingConversionService conversionService, @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService, resourceUrlProvider);}

相关文章:

springboot系列--web相关知识探索二

一、映射 指的是与请求处理方法关联的URL路径&#xff0c;通过在Spring MVC的控制器类&#xff08;使用RestController注解修饰的类&#xff09;上使用注解&#xff08;如 RequestMapping、GetMapping&#xff09;来指定请求映射路径&#xff0c;可以将不同的HTTP请求映射到相应…...

Oracle 12c在Windows环境下安装

适合初学者使用的Oracle 12c在Windows环境下安装步骤、参数配置、常见问题及参数调优的详细补充说明。 一、Oracle 12c安装步骤 1. 准备工作 在安装Oracle 12c之前&#xff0c;确保你的系统满足以下要求&#xff1a; 操作系统&#xff1a;Oracle 12c支持的Windows版本包括Wi…...

Stable Diffusion绘画 | 来训练属于自己的模型:打标处理与优化

上一篇完成的打标工作&#xff0c;是为了获取提示词&#xff0c;让AI认识和学习图片的特征。 因此&#xff0c;合适、恰当、无误的提示词&#xff0c;对最终模型效果是相当重要的。 Tag 如何优化 通过软件自动生成的 Tag 只是起到快速建立大体架构的作用&#xff0c;里面会涉…...

【论文笔记】Visual Instruction Tuning

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: Visual Instruction Tunin…...

ubuntu 设置静态IP

一、 ip addresssudo nano /etc/netplan/50-cloud-init.yaml 修改前&#xff1a; 修改后&#xff1a; # This file is generated from information provided by the datasource. Changes # to it will not persist across an instance reboot. To disable cloud-inits # ne…...

Java 每日一刊(第19期):泛型

文章目录 前言1. 泛型概述1.1 不使用泛型 vs 使用泛型1.2 泛型的作用 2. 泛型的基本语法2.1 定义带类型参数的泛型类2.2 使用泛型类2.3 泛型方法 3. 泛型类型推断与钻石操作符3.1 类型推断3.2 钻石操作符 4. 通配符的使用4.1 无界通配符 <?>4.2 上界通配符 <? exten…...

windows下安装rabbitMQ并开通管理界面和允许远程访问

如题&#xff0c;在windows下安装一个rabbitMQ server&#xff1b;然后用浏览器访问其管理界面&#xff1b;由于rabbitMQ的默认账号guest默认只能本机访问&#xff0c;因此需要设置允许其他机器远程访问。这跟mysql的思路很像&#xff0c;默认只能本地访问&#xff0c;要远程访…...

深度剖析音频剪辑免费工具的特色与优势

是热爱生活的伙伴或者想要记录美好声音的普通用户&#xff0c;都可能会需要对音频进行剪辑处理。而幸运的是&#xff0c;现在有许多优秀的音频剪辑软件提供了免费版本&#xff0c;让我们能够轻松地施展音频剪辑的魔法。接下来&#xff0c;就让我们一同深入了解这些音频剪辑免费…...

Oracle中TRUNC()函数详解

文章目录 前言一、TRUNC函数的语法二、主要用途三、测试用例总结 前言 在Oracle中&#xff0c;TRUNC函数用于截取或截断日期、时间或数值表达式的部分。它返回一个日期、时间或数值的截断版本&#xff0c;根据提供的格式进行截取。 一、TRUNC函数的语法 TRUNC(date) TRUNC(d…...

【Spring Boot 入门一】构建你的第一个Spring Boot应用

一、引言 在当今的软件开发领域&#xff0c;Java一直占据着重要的地位。而Spring Boot作为Spring框架的延伸&#xff0c;为Java开发者提供了一种更加便捷、高效的开发方式。它简化了Spring应用的搭建和配置过程&#xff0c;让开发者能够专注于业务逻辑的实现。无论是构建小型的…...

PPT 快捷键使用、技巧

前言&#xff1a; 本文操作是以office 2021为基础的&#xff0c;仅供参考&#xff1b;不同版本office 的 ppt 快捷键 以及对应功能会有差异&#xff0c;需要实践出真知。 shift 移动 水平/垂直 移动 &#xff1b; shift 放大/缩小 等比例放大 缩小 &#xff1b; 正圆 正…...

Web安全 - 文件上传漏洞(File Upload Vulnerability)

文章目录 OWASP 2023 TOP 10导图定义攻击场景1. 上传恶意脚本2. 目录遍历3. 覆盖现有文件4. 文件上传结合社会工程攻击 防御措施1. 文件类型验证2. 文件名限制3. 文件存储位置4. 文件权限设置5. 文件内容检测6. 访问控制7. 服务器配置 文件类型验证实现Hutool的FileTypeUtil使用…...

vue3中el-input在form表单按下回车刷新页面

摘要&#xff1a; 在input框中点击回车之后不是调用我写的回车事件&#xff0c;而是刷新页面&#xff01; 如果表单中只有一个input 框则按下回车会直接关闭表单 所以导致刷新页面 再写一个input 表单 &#xff0c;并设置style“display:none” <ElInput style"display…...

SQL Server中关于个性化需求批量删除表的做法

在实际开发中&#xff0c;我们常常会遇到需要批量删除表&#xff0c;且具有共同特征的情况&#xff0c;例如&#xff1a;找出表名中数字结尾的表之类的&#xff0c;本文我将以3中类似情况为例&#xff0c;来示范并解说此类需求如何完成&#xff1a; 第一种&#xff0c;批量删除…...

关于按键状态机解决Delay给程序带来的问题

问题产生 我在学习中断的过程中&#xff0c;使用EXTI15外部中断&#xff0c;在其中加入HAL_Delay();就会发生报错 错误地方 其它地方配置 问题原因 在中断服务例程&#xff08;ISR&#xff09;中使用 HAL_Delay() 会导致问题的原因是&#xff1a; 阻塞性&#xff1a; HAL_D…...

62.【C语言】浮点数的存储

目录 1.浮点数的类型 2.浮点数表示的范围 3.浮点数的特性 《计算机科学导论》的叙述 4.浮点数在内存中的存储 答案速查 分析 前置知识:浮点数的存储规则 推导单精度浮点数5.5在内存中的存储 验证 浮点数取出的分析 1.一般情况:E不全为0或不全为1 2.特殊情况:E全为0…...

GO网络编程(一):基础知识

1. 网络编程的基础概念 TCP/IP 协议栈 TCP/IP 是互联网通信的核心协议栈&#xff0c;分为以下四个层次&#xff1a; 应用层&#xff08;Application Layer&#xff09;&#xff1a;为应用程序提供网络服务的协议&#xff0c;比如 HTTP、FTP、SMTP 等。传输层&#xff08;Tra…...

【Linux】用虚拟机配置Ubuntu环境

目录 1.虚拟机安装Ubuntu系统 2.Ubuntu系统的网络配置 3.特别声明 首先我们先要下载VMware软件&#xff0c;大家自己去下啊&#xff01; 1.虚拟机安装Ubuntu系统 我们进去之后点击创建新的虚拟机&#xff0c;然后选择自定义 接着点下一步 再点下一步 进入这个界面之后&…...

酒店智能门锁SDK接口pro[V10] 门锁校验C#-SAAS本地化-未来之窗行业应用跨平台架构

一、代码 int 酒店标识_int Convert.ToInt32(酒店标识);StringBuilder 锁号2024 new StringBuilder(8);//信息 "未知返回值&#xff1a;" bufCard_原始;GetGuestLockNoByCardDataStr_原始(酒店标识_int, bufCard_原始.ToString(), 锁号2024);StringBuilder 退…...

Gitのrebase用法

在 Git 中&#xff0c;rebase 是一种用于整合多个提交历史的操作&#xff0c;它可以将一个分支的变更“重放”到另一个分支上。与 merge 不同&#xff0c;rebase 会产生一个线性的提交历史&#xff0c;使得项目的历史记录更加整洁和易于理解。 1. 什么是 Rebase&#xff1f; …...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!

5月28日&#xff0c;中天合创屋面分布式光伏发电项目顺利并网发电&#xff0c;该项目位于内蒙古自治区鄂尔多斯市乌审旗&#xff0c;项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站&#xff0c;总装机容量为9.96MWp。 项目投运后&#xff0c;每年可节约标煤3670…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

大模型多显卡多服务器并行计算方法与实践指南

一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要&#xff1a; 近期&#xff0c;在使用较新版本的OpenSSH客户端连接老旧SSH服务器时&#xff0c;会遇到 "no matching key exchange method found"​, "n…...