admin管理员组

文章数量:1531222

应用运行主目录 之前的文章集合:

一些可以参考文章集合1_xuejianxinokok的博客-CSDN博客

一些可以参考文章集合2_xuejianxinokok的博客-CSDN博客

一些可以参考的文档集合3_xuejianxinokok的博客-CSDN博客

一些可以参考的文档集合4_xuejianxinokok的博客-CSDN博客

一些可以参考的文档集合5_xuejianxinokok的博客-CSDN博客

一些可以参考的文档集合6_xuejianxinokok的博客-CSDN博客


一些可以参考的文档集合7_xuejianxinokok的博客-CSDN博客
 

一些可以参考的文档集合8_xuejianxinokok的博客-CSDN博客

 一些可以参考的文档集合9_xuejianxinokok的博客-CSDN博客

一些可以参考的文档集合10_xuejianxinokok的博客-CSDN博客

一些可以参考的文档集合11_xuejianxinokok的博客-CSDN博客

一些可以参考的文档集合12_xuejianxinokok的博客-CSDN博客

一些可以参考的文档集合13_xuejianxinokok的博客-CSDN博客

一些可以参考的文档集合14_xuejianxinokok的博客-CSDN博客

一些可以参考的文档集合15-CSDN博客


20240821

https://www.51cto/article/795195.htmlhttps://www.51cto/article/795195.html

20240815

Web Speech API 包括两个核心部分:

  1. SpeechRecognition(语音识别) :

    • 允许用户通过麦克风输入语音,然后将其转换为文本。

    • 可以检测语音的开始和结束,以便进行适当的处理和响应。

    • 提供了各种配置选项,如语言识别设置、连续识别等,以满足不同应用场景的需求。

  2. SpeechSynthesis(语音合成) :

    • 允许开发者将文本转换为语音输出。

    • 支持多种语音合成引擎和语音效果,可以根据需求选择合适的语音风格和语言。

    • 提供了控制音调、语速等参数的接口,以实现个性化的语音输出效果。

纯前端实现语音文字互转

掌握现代 Web API:2024 年强大浏览器功能指南

https://segmentfault/a/1190000040809349?utm_source=sf-similar-articlehttps://segmentfault/a/1190000040809349?utm_source=sf-similar-article

https://www.51cto/article/794822.htmlhttps://www.51cto/article/794822.html

20240806

SpringBoot请求参数还可以这样玩?很少有人知道?

https://www.51cto/article/794175.htmlhttps://www.51cto/article/794175.html

20240805

如何在Ollama和Spring AI上使用本地AI/LLM查询图像数据库

https://www.51cto/article/793952.htmlhttps://www.51cto/article/793952.html

20240802

Promise

https://www.51cto/article/793948.htmlhttps://www.51cto/article/793948.html

前端实现文本对比,并高亮显示差异!

 jsdiff + diff2html 实现文本对比,并高亮显示差异

https://www.51cto/article/793875.htmlhttps://www.51cto/article/793875.html

20240729

每种方法都有其优点,选择取决于您的应用程序的具体需求:

  • Neon:最适合与Node.js直接集成,提供了一种简单有效的方式来编写本机模块
  • NAPI-RS:适用于使用Node-API创建稳定的、与版本无关的本地模块。
  • WebAssembly:非常适合在Node.js和浏览器中运行Rust代码,提供可移植性和性能。
  • FFI:对于需要直接从Node.js调用Rust函数而不需要额外绑定的场景非常有用。

https://www.51cto/article/793462.htmlhttps://www.51cto/article/793462.html

20240722

因为有函数重载

1. C语言中的函数名称解析规则

1.1 无重载C语言中,函数名是唯一的标识符,不能有同名的不同函数签名。因此我们不能在同一作用域下定义多个具有相同名称但参数类型或数量不同的函数。

1.2 名称解析:当调用一个函数时,C编译器仅根据函数名来查找对应的函数实现。如果函数名匹配,那么将调用成功,而不考虑参数的类型或数量是否正确。

1.3 名称修饰不存在:C语言没有名称修饰的概念。链接器和编译器使用相同的函数名称进行符号解析,无论是在编译阶段还是链接阶段。

2. C++中的函数名称解析规则

2.1 支持函数重载:在C++中可以有多个同名函数,只要它们的参数列表(参数的类型、顺序或数量)不同即可。编译器会根据调用语句的上下文自动选择正确的函数版本。

2.2 名称解析:在C++中,函数调用不仅仅依赖于函数名,还依赖于参数的类型和数量。这种基于参数类型的函数选择机制称为“过载解析”。

2.3 存在名称修饰(Name Mangling):为了支持函数重载,C++编译器会对函数名称进行修饰,即将函数的参数信息编码到函数名称中。这样编译后的二进制代码中,每个函数都有一个唯一的名称,即使它们在源代码中具有相同的标识符。

一句话总结就是:C语言的函数名称解析仅仅基于函数名称;而C++的函数名称解析基于函数名称和参数列表。

比如,函数void func(double a) 在CC++中的编译阶段函数名称会被解析成什么呢?

C语言中,由于没有名称修饰,所以在编译时函数名称仍然是func,不会因为参数类型或数量而改变。

C++中,由于名称修饰的存在,函数名称在编译阶段会被编译器转换成一个包含函数原型信息的唯一标识符。通常会涉及函数返回类型、参数类型以及参数数量。以GCC(GNU Compiler Collection)为例,func(double a)会被转换成_Z4funcd ,这里:

  • _Z:是GCC用来表示修饰名称的前缀
  • 4:表示函数名称func的的字符数
  • d:是double类型的编码

【C++】extern "C" 如何使用?

https://zhuanlan.zhihu/p/709910309https://zhuanlan.zhihu/p/709910309

20240719

在复制过程中,主库的 binlog 会不断地同步到从库,而从库则通过一个 SQL 线程不断地拉取并重放这些 SQL 语句。然而,当日志内容过多时,单个线程的执行会产生延迟,导致主从延迟。

为了解决这一问题,MySQL 提供了并行复制的方案。在多个版本中,MySQL 相继推出了多种并行复制的方案:

  • MySQL 5.6 引入了基于库级别的并行复制。
  • MySQL 5.7 推出了基于组提交的并行复制。
  • MySQL 8.0 推出了基于 WRITESET 的并行复制。

https://www.51cto/article/793038.htmlhttps://www.51cto/article/793038.html

20240718

在计算机操作系统中,用户空间(user space)下的缓冲区数据,一般是无法直接写入磁盘的,必须经过操作系统内核空间缓冲区(即OS Buffer)。

  • 日志最开始会写入位于存储引擎Innodb的redo log buffer,这个是在用户空间完成的。

  • 然后再将日志保存到操作系统内核空间的缓冲区(OS buffer)中。

  • 最后,通过系统调用fsync(),从OS buffer写入到磁盘上的redo log file中,完成写入操作。这个写入磁盘的操作,就叫做刷盘

我们可以发现,redo log buffer写入到redo log file,是经过OS buffer中转的。其实可以通过参数innodb_flush_log_at_trx_commit进行配置,参数值含义如下:

  • 0:称为延迟写,事务提交时不会将redo log buffer中日志写入到OS buffer,而是每秒写入OS buffer并调用写入到redo log file中。

  • 1:称为实时写,实时刷”,事务每次提交都会将redo log buffer中的日志写入OS buffer并保存到redo log file中。

  • 2:称为实时写,延迟刷。每次事务提交写入到OS buffer,然后是每秒将日志写入到redo log file。

https://blog.itpub/70027826/viewspace-3021920/https://blog.itpub/70027826/viewspace-3021920/

https://www.51cto/article/792905.htmlhttps://www.51cto/article/792905.html

20240717

C#与C/C++的交互

C#与C++交互,总体来说可以有两种方法:

  • 利用C++/CLI作为代理中间层

 “C++/CLI”是静态C++对象模型到CLI的动态组件对象编程模型的捆绑。简而言之就是如何用C++在·NET中编程,而不是C#或Visual Basic。

C++/CLI(C++ Common Language Infrastructure)是一种允许在 .NET 平台上创建托管代码(managed code)和非托管代码(unmanaged code)之间互操作的语言。其原理可以概括如下:

  • 编译器支持: C++/CLI 编译器能够编译同时包含托管和非托管代码的源文件。这使得在同一个项目中可以混合使用 C++ 和 C# 或其他 .NET 语言。

  • 托管和非托管代码的桥梁: C++/CLI 提供了语法和关键字,允许你在同一个文件中编写托管代码和非托管代码。这些代码可以相互调用,使得 C# 和 C++ 之间的交互变得简单。

  • CLR(Common Language Runtime)集成: C++/CLI 代码在编译时会生成对 CLR 的调用,因此它可以利用 CLR 提供的各种功能,例如垃圾回收、类型安全性和异常处理等。

  • 托管代码的封装: 在 C++/CLI 中,你可以将非托管的 C++ 代码封装在托管的类中,通过公共接口暴露给其他 .NET 语言。这样,C++ 的功能可以被其他 .NET 语言轻松调用和使用。

  • 数据类型转换: C++/CLI 提供了一组转换操作符和工具,用于在托管代码和非托管代码之间进行数据类型的转换。这样,你可以在 C++/CLI 中轻松处理 C# 中的数据类型,反之亦然。

  • 资源管理: 在 C++/CLI 中,你可以使用托管的资源管理功能(如 gcnew 创建托管对象和 delete 销毁对象),同时也可以手动管理非托管资源(如使用析构函数释放内存)。

Windows 操作 (C++/CLI) | Microsoft Learn详细了解:Windows 操作 (C++/CLI)https://learn.microsoft/zh-cn/cpp/dotnet/windows-operations-cpp-cli?view=msvc-170

C#调用C++使用C++/CLI的实现_C#教程_脚本之家在C#开发过程中,我们可能会遇到需要调用Windows API 或是第三方库的场景,本文主要介绍了C#调用C++使用C++/CLI的实现,具有一定的参考价值,感兴趣的可以了解一下https://www.jb51/program/318712brt.htm

  • 利用PInvoke实现直接调用

