上篇文章中我们简单介绍了分布式系统的设计思想以及简单性质,之后用一定篇幅简要介绍了MapReduce这个经典的分布式计算框架的大致工作原理,相信朋友们已经对此有了最基本的理解。在现实场景中,分布式系统的设计初衷是为了解决并发问题,能够承受单机系统所不能承受的流量负担,并充分利用计算机集群的硬件资源。同时,既然会利用到计算机集群,那么集群间通信也是一个不可忽略的讨论关键吧。由此,本文会着重讨论分布式系统中的并发问题以及通信问题。在介绍本文内容之前,需要提示朋友们:如果想要更好的理解本文中提到的知识点,需要有一定的操作系统和网络相关的知识,否则可能会略显吃力。

        说到设计分布式系统,那么相信一定有朋友们会联想到golang这门编程语言。golang是由Google公司与2007年开始设计,2009年正式发布开源的一门主要适配后端开发的编程语言。golang语言的设计初衷是在保证高性能的同时,提高程序员的开发效率,适合构建高并发、高可用的后端系统。相比于C/C++,golang的语法更加简洁清晰,删去了部分冗余特性,同时支持垃圾回收(GC)和手动内存优化,相比C/C++又不失太多性能。最重要的是golang有相对比较完善的内置并发机制。这也是golang语言在现代应用广泛的主要原因,常应用于云原生与微服务架构的项目开发,构建高并发的web服务,DevOps工具开发,网络爬虫,区块链,构建日志系统,数据处理等多个领域。由于篇幅有限,本文不再赘述go语言的基础语法。

一.golang与并发编程

(一)并发与并行

        相信学过操作系统相关知识的朋友一定对进程线程这两个名词不会陌生。这里如果让我去介绍进程线程,我可能真的想不起来那些长篇大论晦涩难懂的八股,背那些东西也挺没意思的,除了应付考试也没有什么用。我个人对于进程和线程的理解是:无论是进程还是线程,本质上都是程序运行的载体。进程拥有独立的内存地址空间,而线程则是进程中最小的执行单元。多个线程可共享同一个进程的内存地址空间,一个进程可以包含很多个线程。

        假如我们需要并发处理一个任务,那就有多进程多线程两种处理策略。可以把多进程策略联想为让不同的公司处理这个问题,每家公司都有独立的办公区域(内存空间),彼此互不干扰。可以把多线程策略联想为让同一家公司内部的不同员工处理这个问题,它们共享办公区域(内存空间),配合效率更高,但是也需要进行协调避免冲突。

        处理一个大型任务通常有两个最基本的优化思路。首先是并发I/O。我们可以通过上面提到的两种策略实现并发处理,而golang语言则内置了一种更加轻量级的用户态线程-goroutine(协程)来解决并发问题,允许不同的goroutine各自处理任务,我们后面会进行介绍。另外我们可以利用多核并行。现在的CPU大多数都拥有多个核心,我们完全可以利用多个CPU核心同时处理不同任务来提升任务的处理效率。在实际开发中,服务端开发者通常会结合使用上面这两种优化思路,在使用多进/线程提高并发量的同时,通过尽可能使用多核并行充分利用CPU中大量核心所产生的性能。

        这里可能会有一些不了解操作系统基本知识的朋友会疑惑,什么是并发,什么又是并行?这确实是初学者非常容易混淆的两个概念。并行是指多个任务在多个 CPU 核心上真正同时运行,彼此互不干扰。而并发则是多个任务在单个或多个核心上“交替执行”。操作系统通过时间片轮转的方式快速在任务之间切换,虽然任意时刻每个核心只执行一个任务,但由于切换足够快,在用户看来就像是这些任务同时进行一样。并发并不意味着真正的同时运行,而是一种在资源有限条件下的高效调度策略。总结一句话,并行关注的是“同时做多件事”,而并发关注的是“如何管理好多件事”。

        除此之外,还有另外一种处理并发的高效方法,即异步编程,也称事件驱动编程。它允许任务在等待某些操作完成期间,不阻塞当前线程而是挂起当前操作、继续执行其他任务。其核心机制是单线程+事件循环。在 系统层面,异步编程往往依赖于 I/O 多路复用机制 来实现非阻塞的 I/O。而且开销会比多进/线程小,规避了创建销毁进/线程,以及上下文切换的成本。不过缺点是无法充分利用CPU的多核并行能力,比较适合I/O密集型任务。所以在选择策略时,我们应重点关注当前任务属于计算密集型任务,还是I/O密集型任务