PInvoke从功能上来说,只支持函数调用,在被导出的函数前面一定要添加 extern "C"来指明导出函数的时候使用C语言方式编译和连接,这样保证函数定义的名字和导出的名字相同,否则如果默认按C++方式导出,那个函数的名字就会变得乱七八糟,我们的程序就无法找到入口点了。在函数前面添加"EXTERN_C"等同于在函数前面添加extern "C",意思是该函数在编译和连接时使用C语言的方式,以保证函数名字不变

https://wwwblogs/warensoft/archive/2011/12/09/warenosoft3d.htmlhttps://wwwblogs/warensoft/archive/2011/12/09/warenosoft3d.html

20240716

WebSocket in .NET .NET 中的 WebSocket

https://www.c-sharpcorner/article/websocket-in-net/https://www.c-sharpcorner/article/websocket-in-net/

c#编写 WebSocket 服务器

https://developer.mozilla/zh-CN/docs/Web/API/WebSockets_API/Writing_WebSocket_servershttps://developer.mozilla/zh-CN/docs/Web/API/WebSockets_API/Writing_WebSocket_servers

https://www.51cto/article/792750.htmlhttps://www.51cto/article/792750.html

20240704

  • @ModelAttribute:主要用于绑定请求中的数据到方法参数或模型属性上,常用于处理表单提交,使得复杂的对象绑定变得简单直接。它允许你预处理传入的数据,或者初始化一个命令对象供表单使用。
  • @SessionAttribute:用于从HttpSession中存取属性,这对于存储非敏感的用户信息或临时数据非常有用,比如用户设置或购物车信息。
  • @RequestAttribute:处理单一请求上下文,允许你在控制器间通过HttpServletRequest传递自定义属性,适合传递那些不需要跨请求持久化的数据。
  • RedirectAttributes:是在执行重定向操作时用来传递闪存属性的利器,这些属性在重定向后的一次请求中可用,之后自动移除,非常适合于向用户展示操作成功或失败的消息,同时保持URL的干净。

https://www.51cto/article/791988.htmlhttps://www.51cto/article/791988.html

20240702

Vue 作者尤雨溪在一次演讲中说道:框架的设计过程其实是一个不断取舍的过程 。

这代表的是什么意思呢?

想要搞明白这个,那么再来明确一下之前说过的概念:

  1. 命令式的性能 > 声明式的性能
  2. 命令式的可维护性 < 声明式的可维护性
  3. 声明式的框架本质上是由命令式的代码来去实现的
  4. 企业项目开发时,大多使用声明式框架

当我们明确好了这样的一个问题之后,那么我们接下来来思考一个问题:框架的开发与设计原则是什么呢?

我们知道对于 Vue 而言,当我们使用它的是通过 声明式 的方式进行使用,但是对于 Vue 内部而言,是通过 命令式 来进行的实现。

所以我们可以理解为:Vue 封装了命令式的逻辑,而对外暴露出了声明式的接口

那么既然如此,我们明知 命令式的性能 > 声明式的性能 。那么 Vue 为什么还要选择声明式的方案呢?

其实原因非常的简单,那就是因为:命令式的可维护性 < 声明式的可维护性 。

为什么说框架的设计过程其实是一个不断取舍的过程?

答案也就呼之欲出了,因为:

我们需要在可维护性和性能之间,找到一个平衡点。在保证可维护性的基础上,尽可能的减少性能的损耗。

所以框架的设计过程其实是一个不断在 可维护性和性能 之间进行取舍的过程。

用了一段时间你的 render 之后,却说:天天这样写也太麻烦了,每次都得写一个复杂的 vnode,能不能让我直接写 HTML 标签结构的方式 你来进行渲染呢?

你想了想之后,说:如果是这样的话,那就不是以上 运行时 的代码可以解决的了!

运行时作用是把vnode渲染出了对应的 DOM。 如果只靠 运行时,那么是没有办法通过 HTML 标签结构的方式 的方式来进行渲染解析的。编译时 把html 模板编译为vnode,运行时把vnode 渲染成实际的html.

编译时可以把html 的节点,编译成 render函数

运行时可以利用render 把 vnode 渲染成真实  dom 节点。

对于编译器而言,它的主要作用就是:把 template 中的 html 编译成 render 函数。然后再利用 运行时 通过 render 挂载对应的 DOM。

为什么 vue 要设计成一个 运行时+编译时的框架呢?

答:

  1. 针对于 纯运行时 而言:因为不存在编译器,所以我们只能够提供一个复杂的 JS 对象。
  2. 针对于 纯编译时 而言:因为缺少运行时,所以它只能把分析差异的操作,放到 编译时 进行,同样因为省略了运行时,所以速度可能会更快。但是这种方式这将损失灵活性(具体可查看第六章虚拟 DOM ,或可点击 这里 查看官方示例)。比如 svelte ,它就是一个纯编译时的框架,但是它的实际运行速度可能达不到理论上的速度。
  3. 运行时 + 编译时:比如 vue 或 react 都是通过这种方式来进行构建的,使其可以在保持灵活性的基础上,尽量的进行性能的优化,从而达到一种平衡。

https://www.51cto/article/791804.htmlhttps://www.51cto/article/791804.html

获取进程ID

ApplicationPid pid = new ApplicationPid() ;
System.out.printf("进程ID: %s%n", pid.toString()) ;

 应用运行主目录

ApplicationHome home = new ApplicationHome() ;
System.out.printf("dir: %s, source: %s%n", home.getDir(), home.getSource()) ;

https://www.51cto/article/791751.htmlhttps://www.51cto/article/791751.html

20240627

1万属性+100亿数据+10万吞吐

https://blog.itpub/28285180/viewspace-2981521/https://blog.itpub/28285180/viewspace-2981521/

20240625

https://blog.51cto/xiuji/11224898https://blog.51cto/xiuji/11224898

20240621

用于播放系统【视频/音频】输入设备的网页应用,使用了 Media Capture and Streams API 技术

Web MediaDevices Playerhttps://canwdev.github.io/web-mediadevices-player/#/

GitHub - canwdev/web-mediadevices-player: 网页版 HDMI 采集卡 / Webcam 查看器,支持截屏、录像,支持桌面客户端 (Tauri)网页版 HDMI 采集卡 / Webcam 查看器,支持截屏、录像,支持桌面客户端 (Tauri). Contribute to canwdev/web-mediadevices-player development by creating an account on GitHub.https://github/canwdev/web-mediadevices-player

beginTime:20240529endTime:2024060433312731332234323c32343e3939

这个endTime初看肯定会被日期解析给拦截的,为啥还往下执行了,难道这也能解析?写了段测试代码执行一下这两个数据,结果惊呆了。





代码非但没有报错,反而停不下来了,3000多万了。



SimpleDateFormat设置lenient为false,会严格校验日期格式,亲测有效。















164万年后的日期解析引发的OOM本文描述的问题是应用的OOM引发的接口成功率下跌的排查过程和整个问题的定位过程。https://mp.weixin.qq/s/aLxftq8OIuyQp-KKsbRgYg

20240620

https://www.51cto/article/790946.htmlhttps://www.51cto/article/790946.html

20240614

2. 多路复用
2.1 什么是 HTTP 队头阻塞?
HTTP 队头阻塞的问题,其根本原因在于 HTTP 基于请求 - 响应模式,在同一个 TCP 长连接中,前面的请求没有得到响应,后面的请求就会阻塞。当然这可以通过并发连接和域名分片的方式解决一部分问题,但这并没有真正从 HTTP 本身的层面解决问题,只是增加了 TCP 连接,分摊风险而已。而且这么做也存在弊端,多条 TCP 连接会竞争有限的带宽,让真正优先级高的请求不能优先处理。

而 HTTP2 便从 HTTP 协议本身解决了队头阻塞问题。注意,这里并不是指的 TCP 队头阻塞,而是 HTTP 队头阻塞,两者并不是一回事。 TCP 的队头阻塞是在数据包层面,单位是数据包,前一个报文没有收到便不会将后面的报文上传给 HTTP,而 HTTP 队头阻塞是在 HTTP 请求-响应层面,前一个请求没处理完,后面的请求就要阻塞住。两者所在的层次不一样。

那么 HTTP2 如何来解决所谓的队头阻塞呢?

2.2 二进制分帧


 


首先,HTTP2认为明文传输对机器而言太麻烦了,不方便计算机的解析,因为对于文本而言有多义性的字符,比如回车换行到底是内容还是分隔符,在内部需要用到状态机去识别,效率比较低。于是 HTTP2 干脆把报文全部换成二进制格式,全部传输 01 串,方便机器的解析

原来的 Headers + Body 的报文格式如今被拆分成了一个个二进制的帧,用 Header 帧存放头部字段,Data 帧存放请求体数据。分帧之后,服务器看到的不再是一个个完整的 HTTP 请求报文,而是一堆乱序的二进制帧。这些二进制帧不存在先后关系,因此也就不会排队等待,也就没有了 HTTP 的队头阻塞问题

通信双方都给对方发送二进制帧,这种二进制帧的双向传输的序列,也就做流(stream),HTTP2用流来在一个 TCP 连接上来进行多个数据帧的通信,这就是多路复用的概念。

这里有一个疑问,既然是乱序齐发,那最后如何来处理这些乱序的数据帧呢?

首先声明的是,所谓的乱序,指的是不同的 ID 的 stream 是乱序的,但同一个 stream ID 的帧一定是按顺序传输的。二进制帧达到后对方会将 Stream ID 相同的二进制帧组装成完整的请求报文和响应报文。当然,在二进制帧当中还有其它的一些字段,实现了优先级和流量控制等功能。

多路复用通过多个stream共享一个tcp连接的方式。

http2.0要解决的一大难题就是多路复用(MultiPlexing),即连接共享。上面协议解析中提到的stream id就是用作连接共享机制的。一个request对应一个stream并分配一个id,这样一个连接上可以有多个stream,每个stream的frame可以随机的混杂在一起,接收方可以根据stream id将frame再归属到各自不同的request里面。

那么HTTP/2是否解决了HOLB问题呢?

答案是“是,也不是”。因为,HOLB有两个问题:一个在HTTP层面,另一个在TCP层面。正像HTTP服务器处理请求一样,TCP协议也必须处理数据包,并确保按顺序接收所有字节,这样HTTP才能使用这些数据。一个对应多个应用程序请求的数据包队列,如果这个队伍的开头,一个数据包延迟或丢失,将会在TCP层面对HOLB问题,HTTP/2是在HTTP层面解决了这个问题。但是,它仍处于与HTTP/1.1相同的TCP协议的上层,所以它无法解决TCP的HOLB问题。甚至情况变得更糟糕,因为流水线操作/多路复用功能的存在,使用HTTP/2的应用程序可以通过一个单独的TCP连接发送更多请求

HTTP2.0的改进_http2.0如何解决对头阻塞-CSDN博客文章浏览阅读1k次。文章目录HTTP2.0的改进1. 头部压缩2. 多路复用2.1 什么是 HTTP 队头阻塞?2.2 二进制分帧3. 服务器推送HTTP2.0的改进由于 HTTPS 在安全方面已经做得非常好了,HTTP 改进的关注点放在了性能方面。主要区别有:featuredescription头部压缩Hpack + 哈夫曼编码压缩多路复用报文转为二进制,生成 steam 在 tcp 乱序传输,但是同一个 Stream ID 的帧有序传输服务器推送新建"流"主动向客户端发送消息_http2.0如何解决对头阻塞https://blog.csdn/qq_41800366/article/details/115109820

HTTP/2技术解密:如何处理线端阻塞https://www.sohu/a/155207655_604699

20240613

https://www.51cto/article/790468.htmlhttps://www.51cto/article/790468.html


20240520

谈谈优雅的钩子--bpftracebpftrace是一个内核跟踪工具,简单来说就是在函数上挂个钩子,挂上钩子后就可以将函数的入参和返回值取出来再放入程序进行二次编程,最终能让程序按照我们的意图来对函数进行观测。https://mp.weixin.qq/s/4sov7IpdWQN8c_UrzWvsCg

20240516

Redis Zset详解:排行榜绝佳选择-51CTO.COM通过本文的介绍,你学会了如何利用Spring Boot和Redis的ZSET数据结构实现热门搜索功能,并深入了解了热搜词汇的实现细节。https://www.51cto/article/788472.html

20240514

项目链接:https://github/adam-maj/tiny-gpu

从零开始手搓GPU,照着英伟达CUDA来,只用两个星期-51CTO.COM近日,美国一家 web3 开发公司的创始工程师之一 Adam Majmudar 分享了他「手搓 GPU」成功的经历,引发了网友们的一大片点赞。https://www.51cto/article/788220.html

20240513

从Java EE到Jakarta EE,企业版Java的发展历程-腾讯云开发者社区-腾讯云诞生于1995年的Java语言,年近三旬,甚至比很多同学的年龄还大得多。正所谓三十年河东,三十年河西,有人说Java已廉颇老矣,基本结构过于老套不灵活,但显示情况是它“老而不死”依旧常年霸榜,是棵常青树。https://cloud.tencent/developer/article/1847110

javax命名空间其实早已成为过去式,毕竟现在已快2024年了。这次Spring团队也是跟着JDK一起,顺势的完全摒弃掉了javax命名空间,拥抱Jakarta EE。

Java EE更名Jakarta EE对程序开发影响,你知道吗?-51CTO.COMjavax命名空间其实早已成为过去式,毕竟现在已快2024年了。这次Spring团队也是跟着JDK一起,顺势的完全摒弃掉了javax命名空间,拥抱Jakarta EE。https://www.51cto/article/788195.html

有两种情况:

  • 线程私有资源,没有线程安全问题

  • 共享资源,线程间以某种秩序使用共享资源也能实现线程安全。

线程安全代码到底是怎么编写的?https://mp.weixin.qq/s/rootXeCwRGQvncZsPHzIyg

20240511

解决视频读取和实时推理后,接下来增加视频划线功能,也可以叫电子围栏。首先给出我划线方案的代码。代码很简单,创建一个类,将鼠标事件和opencv串口绑定。点击鼠标记录一个点,存在self.marks里,一直点一直存。定义一个按键事件,按下1保存所有点位并应用。

显示效果就是绘制区域外目标不识别

【玩转香橙派Orange Pi】探索AI开发板香橙派aipro: 智能安防报警系统案例分享_ITPUB博客

所有数据库都有一个名为 “pg_default” 的默认表空间,这是一种伪表空间,因为它实际上并不存在。从系统表查询该表空间的位置时,会显示一个空位置:

复制

SELECT spcname AS "Name"
     , pg_catalog.pg_get_userbyid(spcowner) AS "Owner"
     , pg_catalog.pg_tablespace_location(oid) AS "Location"
  FROM pg_catalog.pg_tablespace
 WHERE pg_catalog.pg_tablespace.spcname = 'pg_default'
 ORDER BY 1;

    Name    |  Owner   | Location
------------+----------+----------
 pg_default | postgres |
(1 row)

PostgreSQL 临时表空间,常在闺中无人识!-51CTO.COMPostgreSQL 中有很多优秀的特性并不为大众熟知,临时表空间,就是这样一个很有用的特性。让我们来看看如何使用临时表空间、何时以及为什么使用它。https://www.51cto/article/788040.html

既然虚拟线程是守护线程,那么要如何解决上面的问题呢?在SpringBoot3.2.0-RC1版本开始为SpringApplication添加"keep-alive"属性,专门解决虚拟线程问题。

可以通过如下配置开启keepAlive。

spring:
  main:
    keep-alive: true

SpringBoot3使用虚拟线程一定要小心了-51CTO.COM虚拟线程在项目中应用时你稍不注意就可能出现问题。本篇文章将要讲述的是在非Web应用的情况下使用虚拟线程出现的问题(并非BUG)。https://www.51cto/article/788045.html


20240507

这个新功能听起来,更像是学习者在主动学习与完成作业之外,被贴身安排了一位专属的“AI 学习辅导员”,这种“千人千面”的定制化服务,既提高了混沌用户学习效率,完成教育的初衷,也是 AI 大模型在教育行业的一次创新性应用。

AI 问答已经上线混沌平台的多个知名课程,

实测响应速度及时、生成内容准确

让大模型在行业落地不是一蹴而就,为了确保 AI 问答的准确率与效果,混沌技术团队采用大模型之外,仍有部分人工参与专业课程内容的审核。第一批上线课程选择了内容新鲜度、热度较高的 17 堂课,未来混沌学园将推出的 AI 研习社与其他所有新课,AI 问答都将是标配功能。

AI辅导员:教育行业的下一个大事件?详解教育行业AI应用落地实践https://mp.weixin.qq/s/6qcLvDmhY4cQ9FxLb-P9MA

日志发展到今天,被抽象成了三层:接口层、实现层、适配层:

  • 接口层:或者叫日志门面(facade),就是interface,只定义接口,等着别人实现。

  • 实现层:真正干活的、能够把日志内容记录下来的工具。但请注意它不是上边接口实现,因为它不感知也不直接实现接口,仅仅是独立的实现。

  • 适配层:一般称为Adapter,它才是上边接口的implements。因为接口层和适配层并非都出自一家之手,它们之间无法直接匹配。而鲁迅曾经说过:「计算机科学领域的任何问题都可以通过增加一个中间层来解决」(All problems in computer science can be solved by another level of indirection. -- David Wheeler[1]),所以就有了适配层。

适配层又可以分为绑定(Binding)和桥接(Bridging)两种能力:

  • 绑定(Binding):将接口层绑定到某个实现层(实现一个接口层,并调用实现层的方法)

  • 桥接(Bridging):将接口层桥接到另一个接口层(实现一个接口层,并调用另一个接口层的接口),主要作用是方便用户低成本的在各接口层和适配层之间迁移

Java日志通关(一) - 前世今生作者日常在与其他同学合作时,经常发现不合理的日志配置以及五花八门的日志记录方式,后续作者打算在团队内做一次Java日志的分享,本文是整理出的系列文章第一篇。https://mp.weixin.qq/s/eIiu08fVk194E0BgGL5gow

Java日志通关(二) - Slf4j+Logback 整合及排包

20240425

每个程序员都应该了解的硬件知识https://mp.weixin.qq/s/XOkkCwgtX-kSxHiBlrMHqg


20240419

一个具有持久化能力的数据结构在其被修改后可以保存当前的状态,从本质上来说,这样的数据结构是不可改变类型(immutable)

Persistent Singly Linked Lists

持久化的单向链表

The singly linked list is one of the most widely used data structures in programming. It consists of a series of nodes linked together one right after the other. Each node has a reference to the node that comes after it, and the last node in the list terminates with a null reference. To traverse a singly linked list, you begin at the head of the list and move from one node to the next until you have reached the node you are looking for or have reached the last node:

单向链表是一个在编程中使用非常广泛的基础数据结构,它是由一系列相互链接的节点组成。每一个节点都拥有一个指向下一个节点的引用,链表中的最后一个节点将拥有一个空引用。如果你想遍历一个单向链表,可以从第一个节点开始,逐个向后移动,直到到达最后的节点。

如下图所示:

Let's insert a new item into the list. This list is not persistent, meaning that it can be changed in-place without generating a new version. After taking a look at the insertion operation on a non-persistent list, we'll look at the same operation on a persistent list.

让我们插入一个新的节点到这个链表中去,并且该链表是非持久化的,也就是说这个链表可以被改变而无需产生一个新的版本。在查看了非持久化链表的插入操作之后,我们将会查看同样的操作在持久化链表中。

Inserting a new item into a singly linked list involves creating a new node:

插入一个新的节点到单向列表中会涉及到创建一个新的节点:

We will insert the new node at the fourth position in the list. First, we traverse the list until we've reached that position. Then the node that will precede the new node is unlinked from the next node...

我们将会在第四个位置插入新的节点,第一我们遍历链表到达指定位置,也就是插入节点前面的那个节点,将其与后面节点断开。

...and relinked to the new node. The new node is, in turn, linked to the remaining nodes in the list:

然后链接该节点与待插入节点,在下来,链接新的节点与上一步剩余的节点。