(二)go如何支持并发

        在前文的叙述中,我们有提到,golang内置了一种更加轻量级的线程-goroutine来解决并发问题。事实上,goroutine也只是对传统操作系统线程的一种“用户态封装”,由 Go 运行时通过 GMP 模型来进行调度和管理,并非通过操作系统内核直接调度。关于GMP模型我们会单独出文章来介绍,这是很多企业面试的常考题,我就有两三次都被问到过与传统线程相比,goroutine 创建和切换的成本非常低,占用资源极少,初始栈空间只有几 KB(传统操作系统线程大约是MB级别),可以轻松支持成千上万个并发任务。开发者只需使用go关键字即可启动一个新的并发任务,无需手动管理线程、锁或上下文切换。总的来说,goroutine 就是 Go 并发能力的核心。除此之外,go内置了一系列非常使用的并发原语,这些原语我们会在后面穿插介绍。

        处理并发问题主要有以下几大挑战。首先是如何处理共享数据。如果在一块内存区域中存在一个共享的数据对象,多个线程在同时读写共享数据,线程的执行顺序不确定,会造成数据的不一致,也就是会导致一系列并发安全相关的问题。同时,多线程也会引入资源竞争的问题。针对上述问题一个非常行之有效的方法便是引入同步机制,其中最常见的便是加互斥锁。其次,golang的一大设计哲学是“并非通过共享内存实现通信,而是通过通信实现共享内存”。由此,作为开发者,goroutine之间的交互通信需要引起格外重视。go内置了线程安全的channel数据结构来实现gouroutine之间的通信,同时内置Waitgroup原语协调多个goroutine的执行。最后,Go语言也内置了检测锁竞争或者死锁的工具,在编译时加上-race标志,可以检测数据竞争和一些潜在的锁问题,这里需要注意-race并不是一种静态检测机制,即源码层面的检查,而是检查当前程序的运行状态。如果上面介绍的这些专有名词没有理解,没关系,下面会给出具体的实例来帮助大家理解。

(三)多线程编程实例:简单网络爬虫

         相信朋友们应该对“爬虫”这个貌似挺火的词汇不陌生。网络爬虫是一种自动访问网页并提取内容的程序。该程序从网页URL开始,下载网页内容并提取网页中的链接,并不断重复以上过程,在数据的采集,分析,监控等方面有显著作用。我们往往不想重复抓取一个页面,这对于网络带宽是很大的浪费,所以我们会采用布隆过滤器进行去重操作。在本文中将介绍两种最常见的爬虫程序的实现思路。

        第一种思路是串行爬虫,我们在网络路径图中通过有效执行深度优先搜索(DFS),逐步访问页面,每抓取一个URL就启动一个goroutine,维护一个map记录已经爬取过的页面实现去重,避免重复抓取。思路很简单,示例代码如下:

func Serial(url string,fetcher Fetcher,fetched map[string]bool){//去重逻辑if fetched[url]{return}fetched[url]=trueurls,err := fetcher.Fetch(url)if err != nil{return}//深度优先搜索逻辑,递归访问页面for _, u :=range urls{Serial(u,fetcher,fetched)}return
}

        第二种思路是并行爬虫。并行爬虫有两种实现方式。第一种是通过共享数据对象以及加锁实现。在实现中我们使用了 sync.Mutex 来保护共享的 map[string]bool,用于记录已经抓取过的 URL。这里可能需要解释一下为什么要在for循环中使用闭包函数并传入u,因为 range 循环中的变量 u是被复用的,而 闭包默认捕获的是变量的引用地址,而不是值本身。这意味着当 goroutine 实际启动执行时,外层循环可能已经更新了 u 的值,导致捕获到的并不是我们期望的 URL。为了解决这个问题,我们在闭包函数中u 显式作为参数传入,确保每个 goroutine 拿到的都是对应循环当时的 u 值,从而保证抓取逻辑的正确性。而这段代码中也使用到了WaitGroup原语,可以把它看成一个计数器:每启动一个goroutine,执行Add(1),让计数器+1。当这个goroutine完成任务时,执行Done(),让计数器-1。主协程通过调用 Wait() 进入阻塞状态,直到所有 goroutine 执行完毕、计数器归零为止。在实际爬虫程序的设计中,我们往往还需要利用协程池来控制并发goroutine数量,防止资源耗尽。

        说起闭包函数,我会想到一个非常有意思的问题。如果一个闭包函数引用了其外围函数中的局部变量,而此时外围函数已经 return,那么这个变量会发生什么?起初我会担心:既然外围函数已经返回,里面定义的局部变量理应随着栈帧销毁,那闭包函数所引用的变量是否会“悬空”?是否会导致运行时错误?答案是不会,Go 的编译器在处理闭包时,会自动识别这种捕获了外部局部变量的情况,并进行“逃逸分析”。当编译器发现某个局部变量被闭包引用,并且闭包的生命周期可能超过当前函数时,它会将这个变量从栈上分配改为在堆上分配,以确保该变量的生命周期能撑到闭包函数结束。这样,无论外围函数何时 return,闭包中捕获的变量依然有效,直到最后一个引用它的函数也执行完毕,才会被垃圾回收(GC)清理掉。示例代码如下:

type fetchState struct{mu sync.Mutexfetched map[string]bool
}func ConcurrentMutex(url string,fetcher Fetcher,f *fetchState){f.mu.lock()already :=f.fetched[url]f.fetched[url]=truef.mu.Unlock()if already{return}urls,err := fetcher.Fetcher(url)if err !=nil{return}var wg sync.WaitGroupfor _,u :=range urls{wg.Add(1)//go闭包捕获引用,每个range重用了ugo func(u string){defer wg.Done()ConcurrentMutex(u,fetcher,f)}(u)}wg.Wait()return
}

        第二种是通过channel实现协程通信来实现,它遵循 Go 的设计哲学“不要通过共享内存来通信,而应该通过通信来共享内存”的理念。我们无需使用锁,从而避免了显式的并发控制复杂性。主线程master 维护抓取状态,但不共享对象。master中维护了一个map,与基于 mutex 的实现不同,worker 之间并不共享这个 map,而是由 master 单线程统一维护抓取状态,这样天然避免了并发冲突。master 与 worker 通过 channel 通信,所有的 URL 抓取任务都通过一个 chan []string 来传递,每一个 worker 负责抓取一个页面并将获取到的新 URL 列表通过 channel 发送回 master,由 master 决定是否继续抓取。在master中使用一个变量 n 来记录当前正在运行的 worker 数量,每创建一个新的 workern++;每处理完一轮从 channel 中读到的 URL 集合后,n--;n=0 时,说明所有任务已完成,master 主动退出循环。在调用 master之前,ConcurrentChannel会先将种子 URL写入 channel,我们把这个过程称为冷启动机制。在这个过程中,因为 Go 的 channel 默认是无缓冲的,写入操作是阻塞的,因此这一步必须放在 goroutine 中,防止阻塞主协程。每个 worker 会在一个goroutine中执行,通过 channel 接收 URL,抓取内容,并将抓取到的新 URL 发回 channel。这些worker之间完全独立,并不共享任何状态或对象,主从职责清晰。由于Go 的 channel 内部实现中使用了 mutex,因此它天然就是线程安全的,可以安全地在多个 goroutine 之间传递数据,而无需加锁。示例代码如下:

func worker(url string,ch chan []string,fetcher Fetcher){//实际的抓取逻辑urls,err:=fetcher.Fetch(url)//向channel中发送信息if err!=nil{ch<-[]string{}}else{ch<-urls}
}func master(ch chan []string,fetcher Fetcher){n:=1fetched:=make(map[string]bool)//从channel中获取一个URLfor urls:=range ch{//再获取这URL列表中的URLfor_,u:=range urls{//如果这个URL未被抓取,则启动一个新的worker线程去抓这个URLif fetched[u]==false{fetched[u]=truen+=1go worker(u,ch,fetcher)}}n-=1//爬虫完成了所有工作,已抓取完每一个URLif n==0{break}}
}func ConcurrentChannel(url string,fetcher Fetcher){ch:=make(chan []string)//将URL种子写入channelgo func(){ch<-[]string{url}}()master(ch,fetcher)
}

二.服务通信-RPC

(一)既生HTTP,何生RPC?

        在分布式系统中,各模块会部署在不同服务器节点上,此时不同节点之间的通信成为开发者必须考虑的问题。在网络通信实践中,相信朋友们一定对HTTP这个最常见的web应用层协议再熟悉不过了吧。作为标题党,可能会有懂行的朋友立刻指出:HTTP和RPC根本就不能这么对比,前者是协议,后者是设计思想。是这样没错,不过在本文中我还非要取这么个标题,没关系,咱们接着往下看~

        HTTP全称超文本传输协议。常用于万维网服务器本地浏览器之间的数据传输,是一个基于TCP的应用层协议。虽然HTTP是web世界的通用协议,但是在追求高性能,低延迟,强类型的分布式系统中,传统的HTTP+JSON的通信方式已经不能满足需求了。首先我要说清楚,这里说的HTTP指的是传统的HTTP/1.1+JSON/REST API模式。虽然也可以用这种方式进行服务调用,不过由于JSON是纯文本格式,体积太大,解析慢且占用带宽;且HTTP/1.1是单请求单连接的形式,无多路复用机制,大量并发请求易造成连接瓶颈和资源浪费,所以不适合服务间的高效通信。而且JSON是弱类型协议,前后端接口如果变动容易出问题,且RESTful API 只是一个风格,没有强制的规范和工具链,文档靠手写Swagger,代码全靠人维护,服务变动时容易出现客户端和服务端使用两套接口的问题。HTTP只支持客户端请求,服务端响应的单向模式,但是在分布式系统中,可能会需要客户端流服务端流双向流等多种通信方式。总结来讲,HTTP/1.1+JSON/REST API的设计模式只能说对浏览器友好,但是不等同于对服务友好。RPC并不是要替代HTTP,而是专为服务间高效通信而设计的一种更专业的方案。

        其实RPC设计思想的起源,甚至早于HTTP协议,最早可以追溯到上世纪70年代。RPC,即远程过程调用,其实是一种通信思想,既可以基于TCP,UDP等传输层协议,也可以基于HTTP等应用层协议,有很高的可定制性,比如说我们后面要介绍到的gRPC就是以HTTP/2作为底层传输协议实现的。RPC并不是协议,但是像Thrift,gRPC这种具体实现才算得上是协议的范畴。在微服务时代,RPC 提供了更强的性能、更高的类型安全、更好的自动化与服务治理能力,是服务间调用的专业工具。

        我们拿RPC的一种经典实现方式gRPC,在服务通信的场景下,与传统的HTTP/1.1进行性能对比。首先gRPC支持双向流服务端流等通信方式,支持多路复用,即在同一个TCP连接上互不干扰地并发处理多个请求,gRPC压缩请求头的大小,提高传输效率。其次,在序列化层面,gRPC使用的Protobuf(二进制序列化协议)相比纯文本的JSON要更加轻量,具有更快的序列化和反序列化的速度,由此节省更多的网络带宽,不容忽视的的是Protobuf支持强类型结构,并自动生成多语言代码,极大提升了开发效率与可靠性。而且,gRPC支持在服务端与客户端添加拦截器来实现鉴权token校验限流熔断追踪埋点等服务治理的手段,提升了系统的可用性。总结一句话,对人用,选 HTTP/REST,对服务用,选 RPC/gRPC。而实际上,gRPC 与 REST 并不冲突,二者可共存。

(二)gRPC与最佳实践

        

        gRPC是由Google公司研发的一款开源的,高性能的远程过程调用(RPC)框架。实际上,在我的理解来看gRPC既可以理解为框架,也可以理解为协议,所以大可不必为此感到困扰。它使用Protobuf作为序列化格式,同时基于HTTP/2设计,支持多种开发语言。在 gRPC 中,客户端应用程序可以直接调用另一台机器上的服务器应用程序的方法,就像调用本地对象一样。在服务器端,服务器实现此接口并运行 gRPC 服务器来处理客户端调用。在客户端,客户端有一个stub(存根)它提供与服务器相同的方法,gRPC 客户端和服务器可以在各种环境中运行并相互通信。这里我把gRPC的官方文档贴出来,朋友们可以参考学习:gRPC官方文档

        与许多 RPC 系统一样,gRPC 基于定义服务的思想,指定可以远程调用的方法及其参数和返回类型。默认情况下,gRPC 使用Protobuf作为接口定义语言 (IDL),用于描述服务接口和有效消息的结构。这里我节选出我自己项目中某个微服务的一段接口定义和消息结构定义(.proto文件)作为示范:

// 用户服务接口定义
service UserService {// 用户注册:输入注册信息,返回注册结果rpc RegisterUser (RegisterRequest) returns (RegisterResponse);// 用户登录:输入用户名密码,返回登录 tokenrpc LoginUser (LoginRequest) returns (LoginResponse);
}// 消息结构定义 
// 注册请求
message RegisterRequest {string username = 1;//用户名string password = 2;//密码string email = 3;//邮箱string phone = 4;//电话号码
}// 注册响应
message RegisterResponse {bool success=1;//是否成功string user_id = 2;//注册成功后分配的用户IDstring message = 3;//提示信息
}// 登录请求
message LoginRequest {string username = 1;//用户名string password = 2;//密码
}// 登录响应(返回JWT)
message LoginResponse {string token = 1;// 返回JWT token,用于鉴权string user_id = 2;//用户ID
}

        一旦写好了.proto文件,gRPC原生提供Protobuf编译器,我们可以执行相对应的命令,或者编写脚本,自动生成客户端和服务端代码。一般来讲客户端用来调用这些定义好的API,而服务端则实现这些API。这些部分便是一个后端项目真正意义上的业务代码了,所以我暂时不进行展示。

        在真正的项目开发过程中,开发者们总结出了一套较为系统的 gRPC 最佳实践方法。从接口的设计规范、编译脚本的自动化、到中间件的扩展能力、安全认证、负载均衡再到多语言协同开发,gRPC 已经从最初的高性能通信框架演变为一个现代微服务体系的通信骨干工具。在实际开发过程中,我们往往会引入拦截器提升服务治理能力,使用中间件进行错误处理以及重试。我们还经常使用Etcd/Consul等集群管理工具实现服务注册自动发现,在请求量大时启用连接池充分复用节省资源等等。无论是构建小型微服务系统,还是支撑复杂的大规模分布式架构,gRPC 都是一个值得考虑的方案。它不只是“比 HTTP 快”,更是更现代、更自动化、更易维护的服务通信解决方案。

        以上就是我对并发编程和RPC的粗浅理解,如有不当恳请批评指正,我们一起成长!

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

opensuse/debian grub启动界面太模糊?

现代操作系统或者新电脑使用那么模糊的界面启动&#xff0c;虽然没有什么不良反应&#xff0c;但是多少有点看不过去&#xff0c;这是因为为了保证正常启动做出的适配。而我们可以对其分辨率进行选定。 1 您好&#xff0c;非常感谢您提供的截图。这张图片非常关键&#xff0c…

zookeeper Curator(5):集群架构和集群搭建

文章目录 一、集群架构&#xff1a;Leader-Follower 模式二、核心机制&#xff1a;ZAB 协议三、Leader 选举机制四、集群部署要点五、优势与挑战 Zookeeper 集群是一个由多个 Zookeeper 服务实例组成的分布式协调服务系统&#xff0c; 通过奇数个节点&#xff08;通常 3、5、7…

道可云人工智能每日资讯|浦东启动人工智能创新应用竞赛

道可云人工智能&元宇宙每日简报&#xff08;2025年7月1日&#xff09;讯&#xff0c;今日人工智能&元宇宙新鲜事有&#xff1a; 江城模境工信部人工智能大模型公共服务平台&#xff08;武汉&#xff09;上线运行 2025年6月27日&#xff0c;光谷人工智能创新大会在湖北…

Python元组的遍历

一、前言 在 Python 中&#xff0c;元组&#xff08;tuple&#xff09; 是一种非常基础且常用的数据结构&#xff0c;它与列表类似&#xff0c;都是有序的序列&#xff0c;但不同的是&#xff0c;元组是不可变的&#xff08;immutable&#xff09;&#xff0c;一旦创建就不能修…

矩阵的条件数(Condition Number of a Matrix)

文章目录 矩阵的条件数&#xff08;Condition Number of a Matrix&#xff09;&#x1f4cc; 定义&#x1f9ee; 常见形式&#xff1a;2-范数下的条件数&#x1f50d; 条件数的意义&#x1f9e0; 实际意义举例&#x1f4bb; Python 示例&#xff08;NumPy&#xff09;&#x1f…

1 Studying《Computer Architecture A Quantitative Approach》1-4

目录 Preface 1 Fundamentals of Quantitative Design and Analysis 1.1 Introduction 1.2 Classes of Computers 1.3 Defining Computer Architecture 1.4 Trends in Technology 1.5 Trends in Power and Energy in Integrated Circuits 1.6 Trends in Cost 1.7 Depe…

Reactor Hot Versus Cold

这段文字详细解释了 Reactor 中 热发布者&#xff08;Hot Publisher&#xff09; 和 冷发布者&#xff08;Cold Publisher&#xff09; 的区别&#xff0c;并通过示例展示了它们的行为差异。以下是对其含义的总结和解释&#xff1a; 1. 冷发布者&#xff08;Cold Publisher&…

OpenCV CUDA模块设备层-----逐通道最小值比较函数min()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 OpenCV 的CUDA并行计算模块&#xff08;cv::cudev&#xff09;中的一个设备端内联函数&#xff0c;用于在CUDA核函数中对两个uchar2类型像素值进…

proteus实现stm32按键控制LED灯流水灯方向

一、新建工程 1、工程命名 2、选择工程存储位置 3、默认下一步 4、默认下一步 5、选择没有固件项目&#xff0c;下一步 二、器件放置并连线 1、点击左边工具栏中运放的形状的符号 2、再点击‘P’&#xff0c;搜索器件 3、搜索器件并放置连线 按键控制LED需要的器件有&#…

华为云Flexus+DeepSeek征文 | 让运维更智能:Chaterm AI终端工具与华为云ModelArts Studio深度集成指南

华为云FlexusDeepSeek征文 | 让运维更智能&#xff1a;Chaterm AI终端工具与华为云ModelArts Studio深度集成指南 引言一、ModelArts Studio平台介绍华为云ModelArts Studio简介ModelArts Studio主要特点 二、Chaterm介绍Chaterm简介Chaterm主要特点 三、安装Chaterm工具下载C…

湖北理元理律师事务所债务解法:从法律技术到生活重建

数据透视&#xff1a; 2023年武汉法院受理债务纠纷案11.4万件&#xff0c;其中37%因不当还款规划导致债务雪球效应。 一、债务危机的法律归因 通过分析1200例债务咨询案例&#xff0c;发现三大共性法律认知盲区&#xff1a; 担保责任误判 某企业主为朋友担保200万&#xff0…

小程序学习笔记:加载效果、上拉加载与节流处理

在微信小程序开发过程中&#xff0c;优化用户体验是非常重要的一环。今天我们就来分享如何在小程序中实现加载提示效果、上拉触底加载下一页数据以及对上拉触底事件进行节流处理&#xff0c;让你的小程序更加流畅和高效。 一、添加 loading 提示效果 在小程序中&#xff0c;当…

计算机网络:【socket】【UDP】【地址转换函数】【TCP】

一.socket 1.1socket接口 它返回的是一个文件描述符。创建socket文件描述符(TCP/UDP,客户端服务器) • socket()打开一个网络通讯端口,如果成功的话,就像 open()一样返回一个文件描 述符; • 应用程序可以像读写文件一样用 read/write 在网络上收发数据; • 如果 socket()调用…

机器人轨迹跟踪控制与动力学模型详解

1. 机器人控制的本质&#xff1a;通过关节扭矩执行轨迹 机器人控制的核心目标是让机器人关节精确跟踪期望轨迹 ( q d , q ˙ d , q d ) (q_d, \dot{q}_d, \ddot{q}_d) (qd​,q˙​d​,q​d​)。为此&#xff0c;控制器需根据当前状态 ( q , q ˙ ) (q, \dot{q}) (q,q˙​)计…

智能办公与科研革命:ChatGPT+DeepSeek大模型在论文撰写、数据分析与AI建模中的实践指南

随着人工智能技术的快速发展&#xff0c;大语言模型如ChatGPT和DeepSeek在科研领域的应用正在为科研人员提供强大的支持。这些模型通过深度学习和大规模语料库训练&#xff0c;能够帮助科研人员高效地筛选文献、生成论文内容、进行数据分析和优化机器学习模型。 ChatGPT和Deep…

运营商场景下的实时脱敏方案:PB 级日志流的分布式处理架构

在数字化浪潮中&#xff0c;运营商积累了海量数据&#xff0c;涵盖用户信息、通信记录、业务运营数据等。这些数据不仅是运营商业务运营的关键资产&#xff0c;也是创新服务、精准营销的核心驱动力。然而&#xff0c;随着数据量呈指数级增长&#xff0c;运营商每日需处理 PB 级…

docker+n8n的工作流中无法使用本地ollama服务的问题

使用docker创建n8n服务后&#xff0c;工作流中不想用大模型付费API测试&#xff0c;想用本地大模型来跑&#xff0c;刚好电脑上装了ollama&#xff0c;就试了下添加ollama节点来替代大模型付费API&#xff0c;结果就遇到了以下问题 ollama正常运行中 但是工作流会卡在这&…

通过交互式可视化探索波动方程-AI云计算数值分析和代码验证

波动方程是一个基本的数学模型&#xff0c;它描述了各种类型的波&#xff08;包括机械波、声波、电磁波和流体波&#xff09;如何通过不同的介质传播&#xff0c;这使得它对于物理学、工程学和其他科学学科中声学、光学、医学成像和电信等领域的预测和设计都至关重要。 波动方程…

10授权

目录 本节大纲 一、权限管理 1. 认证 2. 授权 二、授权核心概念 三、权限管理策略 1. 基于 URL 权限管理 权限表达式 2. 基于 方法 权限管理 EnableGlobalMethodSecurity 四、基本用法 五、原理分析 六、实战 1. 简介 2. 库表设计 3. 创建 springboot 应用 本节…

线性规划模型

线性规划算是数学建模中最基础的模型了&#xff0c;其典型特征就是线性和有限资源&#xff0c;即在一组线性约束条件下&#xff0c;求解一个线性目标函数的最大值或最小值问题&#xff1a; 其中x 是决策变量向量&#xff0c;c 是目标函数系数向量&#xff0c;a 和 b 分别是约束…