Inserting a new item into a persistent singly linked list will not alter the existing list but create a new version with the item inserted into it. Instead of copying the entire list and then inserting the item into the copy, a better strategy is to reuse as much of the old list as possible. Since the nodes themselves are persistent, we don't have to worry about aliasing problems.

如果插入一个新的节点到持久化的单向链表中,我们不应该改变当前链表的状态,而需要创建一个新的链表而后插入指定节点。相对于拷贝当前链表,而后插入指定节点,一个更好的策略是尽可能的重用旧的链表。因为节点本身是可持久化的,所以我们不必担心对象混淆的问题。

To insert a new node at the fourth position, we traverse the list as before only copying each node along the way. Each copied node is linked to the next copied node:

为了插入新节点到第四个位置,我们遍历链表到指定位置,拷贝每个遍历节点,同时指定拷贝的节点指向其下一个节点的拷贝。

The last copied node is linked to the new node, and the new node is linked to the remaining nodes in the old list:

最后一个拷贝的节点指向新的插入节点,而后,新节点指向旧链表剩下的节点。

On an average, about N/2 nodes will be copied in the persistent version for insertions and deletions, where N equals the number of nodes in the list. This isn't terribly efficient but does give us some savings. One persistent data structure where this approach to singly linked list buys us a lot is the stack. Imagine the above data structure with insertions and deletions restricted to the head of the list. In this case, N nodes can be reused for pushing items onto a stack and N - 1 nodes can be reused for popping a stack.

平均来看,对于插入和删除操作,大约有N/2的节点将被拷贝,而N等于链表长度。这并不是特别的高效,仅仅只是节省了一些空间。与通过这样的方式来构建单向链表一样的一个数据结构是堆栈,我们可以想象一下在链表起始位置的插入以及删除操作,在这个场景中,对于堆栈来讲,压栈操作时全部节点都可以被重用,而出栈操作也有N-1个节点被重用。 

https://wwwblogs/tedzhao/archive/2008/11/12/1332112.htmlhttps://wwwblogs/tedzhao/archive/2008/11/12/1332112.html

如何找到Chrome的Crash原因?

豆米的博客全文会教你在哪里拿到Chrome的Crash日志,并借助minidump文件去解析dmp文件,获得对应的堆栈,从而定位到Chrome源码的哪一部分报错,非常有用的一份教程,全网找不到第二篇https://blog.5udou/blog/detail/1711630845567

Change Buffer会缓存多次二级索引的更改操作,并在合适的时间写入磁盘。

这里要注意的是,只是非唯一的二级索引才能用到Change Buffer,对于唯一索引,所有操作都需要判断是否违反唯一性约束。而唯一性判断,会将数据页读入到内存。所以更新操作直接在内存中进行就可以了。

Change Buffer在什么时候合并?

1 当页面被读入缓冲池时,缓冲的更改会在读取完成后合并,然后页面才可用;

2 另外,通过参数innodb_io_capacity 控制,这个参数能决定每秒从buffer pool刷新到磁盘的次数,以及合并change buffer数据的次数;

3 在崩溃恢复期间执行合并;

4 更改缓冲区中的变更操作,不单单在内存中,也会被写入到磁盘。在实例重启后,更改缓冲区合并操作会是恢复的一部分工作。

一文弄懂MySQL更改缓冲区


20240417

  • 1 业务可以拆表(把需要经常并行更新的字段, 拆到多个表, 使用PK关联起来).

  • 2 业务把对同一行的更新合并到一个会话/事务里面进行更新.

  • 3 业务把对同一行不同字段的更新分配到同一个会话的不同或相同事务中执行.

震惊!这种问题也问得出来?“能不能并发更新同一条记录”这不是常识吗?天呐, “能不能并发更新同一条记录”这个问题还需要回答吗? 这不是常识吗?https://mp.weixin.qq/s/yUYBfqtxZxKSpeS6rjHyCw

20240412

JS 的 AI 时代来了,携手迎接 JS 的 AI 时代吧!-51CTO.COM本文将分享几个引领 JS 迈向 AI 时代的机器学习库,探索这些库的魅力,携手迎接 JS 的 AI 时代吧!https://www.51cto/article/786017.html

20240410

本文主要参考以下资料:

+ <https://noxim.xyz/blog/rust-ch32v003/>

+ <https://github/ch32-rs/ch32v00x-hal>

在此感谢 Noxim 这位大神在使用 rust 编写 rv32ec 单片机程序方面的先驱探索和贡献 !

点亮一颗 LED: 单片机 ch32v003 (RISC-V) 使用 rust 编写固件 - 知乎使用 rust 编写单片机的程序 ? 很新, 但没问题. 使用 RISC-V CPU 的单片机 (比如 ch32v003) ? 也没问题. 同时使用 ? 哦嚯, 问题出现了 !! ch32v003 是一款使用 rv32ec 指令集的国产单片机, 很便宜 (某宝零卖只要 0…https://zhuanlan.zhihu/p/691404262

纯C语言手搓GPT-2,前OpenAI、特斯拉高管新项目火了-51CTO.COM今天凌晨,前特斯拉 Autopilot 负责人、OpenAI 科学家 Andrej Karpathy 发布了一个仅用 1000 行代码即可在 CPU/fp32 上实现 GPT-2 训练的项目「llm.c」。https://www.51cto/article/785725.html

20240409

UNIX 的各种版本和变体都起源于在 PDP-11 系统上运行的 UNIX 分时系统第 6 版(1976 年)和第 7 版(1979 年),它们通常分别被称为 V6 和 V7。这两个版本是在贝尔实验室以外首先得到广泛应用的 UNIX 系统。

PDP 11 计算机

使用Result<T, E>测试错误

除了使用panic!进行测试之外,你还可以返回Result<T, E>类型来表示测试的成功或失败:

复制

#[cfg(test)]
mod tests {
    #[test]
    fn test_result() -> Result<(), String> {
        if 2 + 2 == 4 {
            Ok(())
        } else {
            Err(String::from("two plus two does not equal four"))
        }
    }
}

如果测试失败,将返回一个包含错误信息的Err值。

通过上述深入的讲解和丰富的示例,相信你已经对Rust中的测试有了较为全面的理解。现在,应用这些知识点到你的Rust项目中,可以让你的代码更加稳健,降低潜在的错误和问题。

深入掌握Rust测试:从基础用例到控制测试执行的完全指南-51CTO.COM在本文中,我们将详细介绍如何在Rust中编写和控制测试,帮助您提高代码质量和稳定性。https://www.51cto/article/785619.html

20240329

使用Lua脚本可以显著提高Spring Boot应用程序的性能,尤其是在与Redis交互方面。以下是如何使用Lua脚本来实现性能优化的几种方法:

1. 减少网络开销:

Redis是内存数据库,数据存储在内存中,而网络通信通常是Redis操作的性能瓶颈之一。通过使用Lua脚本,你可以将多个操作组合成一个原子操作,从而减少了多次的网络往返次数。这对于需要执行多个Redis命令以完成一个操作的情况非常有用。

2. 原子操作:

Lua脚本的执行是原子的,这意味着在Lua脚本执行期间,没有其他客户端可以插入其他操作。这使得Lua脚本在实现诸如分布式锁、计数器、排行榜等需要原子操作的情况下非常有用。

例如,考虑一个计数器的场景,多个客户端需要原子性地增加计数。使用Lua脚本,你可以实现原子递增:

local key = KEYS[1]  
local increment = ARGV[1]  
return redis.call('INCRBY', key, increment)

3. 复杂操作:

Lua脚本允许你在Redis服务器端执行复杂的数据处理。这减少了将数据传输到客户端进行处理的开销,并允许你在Redis中执行更复杂的逻辑,从而提高性能。

例如,你可以使用Lua脚本来处理存储在多个键中的数据并返回聚合结果:

复制

local total = 0  
for _, key in ipairs(KEYS) do  
    local value = redis.call('GET', key)  
    total = total + tonumber(value)  
end  
return total
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

4. 事务:

与Lua脚本一起使用事务可以确保一系列Redis命令的原子性执行。这对于需要一组操作要么全部成功,要么全部失败的情况非常重要。

例如,你可以使用Lua脚本在事务中执行一系列更新操作,如果其中一个操作失败,整个事务将回滚:

复制

local key1 = KEYS[1]  
local key2 = KEYS[2]  
local value = ARGV[1]  
  
redis.call('SET', key1, value)  
redis.call('INCRBY', key2, value)  
  
-- 如果这里的任何一步失败,整个事务将回滚

“总之,使用Lua脚本可以大大提高Spring Boot应用程序与Redis之间的性能。它减少了网络开销,允许执行原子操作,执行复杂操作并实现事务,这些都有助于提高应用程序的性能和可伸缩性。因此,Lua脚本是在与Redis交互时实现性能优化的有力工具。

SpringBoot + Lua = 王炸!-51CTO.COM使用Lua脚本可以大大提高Spring Boot应用程序与Redis之间的性能。它减少了网络开销,允许执行原子操作,执行复杂操作并实现事务,这些都有助于提高应用程序的性能和可伸缩性。https://www.51cto/article/784925.html

消息收发架构保证了消息收发双方能够及时收发消息,但该架构不能保证消息在传输过程中不发生丢弃。当然为了达到任意一条消息都不丢的状态,最简单的方案是手机端对收到的每条消息都给服务器进行一次 ack 确认,但该方案在手机端和服务器之间的交互过多,并且也会遇到在弱网络情况下 ack 丢失等问题。为了完美的做到消息不丢,微信消息系统对消息收发引入了 sequence 机制。

   3.1 sequence 机制

  1. 每个用户都有42亿的 sequence 空间(从1到 UINT_MAX),从小到大连续分配;

  2. 每个用户的每条消息都需要分配一个 sequence;

  3. 服务器存储有每个用户已经分配到的最大 sequence;

  4. 手机端存储有已收取消息的最大 sequence。

十年前的微信消息收发架构长啥样?https://mp.weixin.qq/s/dD-aPhnynOqSC3MUYLjeAA

20240328

Rust与Java交互-JNI模块编写-实践总结 - 知乎近期工作中有Rust和Java互相调用需求,这篇文章主要介绍如何用Rust通过JNI和Java进行交互,还有记录一下开发过程中遇到的一些坑。 JNI简单来说是一套Java与其他语言互相调用的标准,主要是C语言,官方也提供了基于…https://zhuanlan.zhihu/p/568062165

客户端是如何验证服务器返回的SSL证书的有效性的?

通常客户端具体是指浏览器,客户端浏览器从从以下几个方面来验证服务器返回的SSL证书的有效性:

  1. 证书链验证

客户端首先会检查服务器返回的SSL证书是否由受信任的证书颁发机构(CA)签发,即验证证书的颁发者是否在客户端的信任列表中。这一过程称为证书链验证,确保服务器证书的可信任性。

  1. 证书有效期检查

客户端会验证证书的有效期,确保证书尚未过期。如果证书已经过期,客户端将不予信任。

  1. 主机名匹配

客户端会检查证书中的主机名与客户端正在连接的服务器主机名是否匹配。这一步骤可以防止针对恶意伪造证书的中间人攻击。

  1. 证书吊销检查

客户端还会检查证书颁发机构是否已经吊销了服务器证书。这可以通过查询证书颁发机构的证书吊销清单(CRL)或者在线证书状态协议(OCSP)来进行检查。

  1. 可选的附加验证

某些情况下,客户端还可能进行其他附加的验证,如检查证书中的扩展字段等。

通过以上多个方面的验证,客户端就可以确保服务器返回的SSL证书的有效性和可信任性,从而建立起安全的通信连接。如果证书验证失败,客户端将会发出警告或者拒绝连接,以保护数据通信的安全性。

深入理解SSL协议:从理论到实践-51CTO.COM使用浏览器访问你的网站,由原来http访问,改成https访问,如果可以通过 HTTPS 访问并且浏览器地址栏中显示安全连接的标识,那就说明配置已经成功。https://www.51cto/article/784814.html

使用 JDK 17 新增的 HexFormat 工具类来格式化和解析十六进制字符串,通过 HexFormat 工具类还可以轻松地设置分隔符,字母大小写以及前缀和后缀。

使用 HexFormat 来格式化和解析十六进制字符串-51CTO.COM本文介绍了如何使用 JDK 17 新增的 HexFormat​ 工具类来格式化和解析十六进制字符串,通过 HexFormat 工具类还可以轻松地设置分隔符,字母大小写以及前缀和后缀。https://www.51cto/article/784822.html

强引用的特点在于,只要强引用存在,垃圾回收器就不会回收这个对象。换句话说,只有当没有任何强引用指向一个对象时,该对象才会被垃圾回收器回收。

软引用是一种相对较弱的引用类型。其特点是在内存不足时,垃圾回收器会回收软引用指向的对象,以释放内存。相较于强引用,软引用具有更低的优先级,在内存不足时会被垃圾回收器优先回收。

软引用通常用于一些内存敏感的场景,比如缓存。在缓存中,我们可以使用软引用来持有对象的引用,当内存不足时,垃圾回收器会回收软引用指向的对象,从而释放内存。这样一来,我们可以避免因为缓存占用过多内存而导致的性能问题。

弱引用是Java中的一种引用类型,比软引用更弱,也更容易被垃圾回收器回收。与软引用类似,弱引用同样是为了解决内存敏感的场景而设计的。

。虚引用是所有引用类型中最弱的一种引用类型,也是最不直接的引用类型。与其他引用类型不同,虚引用并不能通过 get() 方法来获取被引用的对象,其唯一的作用是在对象被回收时收到一个系统通知。

虚引用通常用于一些高级的内存管理场景,比如管理堆外内存。在NIO中的DirectByteBuffer对象就是一个典型的应用场景。当虚引用接收到通知时,我们可以进行一些必要的清理工作,比如释放堆外内存,以确保资源得到及时释放,避免资源泄漏。

拿下阿里面试:揭秘JVM对象引用的奥秘!-51CTO.COM通过本文的介绍,相信大家对JVM对象引用有了更深入的了解。强引用、软引用、弱引用和虚引用各有其特点,我们可以根据不同的场景选择合适的引用类型来管理对象的生命周期,从而更好地优化内存、避免内存泄漏等问题。https://www.51cto/article/784741.html

大模型应用的 10 种架构模式_生成式 AI_曹洪伟_InfoQ精选文章大模型应用的 10 种架构模式https://www.infoq/article/IDih2TUJ1xYL3VxO16xb


20240322

0拷贝**:指的是不需要在用户空间和内核空间中来回复制数据,在用户和内核空间之间0拷贝。而不是不复制数据,

面试官:说说零拷贝的实现原理?-51CTO.COM零拷贝技术可以利用 Linux 下的 MMap、sendFile 等手段来实现,使得数据能够直接从磁盘映射到内核缓冲区,然后通过 DMA 传输到网卡缓存,整个过程中 CPU 只负责管理和调度,而无需执行实际的数据复制指令。https://www.51cto/article/784316.html

20240321

一、清理无用的容器

使用docker ps -a命令可以列出所有的容器,包括正在运行的和已经停止的。我们可以根据容器的状态来判断是否需要删除。

示例:删除所有已经停止的容器

docker container prune

这个命令会删除所有已经停止的容器,但不会删除正在运行的容器。如果你想删除所有容器(包括正在运行的),可以加上-f或--force参数:

docker container prune -f
二、清理无用的镜像

使用docker images命令可以列出所有的镜像。同样,我们可以根据镜像的使用情况来判断是否需要删除。

示例:删除所有无标签的镜像

docker image prune

这个命令会删除所有无标签的镜像,也就是那些没有被任何容器引用的镜像。如果你想删除所有未使用的镜像(包括有标签但未被引用的),可以加上-a参数:

docker image prune -a
三、清理Docker占用的空间

除了容器和镜像外,Docker还会占用一些其他的空间,比如构建缓存、容器卷等。为了彻底清理Docker占用的空间,我们可以使用docker system prune命令。

示例:清理Docker占用的所有空间

docker system prune

这个命令会删除所有已经停止的容器、无标签的镜像以及构建缓存。如果你想删除所有未使用的资源(包括有标签但未被引用的镜像和容器卷),可以加上-a参数:

docker system prune -a

每天一个Linux系统小技巧:Docker容器与镜像的清理-51CTO.COMDocker,作为目前最流行的容器化技术之一,广泛应用于开发、测试和生产环境中。然而,随着容器和镜像的不断创建和使用,系统中可能会积累大量的无用容器和镜像,占用大量的磁盘空间,甚至影响系统的性能。https://www.51cto/article/784179.html

动态编译时一定要添加-g参数生成完整调试信息,不然热部署代码Debug会发现方法栈内变量没有名字、Jacoco布尔数组透出、slot对不上等问题。(坑了我半年多一直没发现原因)

远程热部署的落地与思考-动态编译篇-51CTO.COM远程热部署是参考美团Sonic并结合转转的业务场景研发的一款热部署组件,由Java Agent与IDEA插件组成。整个热部署全流程涉及知识范围广泛,三言两语无法描述清楚,全流程会拆分成专题的形式进行分享。本文主要选讲在落地过程中遇到的一些Sonic未提及的问题与自己的思考感悟。https://www.51cto/article/784185.html


20240319

通过上图,我们可以清楚的了解到,在整个文件写入的过程中,其需要经过很多个buffer缓存。如IO Buffer、Page Cache、驱动缓存、Disk cache。所有这些缓存存在的意义都是为了提升我们的文件读写速度。但是在我们需要确保数据百分之百安全的场景下(如WAL),这些buffer就成了一个一个的障碍。为了让数据从应用层直接万无一失的写入磁盘,我们需要合理的利用上面提到的各类方法调用。根据不同的业务需求我们可以采用不同的方法组合。

1、允许应用奔溃的写操作

通过上图可知只有数据写入内核的Page Cache中之后,应用崩溃才不会导致数据丢失。通常我们有两个种方式可以将数据写入到内核中。

A、普通写入(write/flush/close)

咱们平时调用write(fwrite)的时候,数据仅仅是从应用写入到了C标准库的IO Buffer。此时数据还在用户空间。如果此时我们就调用close关闭操作。那么数据通常不会理解写入内核,更不用说磁盘了。通常需要等到C标准库的IO Buffer满了之后才能被主动写入内核缓存的Page Cache。通过上图我可以看到,我们可以通过flush将数据主动写入到内核的Page Cache中。这就是为什么我们通常被建议在关闭之前flush文件。因为当数据进入内核之后相对于应用来说,该数据就是安全的。此时如果应用挂了,咱们的数据还是安全的。且能够被内部后续写入磁盘。

B、mmap

在持久化中经常被提及的mmap的数据其实也只是在Application Cache和内核Page Cache中建立了映射关系。这样所有在应用层对数据的操作实际是映射到内核的Page Cache中的。因此使用mmap我们不用调用flush,也不用担心数据会因为应用崩溃而丢失。

当然mmap除了能够直接在应用层操作内核中的数据,同时也因此减少了不必要的上下文切换。比如普通写入中,我们调用flush是需要相应的上下文切换呢,这里会有一定的开销。这也是为什么在持久化场景中,我们通常使用mmap的主要原因。

2、允许操作系统崩溃的写操作

通过上图可知,只有当数据被写入到磁盘缓存或者磁盘介质中之后,才能够保证当系统崩溃之后,数据不会丢失(如果数据在磁盘缓存中,则需要磁盘具有备份电源)。

那么需要将内核中的Page Cache中的数据写入到磁盘(缓存)中,我们只需要调用fsync(fdatasync)即可。此时就算机器宕机了,咱们的数据还是安全的。这也就是很多WAL都是fsync刷盘的原因。

调用fsync后数据就安全了么(fsync、fwrite、fflush、mmap、write barriers详解) - 墨天轮fsync和fwrite/fflush组合的区别是啥?mmap和fsync有什么关系?为什么都说fsync之后数据就不会丢失了,真的不会丢失么?数据写入磁盘就安全了么?\x0a为什么不能直接close文件,而需要先flush?https://www.modb.pro/db/228651


20240318

Weak 非常类似于 Rc,但是与 Rc 持有所有权不同,Weak 不持有所有权,它仅仅保存一份指向数据的弱引用:如果你想要访问数据,需要通过 Weak 指针的 upgrade 方法实现,该方法返回一个类型为 Option<Rc<T>> 的值。

看到这个返回,相信大家就懂了:何为弱引用?就是不保证引用关系依然存在,如果不存在,就返回一个 None

因为 Weak 引用不计入所有权,因此它无法阻止所引用的内存值被释放掉,而且 Weak 本身不对值的存在性做任何担保,引用的值还存在就返回 Some,不存在就返回 None

所以 weak 引用多用在缓存实现中,如果返回则缓存有效,返回None 则缓存无效

Weak 与循环引用 - Rust语言圣经(Rust Course)https://course.rs/advance/circle-self-ref/circle-reference.html#tree-%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84

在今天的技术世界中,一些团队或开发者往往假装他们在做高并发、超复杂的系统,但他们可能甚至不知道数据库索引的基础知识。我们正处在一个充满不合理的过度自信、浪费的海洋中,那么真正的骗子是谁呢?

为什么会变成这样呢?我们的目标本来应该是让系统正常的运行起来,满足业务的需要,但是现在却变成了要解决根本没有的问题(微服务带来的问题),结果就是熬了好多个夜,掉了好多根头发,给服务器运营商分了不少钱,结果呢?

不要解决您没有的问题

这是一个简单的问题,您解决的是什么问题?是规模问题吗?

当你准备将系统做成复杂的微服务架构时,你就要面对下面众多的问题:

  • 系统的拆分策略是什么,哪些功能要归结为一个服务,哪些要隔离开;
  • 分布式系统是为高并发、稳定性而建立的。您的系统是否可以同时扩展性和稳定性?
  • 如果其中一个服务崩溃或变慢会发生什么?会影响到其他服务吗?
  • 你是否考虑了如果将会发生了变化,将如何应对?
  • 要做压力测试,要有断容器,有队列,分布式事务等等。
  • 每个接口都有合理的超时时间吗?
  • 有没有绝对安全的保护措施,以确保简单的变更不会导致整个系统崩溃?

这是,需要了解和调整的配置和功能就是无穷无尽的,这样,我们在解决业务问题的同时,又增加许多本来不必要的问题,甚至这些不必要的问题超过了原本的业务问题。

事实上,大多数公司永远不会达到真正需要构建真正分布式系统的规模,这实际上可能只是在浪费金钱和时间。

比分布式系统更糟糕的是错误的分布式系统。

一千个微服务之死-51CTO.COM在今天的技术世界中,一些团队或开发者往往假装他们在做高并发、超复杂的系统,但他们可能甚至不知道数据库索引的基础知识。我们正处在一个充满不合理的过度自信、浪费的海洋中,那么真正的骗子是谁呢?https://www.51cto/article/783777.html

20240315

MYSQL会有一个后台线程,定时刷新redo log buffer 中的数据到磁盘上。除此以外,当innodb_flush_log_at_trx_commit 值为1时 redo log则会在在prepare阶段将redo log buffer 中的数据落入磁盘。

当innodb_flush_log_at_trx_commit 值为 0 时,redo log buffer则不会在prepare或者事务提交时刷盘,而是由后台定时任务定时刷新redo log buffer中的数据到磁盘上。

当innodb_flush_log_at_trx_commit 值为2时,则是将redo log buffer中的内容刷新到文件系统缓存中,由操作系统决定何时刷新到磁盘上。

所以可以看到,在事务执行过程中,redo log是可能一部分在内存,一部分已经落入磁盘了。

3, 在写完redo log后,会去写binlog,写binlog同样不是直接写文件,而是写到binlog cache中,那么binlog是何时刷新到磁盘上呢,这个是由sync_bin参数决定的。

  • sync_binlog = 0 :提交事务时,将内存中的binlog cache写到文件系统缓存中,后续交由操作系统决定何时将数据持久化到磁盘。
  • sync_binlog = 1 :提交事务时,将binlog cache中的数据写入到文件系统缓存,并立马刷新到磁盘。
  • sync_binlog =N(N>1) :提交事务时,都写到文件系统缓存,但累积 N 个事务后才 fsync 刷新到磁盘。

从MYSQL 实现两阶段提交的逻辑,可以归纳下,它是如何做到对两个业务做到最终一致的。

我举个业务上的例子,  比如有A,B两个服务,A服务依赖B服务,如何保证在A服务上的数据操作和请求B服务接口这两个动作同时成功或失败❓

我直接说下结论:

借鉴两阶段提交的逻辑,我们可以将A服务的数据操作在业务设计上增加一个预扣减的概念,先锁定A服务数据资源,然后去请求B服务的接口,失败的话,则释放A服务锁定的数据资源,成功的话则进行真实的扣减。

除此以外,还需要增加一个对A服务数据进行补偿修复的定时任务,类似与MYSQL数据库宕机根据binlog是否完整看事务是否提交一样,定时任务定期查看还没有终结的A服务数据,拎出来请求B服务查看业务成功状态,B服务返回成功,则将A服务的业务数据进行真实扣减,否则释放A服务锁定的数据资源。

通过两阶段提交,来查看业务的最终一致性。

MySQL 是如何保证 binlog 和 redo log同时提交的?-51CTO.COM借鉴两阶段提交的逻辑,我们可以将A服务的数据操作在业务设计上增加一个预扣减的概念,先锁定A服务数据资源,然后去请求B服务的接口,失败的话,则释放A服务锁定的数据资源,成功的话则进行真实的扣减。https://www.51cto/article/783720.html

20240313

角色转换这幅图是领袖、候选人和群众的角色切换图,我先简单总结一下:

  • 群众 -> 候选人:当开始选举,或者“选举超时”时
  • 候选人 -> 候选人:当“选举超时”,或者开始新的“任期”
  • 候选人 -> 领袖:获取大多数投票时
  • 候选人 -> 群众:其它节点成为领袖,或者开始新的“任期”
  • 领袖 -> 群众:发现自己的任期ID比其它节点分任期ID小时,会自动放弃领袖位置

深度解析 Raft 协议与 KRaft 实战演示-51CTO.COMRaft 协议是一种分布式一致性算法,它用于在分布式系统中的多个节点之间达成一致性。Raft 协议的目标是提供一种相对简单、易于理解和实现的方法,以确保在网络分区、节点故障等情况下,系统仍然能够保持一致性和可用性。https://www.51cto/article/783459.html

sendfile是Linux2.1内核版本后引入的一个系统调用函数,API如下:

复制

ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
  • 1.
  • out_fd:为待写入内容的文件描述符,一个socket描述符。,
  • in_fd:为待读出内容的文件描述符,必须是真实的文件,不能是socket和管道。
  • offset:指定从读入文件的哪个位置开始读,如果为NULL,表示文件的默认起始位置。
  • count:指定在fdout和fdin之间传输的字节数。

sendfile表示在两个文件描述符之间传输数据,它是在操作系统内核中操作的,避免了数据从内核缓冲区和用户缓冲区之间的拷贝操作,因此可以使用它来实现零拷贝。

sendfile实现的零拷贝流程如下:

图片

sendfile实现的零拷贝

  1. 用户进程发起sendfile系统调用,上下文(切换1)从用户态转向内核态
  2. DMA控制器,把数据从硬盘中拷贝到内核缓冲区。
  3. CPU将读缓冲区中数据拷贝到socket缓冲区
  4. DMA控制器,异步把数据从socket缓冲区拷贝到网卡,
  5. 上下文(切换2)从内核态切换回用户态,sendfile调用返回。

可以发现,sendfile实现的零拷贝,I/O发生了2次用户空间与内核空间的上下文切换,以及3次数据拷贝。其中3次数据拷贝中,包括了2次DMA拷贝和1次CPU拷贝。那能不能把CPU拷贝的次数减少到0次呢?有的,即带有DMA收集拷贝功能的sendfile!

linux 2.4版本之后,对sendfile做了优化升级,引入SG-DMA技术,其实就是对DMA拷贝加入了scatter/gather操作,它可以直接从内核空间缓冲区中将数据读取到网卡。使用这个特点搞零拷贝,即还可以多省去一次CPU拷贝。

sendfile+DMA scatter/gather实现的零拷贝流程如下:

图片

5. Java提供的零拷贝方式

  • Java NIO对mmap的支持
  • Java NIO对sendfile的支持

5.1 Java NIO对mmap的支持

Java NIO有一个MappedByteBuffer的类,可以用来实现内存映射。它的底层是调用了Linux内核的mmap的API。

mmap的小demo如下:

复制

public class MmapTest {

    public static void main(String[] args) {
        try {
            FileChannel readChannel = FileChannel.open(Paths.get("./jay.txt"), StandardOpenOption.READ);
            MappedByteBuffer data = readChannel.map(FileChannel.MapMode.READ_ONLY, 0, 1024 * 1024 * 40);
            FileChannel writeChannel = FileChannel.open(Paths.get("./siting.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
            //数据传输
            writeChannel.write(data);
            readChannel.close();
            writeChannel.close();
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
    }
}

5.2 Java NIO对sendfile的支持

FileChannel的transferTo()/transferFrom(),底层就是sendfile() 系统调用函数。Kafka 这个开源项目就用到它,平时面试的时候,回答面试官为什么这么快,就可以提到零拷贝sendfile这个点。

复制

@Override
public long transferFrom(FileChannel fileChannel, long position, long count) throws IOException {
   return fileChannel.transferTo(position, count, socketChannel);
}

sendfile的小demo如下:

复制

public class SendFileTest {
    public static void main(String[] args) {
        try {
            FileChannel readChannel = FileChannel.open(Paths.get("./jay.txt"), StandardOpenOption.READ);
            long len = readChannel.size();
            long position = readChannel.position();
            
            FileChannel writeChannel = FileChannel.open(Paths.get("./siting.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
            //数据传输
            readChannel.transferTo(position, len, writeChannel);
            readChannel.close();
            writeChannel.close();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

看一遍就理解:零拷贝详解-51CTO.COM做服务端开发的小伙伴,文件下载功能应该实现过不少了吧。如果你实现的是一个web程序,前端请求过来,服务端的任务就是:将服务端主机磁盘中的文件从已连接的socket发出去。https://www.51cto/article/783439.html

20240311

PostgreSQL中IN, EXISTS在SQL查询中到底有无区别https://mp.weixin.qq/s/vxajtSr-WmJS-J-5XlwFWQ

  • 更好的解决方案是使用--user以已知 uid 启动容器(也可以使用用户名,但请记住这只是提供主机用户名系统中的 uid 的一种更友好的方式),然后限制主机上您决定容器将以其运行的 uid 的访问权限。
docker run --user 0 -d test

在创建容器时指定用户标志也会覆盖 Dockerfile 中的值。

理解用户名、组名、用户ID(UID)和组ID(GID)在容器内运行的进程与主机系统之间的映射是构建安全系统的重要一环。如果没有提供其他选项,容器中的进程将以root用户身份执行(除非在Dockerfile中提供了不同的UID)。

理解 Docker 容器中 UID 和 GID 的工作原理-51CTO.COM如果容器内部的进程正在执行的已知 uid,那么简单地限制对主机系统的访问,使容器中的 uid 仅具有有限访问权限就可以了。https://www.51cto/article/783271.html


20240308

jpackage  --input . --name helloApp1 --win-console --win-shortcut --main-jar hello.jar

 jpackage 工具(从 Java 14 版本开始提供),该工具可以打包 JAR 文件和必要的 JRE 环境,生成适用于 Windows、Linux、MacOS 的可执行程序,使得 Java 应用程序能够在无需预装 JRE 的情况下运行。

Jpackage-制作无需预装Java环境的Jar可执行程序-51CTO.COM本文介绍了在没有预装 JRE 环境的系统上运行 Java 程序的方法。首先,介绍如何使用 Java 的 jar​ 命令行工具创建一个可执行的 JAR 文件,这需要编写 Java 程序,配置 MANIFEST.MF​ 文件,并使用 jar​ 命令创建包含主类的 JAR 文件。https://www.51cto/article/783208.html


20240307

Rust中的数据可视化指南-51CTO.COM强调性能、安全性和并发性的Rust可能不是首先想到的数据可视化语言。然而,它的生态系统正在滚雪球般扩大,各种库和工具不断涌现,以满足各种需求,包括数据可视化。https://www.51cto/article/783094.html


20240306

如何精通前端Async/Await的错误处理方式?-51CTO.COM异步错误处理对于保证应用程序的稳定性和可靠性至关重要。在本文中,我们介绍了几种常见的异步错误处理方法,并提供了相应的实现示例。在实际开发中,可以根据具体情况选择合适的错误处理方式,并结合项目需求进行适当的调整和优化。https://www.51cto/article/782944.html


20240305

在这里,我们初始化了一个文件,并把它映射到了128M的内存中。分FileChannel还有MMAP的方式,通过顺序或随机读写,写了一些内容并读取一部分内容。

运行结果是:

FileChannel初始化时间:7ms
MMAPFile初始化时间:8ms
FileChannel顺序读写时间:420ms
MMAPFile顺序读写时间:20ms
FileChannel随机读写时间:860ms
MMAPFile随机读写时间:45ms

可以看到,通过MMAP内存映射文件的方式操作文件,更加快速,并且性能提升的相当明显,大约20 倍的性能提升

JDK核心JAVA源码解析(5) - JAVA File MMAP原理解析 - 知乎想写这个系列很久了,对自己也是个总结与提高。原来在学JAVA时,那些JAVA入门书籍会告诉你一些规律还有法则,但是用的时候我们一般很难想起来,因为我们用的少并且不知道为什么。知其所以然方能印象深刻并学以致用…https://zhuanlan.zhihu/p/258934554


20240301

纵览机器学习前生今世,万字整理谷歌首席科学家 Jeff Dean 一小时演讲-CSDN博客文章浏览阅读1.8k次,点赞12次,收藏27次。经过算法的改进和机器学习专用硬件的显著提升,我们现在能够构建比以往任何时候都更为强大的通用机器学习系统。演讲者| Jeff Dean整理 |王启隆自从 2017 年谷歌发表了题为 “Attention is All You Need” 的重磅论文,其中提出的“自注意力”这一革命性的概念成为 Transformer 模型的核心部分,引领了我们目前正在经历的 AIGC 革命。然而,当前的大模型领域...https://blog.csdn/dQCFKyQDXYm3F8rB0/article/details/136384262

几乎所有我喜欢的软件架构师,都认同康威定律(Conway Law),认为这个定律非常重要,足以影响到所有系统。而且,你没法与之抗争,想要抗拒康威定律注定要失败。

康威定律的最好表述是:"任何系统的构成,都反映了设计这个系统的组织结构。"

它的出处是 Melvin Conway 在1968年写的一篇文章。后来,弗雷德·布鲁克斯(Fred Brooks)在著名的《人月神话》(The Mythical Man-Month)引用了这条定律。

Melvin Conway 观察到,软件系统的架构看起来与构建它的开发团队的组织结构非常相似。

最初的描述是,如果一个团队编写一个编译器,那么它将是一个单通道编译器;但是,如果两个团队共同开发,那么它将是一个双通道编译器。这个描述后来被发现,广泛适用于大量系统。

正如我的同事 Chris Ford 对我说的:"软件耦合是由人类交流促成的。" 如果我可以轻松与代码作者交谈,那么我就更容易对代码有更深入的了解,因此我的代码更容易耦合到该代码。

应对康威定律的第一步是不要与之抗争。我仍然记得一位技术主管,他刚刚被任命为​​一个大型新项目的架构师,该项目由分布在世界各地不同城市的六个团队组成。"我做出了第一个架构决定",他告诉我:"就是这个系统将有六个主要的子系统。我不知道它们会是什么子系统,但肯定会有六个。"

为了适应康威定律,现在有一种策略,就是一旦定下软件架构,就相应改变组织结构,让紧密耦合模块的开发者更容易沟通。

Conway's Lawhttps://martinfowler/bliki/ConwaysLaw.html

从零开始构建编程语言的挑战与乐趣-51CTO.COM大约 15 年前,当我刚开始职业生涯并偶然踏入编译器构建领域时,我的团队架构师递给我一本 《龙书》,并强调这是一部经典之作,需要倍加珍惜。不过不幸的是,有一天晚上我阅读时不慎睡着,书本从手中滑落,重重地落在地板上。还书的时候,我非常希望他没注意到封面上的那个小凹痕。https://www.51cto/article/781860.html

一条原则:如果一个类的equals方法相等,那么它们的hashcode方法必须相等。由于没有重写hashcode方法导致违背这一原则。因此,在隐式使用Map时就出现了莫名其妙的问题

为什么list.contains方法不会出现这个问题呢?

因为List的底层结构是数组,不像Map那样为了提升效率先对Key进行hash处理比较。

// 这样写有问题,会隐式使用hashcode
List<DeviceModel> models =  list.stream().map(ProjectId::getDeviceModel).distinct().collect(Collectors.toList()); 

// 这样写没有问题 没有用到hashcode 只用equals
List<DeviceModel> results = new ArrayList<>(); 
for (DeviceModel deviceModel : list.stream().map(ProjectId::getDeviceModel).collect(Collectors.toList())) { 
    if (!results.contains(deviceModel)) { 
        results.add(deviceModel); 
    } 
} 
System.out.println(results); 

大佬也Hashcode方法上翻船了,不小心秀了一把!-hashcode方法详解大佬解决问题思路值得我们先学习一波,在大佬决定最终放弃的前,给我发消息了,问有兴趣看一看没。有这么奇怪的现象,怎能不研究一下呢?https://www.51cto/article/652881.html


20240229

我们把库里的文本都转换为simhash签名,并转换为long类型存储,空间大大减少。现在我们虽然解决了空间,但是如何计算两个simhash的相似度呢?难道是比较两个simhash的01有多少个不同吗?对的,其实也就是这样,我们通过海明距离(Hamming distance)就可以计算出两个simhash到底相似不相似。两个simhash对应二进制(01串)取值不同的数量称为这两个simhash的海明距离。举例如下: 1010100110 从第一位开始依次有第一位、第四、第五位不同,则海明距离为3。对于二进制字符串的a和b,海明距离为等于在a XOR b运算结果中1的个数(普遍算法)。

经过simhash映射以后,我们得到了每个文本内容对应的simhash签名,而且也确定了利用汉明距离来进行相似度的衡量。那剩下的工作就是两两计算我们得到的simhash签名的汉明距离了,这在理论上是完全没问题的,但是考虑到我们的数据是海量的这一特点,我们是否应该考虑使用一些更具效率的存储呢?其实SimHash算法输出的simhash签名可以为我们很好建立索引,从而大大减少索引的时间,那到底怎么实现呢?

1. 当文本内容较长时,使用SimHash准确率很高,SimHash处理短文本内容准确率往往不能得到保证;

  2. 文本内容中每个term对应的权重如何确定要根据实际的项目需求,一般是可以使用IDF权重来进行计算。

1、分词,把需要判重的文本分词,形成去掉噪音词的单词序列并为每个词加上权重。我们假设权重分为5个级别(1~5)。比如:“ 美国“51区”雇员称内部有9架飞碟,曾看见灰色外星人 ” ==> 分词后为 “ 美国(4) 51区(5) 雇员(3) 称(1) 内部(2) 有(1) 9架(3) 飞碟(5) 曾(1) 看见(3) 灰色(4) 外星人(5)”,括号里的权重代表重要程度,数字越大越重要,这里我们采用ansj分词器,tf-idf的方式计算权重。生成一个词和对应权重的map。

2、hash,通过hash算法把每个词变成hash值,比如“美国”通过hash算法计算为 100101,“51区”通过hash算法计算为 101011。这样我们的字符串就变成了一串串数字。

3、加权,通过2步骤的hash生成结果,需要按照单词的权重形成加权数字串,比如“美国”的hash值为“100101”,通过加权计算为“4 -4 -4 4 -4 4”;“51区”的hash值为“101011”,通过加权计算为 “ 5 -5 5 -5 5 5”。

4、合并,把上面各个单词算出来的序列值累加,变成只有一个序列串。比如 “美国”的 “4 -4 -4 4 -4 4”,“51区”的 “ 5 -5 5 -5 5 5”, 把每一位进行累加, “4+5 -4+-5 -4+5 4+-5 -4+5 4+5” ==》 “9 -9 1 -1 1 9”。这里作为示例只算了两个单词的,真实计算需要把所有单词的序列串累加。

5、降维,把4步算出来的 “9 -9 1 -1 1 9” 变成 0 1 串,形成我们最终的simhash签名。如果每一位大于0 记为 1,小于0 记为 0。最后算出结果为:“1 0 1 0 1 1”。

计算原理
分词:将处理后的文本(去除特殊字符等)进行分词,可为分词设置权重,得到分词向量
计算:通过Hash函数计算每个分词向量的Hash值,值为二进制串
加权:计算权重向量=每个分词的hash*该词对应的权重weight
合并:将所有分词的权重向量累加,得到一个新的权重向量
降维:对上述合并后得到的权重向量,大于0的位置为1,小于等于0的位置为0,从而得到文本的simHash值

SimHash算法原理与应用(Java版)_java simhash-CSDN博客文章浏览阅读2k次。本篇博客将介绍的SimHash算法属于一种局部敏感哈希算法,利用海明距离比较内容之间的相似度。_java simhashhttps://blog.csdn/hu_zhiting/article/details/127725244

Simhash在内容去重中的应用,你学会了吗?-51CTO.COM内容去重有很多应用场景,simhash作为谷歌选来作为网页内容去重的一种算法,在海量数据去重的效率上有着明显的速度优势,相对传统文本相似性方法,simhash的降维解决了计算量庞大的问题,但对短文本的去重准确率上有较明显的欠缺,因此我们在了解业务的背景和需求后才能做出相对合理的选择。​https://www.51cto/article/782467.html

使用SimHash进行海量文本去重-腾讯云开发者社区-腾讯云传统的Hash算法只负责将原始内容尽量均匀随机地映射为一个签名值,原理上仅相当于伪随机数产生算法。传统的hash算法产生的两个签名,如果原始内容在一定概率下是相等的;如果不相等,除了说明原始内容不相等外,不再提供任何信息,因为即使原始内容只相差一个字节,所产生的签名也很可能差别很大。所以传统的Hash是无法在签名的维度...https://cloud.tencent/developer/article/1682570


20240227

终于有篇文章把后管权限系统设计讲清楚了-51CTO.COM本文给大家讲解了常见后管系统的权限控制系统该如何设计以及 Spring Security 实战,在常用的 RBAC0 权限模型下,权限要素包含用户、角色、权限(菜单)三要素,只要大家能理解用户、角色、权限(菜单)三要素的设计理念以及表结构后,相信就能轻松掌握后管权限控制系统的设计精髓。https://www.51cto/article/782251.html


20240226

任何傻瓜都可以编写计算机可以理解的代码。优秀的程序员可以编写人类可以理解的代码。

我相信这句话可以作为评估优秀代码的起点:它必须可读、可理解且清晰。编写优秀代码的首要原则是将人类读者放在第一位。

微服务架构区别于其他概念的本质在于,将大型单体分解为独立的微服务后,不同模块之间的界限更加清晰。与维护单一系统的数百人的大型团队相比,许多各自维护自己的微服务的小型组织可以更高效地运营。

在没有特定组织规模(即“人”)的情况下谈论微服务的各种技术优势和奇特功能是本末倒置

Focusing on the cost-effectiveness of learning
注重学习的成本效益


下图显示了学习成果与投入的努力之间的关系。


学习成果与投资图表,x 轴为学习努力,y 轴为绩效


该图表明,在学习的初始阶段,相对较小的投资回报会迅速增长。然而,一旦结果超过一定阈值,继续改进所需的投资就会呈指数级增长。


因此,我建议,每当你开始学习新东西时,首先在脑海中弄清楚这个问题:“我应该在图表的哪个点停止?”而不是坚持不懈地学习。


知识的海洋是无边无际的。有些事情需要多年的不断学习和完善,而另一些事情只需要摸一摸就能获得足够的理解。准确评估和分配你有限的学习精力有时甚至比努力学习本身更重要。

After 14 years in the industry, I still find programming difficult | Pigleipiglei's bloghttps://www.piglei/articles/en-programming-is-still-hard-after-14-years/


需要 TIME-WAIT 状态,主要是两个原因:

  • 防止历史连接中的数据,被后面相同四元组的连接错误的接收;
  • 保证「被动关闭连接」的一方,能被正确的关闭;

原因一:防止历史连接中的数据,被后面相同四元组的连接错误的接收

为了能更好的理解这个原因,我们先来了解序列号(SEQ)和初始序列号(ISN)。

  • 序列号,是 TCP 一个头部字段,标识了 TCP 发送端到 TCP 接收端的数据流的一个字节,因为 TCP 是面向字节流的可靠协议,为了保证消息的顺序性和可靠性,TCP 为每个传输方向上的每个字节都赋予了一个编号,以便于传输成功后确认、丢失后重传以及在接收端保证不会乱序。序列号是一个 32 位的无符号数,因此在到达 4G 之后再循环回到 0
  • 初始序列号,在 TCP 建立连接的时候,客户端和服务端都会各自生成一个初始序列号,它是基于时钟生成的一个随机数,来保证每个连接都拥有不同的初始序列号初始化序列号可被视为一个 32 位的计数器,该计数器的数值每 4 微秒加 1,循环一次需要 4.55 小时

给大家抓了一个包,下图中的 Seq 就是序列号,其中红色框住的分别是客户端和服务端各自生成的初始序列号。

通过前面我们知道,序列号和初始化序列号并不是无限递增的,会发生回绕为初始值的情况,这意味着无法根据序列号来判断新老数据

假设 TIME-WAIT 没有等待时间或时间过短,被延迟的数据包抵达后会发生什么呢?

  • 服务端在关闭连接之前发送的 SEQ = 301 报文,被网络延迟了。
  • 接着,服务端以相同的四元组重新打开了新连接,前面被延迟的 SEQ = 301 这时抵达了客户端,而且该数据报文的序列号刚好在客户端接收窗口内,因此客户端会正常接收这个数据报文,但是这个数据报文是上一个连接残留下来的,这样就产生数据错乱等严重的问题。

为了防止历史连接中的数据,被后面相同四元组的连接错误的接收,因此 TCP 设计了 TIME_WAIT 状态,状态会持续 2MSL 时长,这个时间足以让两个方向上的数据包都被丢弃,使得原来连接的数据包在网络中都自然消失,再出现的数据包一定都是新建立连接所产生的。

原因二:保证「被动关闭连接」的一方,能被正确的关闭

在 RFC 793 指出 TIME-WAIT 另一个重要的作用是:

TIME-WAIT - represents waiting for enough time to pass to be sure the remote TCP received the acknowledgment of its connection termination request.

也就是说,TIME-WAIT 作用是等待足够的时间以确保最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭。

如果客户端(主动关闭方)最后一次 ACK 报文(第四次挥手)在网络中丢失了,那么按照 TCP 可靠性原则,服务端(被动关闭方)会重发 FIN 报文。

假设客户端没有 TIME_WAIT 状态,而是在发完最后一次回 ACK 报文就直接进入 CLOSE 状态,如果该 ACK 报文丢失了,服务端则重传的 FIN 报文,而这时客户端已经进入到关闭状态了,在收到服务端重传的 FIN 报文后,就会回 RST 报文。

服务端收到这个 RST 并将其解释为一个错误(Connection reset by peer),这对于一个可靠的协议来说不是一个优雅的终止方式。

为了防止这种情况出现,客户端必须等待足够长的时间,确保服务端能够收到 ACK,如果服务端没有收到 ACK,那么就会触发 TCP 重传机制,服务端会重新发送一个 FIN,这样一去一来刚好两个 MSL 的时间。

客户端在收到服务端重传的 FIN 报文时,TIME_WAIT 状态的等待时间,会重置回 2MSL

为什么需要 TIME_WAIT 状态? - 知乎主动发起关闭连接的一方,才会有 TIME-WAIT 状态。需要 TIME-WAIT 状态,主要是两个原因: 防止历史连接中的数据,被后面相同四元组的连接错误的接收;保证「被动关闭连接」的一方,能被正确的关闭; 原因一:防止…https://zhuanlan.zhihu/p/682680784


20240223

每个操作系统都附带一些内置的标准 C 库,这些库大约是 C 可执行文件“免费”获得的 30MB 代码,例如一个小的“Hello World”  C 可执行文件实际上无法打印任何内容,它只调用操作系统附带的 printf 。 Rust 不能指望操作系统内置 Rust 标准库,因此 Rust 可执行文件捆绑了自己的标准库(300KB 或更多)。幸运的是,这是一次性开销并且可以减少。对于嵌入式开发,可以关闭标准库,Rust 将生成“裸”代码

Speed of Rust vs C

MySQL 索引原理以及 SQL 优化-CSDN博客文章浏览阅读969次,点赞9次,收藏19次。【代码】MySQL 索引原理以及 SQL 优化。https://blog.csdn/weixin_44585214/article/details/136197000

SVG Tutorial - Learn how to code images in HTML with SVGLearn the fundamentals of Scalable Vector Graphics (SVG) from the basics up to advanced concepts like animation and interactivity.https://svg-tutorial/

音标练习

爱发音美式音标发音教学、英式音标发音教学https://fayin.love/

Web 终极拦截技巧(全是骚操作) | 风痕 · 術&思拦截的价值> 计算机科学领域的任何问题都可以通过增加一个中间层来解决。 —— Butler Lampson如果系统的控制权、代码完全被掌控,很容易添加中间层;现实情况我们往往无法控制系统的所有细节,所以需要使用一些 “非常规”(拦截) 手段来增加中间层。拦截的方法拦截/覆写 浏览器 API最常见的场景有通过拦截 console ...https://hughfenghen.github.io/posts/2023/12/23/web-spy/https://hughfenghen.github.io/WebAV/demohttps://hughfenghen.github.io/WebAV/demo

本文标签: 文档