详解微服务技术中进程间通信

2023-05-16

640?wx_fmt=jpeg

640?wx_fmt=gif&wxfrom=5&wx_lazy=1

在单体应用中,一个组件调用其它组组件时,是通过语言级的方法或者函数调用,而一个基于微服务的应用是运行于多个服务器上的分布式系统,每个服务实例是一个典型的进程。所以,如下图显示的,服务必须通过内部进程交互机制(IPC)进行交互。

640?wx_fmt=png

交互风格

在为一个服务选择IPC的时候,首先考虑一下这些服务是如何交互的是很有用处的。有多种client/server的交互风格,它们可以通过两个维度分类,第一种维度是交互是一对一,还是一对多的:

  • 一对一:每个客户端的请求只被一个服务实例处理

  • 一对多:每个客户端请求被多个服务实例处理

  • 第二种维度是交互是同步的还是异步的:

  • 同步:客户端期望从服务得到及时的返回,并且甚至可以因此阻塞片刻

  • 异步:客户端不会在等待返回结果的时候阻塞,返回结果也没必要立刻被发送出来

下表显示出各种交互风格:

 

一对一

一对多

同步

请求/响应

 

异步

通知

发布/订阅

请求/异步响应

发布/异步响应

有如下几种一对一的交互形式:

请求/响应:客户端发送一个请求给一个服务,并且等待响应结果,客户端期望结果能快速的返回,在一个基于线程的应用中,发送请求的线程甚至可能在等待的时候被阻塞。

  • 通知(一种单向请求):客户端发送一个请求到服务,但不期望有响应发送回来。

  • 请求/异步响应:客户端往服务发送请求,响应结果异步的返回。客户端不会在等待的时候阻塞,而且客户端是基于响应在一段时间之后才返回的假设来设计的。

有如下几种一对多的交互形式:

  • 发布/订阅:客户端发布消息,消息被零或者多个感兴趣的服务消费

  • 发布/异步响应:客户端发布一个请求消息,等待固定的一段时间,以获得从感兴趣的服务返回的响应结果

每个服务一般都使用这几种交互风格的组合风格。对于一些服务来说,单一的IPC机制就足够了,而其它的服务可能需要组合使用若干种IPC机制。下面的图给出当客户请求行程时,在一个打车应用可能出现的一些交互。

640?wx_fmt=png

这些服务使用了通知,请求/响应,发布/订阅的交互方式。比如说,一个乘客的智能手机向行程管理服务发送了一个上车请求,行程管理服务通过请求/响应方式向乘客服务确认乘客的账户是否是活跃账户,行程管理服务于是创建一个行程订单,并且用发布/订阅方式通知其它的服务,包括一个分发服务,用以定位空闲的司机。

定义API

服务的API是服务与它所有的客户端之间的一种契约,不管选用何种IPC机制,使用一些接口定义语言(IDL),对于精确定义服务API是很重要的,甚至已经有一些关于使用API-first approach来定义服务的好的讨论。

开发一个微服务从书写接口定义以及与客户端开发人员一起review这些接口定义开始,在不断的对这些API定义进行迭代,最终才算是实现了一个微服务。这种基于前端的设计方式,增加了构建出符合客户端需求的机会。

 

在文章的后面你会看到,API定义的特性依赖与你使用的IPC机制,如果你使用消息机制,API就会涉及到消息通道和消息类型;如果你选用HTTP方式,那么API就会包含一些URL和请求/响应的格式,之后我们会详细的介绍IDL。

 

API的演进

一个服务的API会随着时间而经常变化。在单体应用中,通常是很直接的修改API,再更新所有的调用之处,但在基于微服务的应用中,情况要困难得多,甚至你API的所有消费者是同一个应用中的其它服务。你通常不能强迫所有的客户端步调一致的升级它们的服务。而且你可能会大量的开发服务的新版本,于是新旧版本的服务会同时运行,制定一个处理这种问题的战略原则显得很重要。

 

如何处理一个API的变化,取决于这种变化的多少。有的变化很少,可以向后兼容之前的版本,比如,你可能只是在请求或者响应格式中增加一些属性。设计出具有鲁棒原则的客户端和服务是有意义的,那些使用更旧的API的客户端应该能够继续和新版本的服务工作得很好,服务会给请求中没有的属性提供默认值,客户端会忽略那些响应中额外的属性。使用IPC机制和消息格式是重要的,让你能轻易的演进API。

 

有时候,你不得不对API做一些主要的、不兼容的改动。既然不能强制客户端立刻升级,那这个服务必须能够支持旧版本的API一定时期。如果你用的是基于HTTP的机制,如REST,一个好的办法是在API的URL中嵌入版本号。每个服务实例应该可以同时处理不同版本的API请求,或者是部署不同的服务实例来处理不同的API版本。

 

处理部分失败

在之前关于API网关的文章中曾经提到,在分布式系统中,总会存在部分失败的风险,既然客户端和服务是分开的进程,一个服务可能不能对一个客户端请求及时的返回结果,服务也可能因为错误或者是维护停止了,亦或是因为过载而对请求响应缓慢。

 

比如说,如上篇文章中提到的那个产品详页的场景,试想一下如果那个推荐服务失去响应了,客户端的一个本地实现就可能在无限的等待响应中被阻塞了,这不仅会带来劣质的用户体验,而且在很多应用中,这会消耗宝贵的资源,如一个线程,最终运行时环境会线程耗尽,变成无法响应,正如下图所示。

640?wx_fmt=png

为了避免这种问题,把你的服务设计成能处理部分失败是很有必要的。

 

Netfix给我们提出了一个可以遵循的好办法,其中处理部分失败的原则包括:

  • 网络超时:永远不要无限的阻塞,总是在等待响应中使用超时,使用超时来确保资源不会被无限绑定。

  • 限制未解决的请求数量:对一个客户端持有的对一个服务没有完成的请求,应该设定上限值,这个上限一旦达到,发送更多的请求就会是无意义的,而且这些新的请求需要立刻返回为失败。

  • 回路中断器模式:跟踪成功请求和失败请求的数量,如果错误率超过了一个事先配置的阈值就开启回路中断器,让进一步的尝试立刻失败。如果大量的请求正处在失败中,那就预示服务不可用,而且发送请求也是无意义的。经过超时周期之后,客户端应该再进行尝试发送请求,如果请求成功,就关闭回路中短器。

  • 提供回滚机制:一个请求失败时,执行回滚逻辑,比如说返回缓存的数据或者是默认值,也或者诸如一个关于推荐商品的空集合。

Netfix Hystrix是这些模式的一种开源实现,如果你正在使用JVM,你肯定会考虑使用Hystrix的,如果你运行的是一个非JVM的环境,同样需要考虑使用一个类似的库。

IPC技术

有许多IPC技术可供选择,如同步的请求/响应机制,这里面有基于HTTP方式的REST和Thrift,另外有基于消息的异步通信机制,如AMQP和STOMP。其中消息的格式也是多种多样的,有一些是人可读的,比如JSON和XML,有些是二进制格式的(这种更高效),如Avro和缓存协议。稍后我们介绍同步的IPC机制,但在这之前,先讨论异步的IPC机制。

异步(基于消息的通信)

当使用消息时,进程间通过异步的交换消息来通信。客户端通过向服务发送消息来发送请求,如果期望服务返回应答,那么它发送回一个独立的消息给客户端。由于通信是异步的,客户端不会阻塞在等待返回结果上,客户端应该是基于不会立刻收到返回结果的假设来实现。

 

消息包含消息头(如发送者这样的元数据)和消息体,各种消息在通道上交换,任意数量的生产者都能往通道上发送消息,同样,任意数量的消费者也能从这个通道接收消息。有两种类型的通道:点对点通道和发布/订阅通道。点对点的通道只给连接到这个通道上的众多消费者中的一个发送消息,服务使用这种通道往往是采用前面提到的一对一的交互风格。发布/订阅这种通道,是给连接到它之上的所有消费者发送消息,这种通道往往被一对多风格的服务采用。

下图描述的是,在打车应用中,发布/订阅的通道是如何使用的

640?wx_fmt=png

 

行程管理服务向发布/订阅通道发送一个行程创建的消息,以此告诉那些对此感兴趣的服务(比如说分发器服务),一个新行程创建了。分发器服务找到一个可用的司机,将一个需要提名司机的消息写入发布/订阅通道,这样其它的服务就能得到这个通知。

 

有许多消息系统可供选择,你应该选择那些能支持多种开发语言的。一些消息系统支持AMQP和STOMP这些标准协议,其它的系统是一些专有而且文档化的协议。现在有不少开源的消息系统,其中包括RabbitMQ,Apache Kafka,Apache ActiveMQ和NSQ。总体上看,他们都支持消息格式和通道,都是可靠的、高性能的和可扩展的,但它们在消息模型细节方面有着巨大的差异。

 

使用消息有诸多优点:

  • 把客户端从服务中解耦出来:客户端只需要简单的往正确的通道里发消息,它完全不用感知服务实例,它不需要通过发现机制来定位服务实例所在的位置。

  • 消息缓冲:在使用HTTP这种同步的请求/响应协议时,客户端和服务都必须在交换数据的时候保持可用。与此相反,消息代理会将写到通道里面的消息队列化,直到消费者能够处理这些消息。这意味着,比如,对订单的消息进行简单的队列化之后,即使是订单填写系统响应缓慢或者不可用,一个在线商店仍然可以接收到来自客户的订单。

  • 灵活的客户——服务交互:消息机制支持之前提到的所有交互风格。

  • 显式的进程间通信:基于RPC的机制能够让调用远端的服务看起来如同调用本地服务,但由于存在物理规则和部分失败的可能,这些机制都有较大不同。消息机制让这些不同之处变得很显式,这样程序员不用陷于安全失误当中。

 

当然,消息机制也有缺点:

  • 额外的操作复杂性:消息系统是另外一个系统,必须安装,配置和操作,消息代理必须高可用,要不然整个个系统的可靠性将受到影响。

  • 实现基于请求/响应的交互比较复杂:请求/响应风格的交互要求一些实现上的工作,每个请求消息必须包含一个应答通道ID和关联ID,服务将相关ID包含在响应的消息中,并发送到响应通道,客户端就通过这个相关ID来将响应和请求匹配起来。使用IPC机制来直接支持请求/响应通常简单一些。

现在我们已经讨论完了基于消息的IPC,接下来探讨一下基于请求/响应的IPC

同步的请求/响应IPC

在同步的、基于请求/响应的IPC机制中,客户端向服务发送一个请求,服务处理这个请求,并将响应发回。在许多客户端的实现中,发送请求的线程会在等待响应的时候阻塞。

而另一些客户端的实现,可能使用异步的、事件驱动的方式,请求相关的代码会被封装在Futrues或者Rx Observables这样的库中。和前面介绍的消息机制不同,在这种IPC里客户端是假设响应会及时返回。有很多协议可供选择,其中有两种很流行:REST和Thrift。我们先来看看REST

REST

目前,使用RESTful风格来开发API是很流行的做法,REST是使用HTTP的IPC机制,REST的一个关键概念是资源,资源代表一个业务对象,比如说一个客户,一个产品,或者是一些业务对象的集合。REST使用HTTP的方法来操作资源,通过URL来引用资源。比如,GET请求会返回一个资源的信息,返回结果用XML文档或者JSON对象来表示,POST请求创建一个资源,PUT请求是更新一个资源。REST的创建者Roy Fielding的描述如下:

 

“REST提供一个架构约束的集合,当被整体应用时,强调组件交互的扩展性、接口的普遍性,组件的独立部署,减少交互延时的中间组件,增强的安全性以及对遗留系统的封装。”

 

下图展示了打车应用中使用REST的一个场景。

640?wx_fmt=png

乘客的智能手机向行程管理服务发送创建行程的请求,这个时候一个POST请求发送到服务端,请求创建一个/trips资源,行程管理服务随后发送一个GET请求到乘客管理服务,来获取乘客的信息,在确认了这个乘客是一个授权过可以创建行程的用户之后,行程管理服务正式的创建出行程,并且返回一个201结果给智能手机。

 

很多开发者都声称他们的HTTP API都是RESTful的,但如Fielding在他的这篇博客里描述的,其实他们不一定都是。Leonard Richardson给出了一个很有用的REST成熟度模型,包含如下一些级别:

  • 级别0:客户端通过发送基于HTTP的POST请求到唯一的URL服务端,每个请求指定要执行的动作,动作的对象(比如业务对象),以及其它任何参数。

  • 级别1:支持资源的概念,为了在一个资源上执行动作,客户端需要在POST请求中指定执行的动作和所有的参数。

  • 级别2:API使用HTTP的动词来执行动作:GET用来获取,POST用来创建,PUT用来修改。请求要求参数和请求体,如果有,还需要指定动作的参数,这样服务就可以利用页面系统的一些基础设施,如缓存GET请求。

  • 级别3:这个级别的API是基于HATEOAS(超文本应用状态引擎)原则的,基本思想是在GET请求返回的代表资源的响应中,需要包含一些链接,这些链接对应与可对这个资源执行的动作。举个例子,订单的GET请求的返回结果中会包含操作的链接,其中有取消订单的操作链接,客户端可以从结果中找到这个链接,使用它取消订单。

  • HATEOAS的优势在于不再需要将URL硬编码到客户端的代码里面去了,另一个好处是由于资源的返回结果中已经包含允许的操作的链接,客户端不用去猜测当前状态下能对资源做哪些操作了。

 

使用基于HTTP的协议的好处有:

  • HTTP对与大家来说简单而熟悉。

  • 可以用一些有Postman这种插件的浏览器来测试API,也可以用curl这种命令行工具来测试(返回结果是用JSON或者其它类型的文本格式)

  • 直接支持请求/响应风格的通信

  • HTTP是防火墙友好的

  • 不需要有中间代理,这让系统的架构得到简化

 

使用HTTP也有缺点:

  • 只支持请求/响应的交互风格,这使得在使用HTTP来发送通知的时候,服务端必须总是发送HTTP响应回来。

  • 因为客户端和服务端直接通信(中间没有缓冲消息),他们在交换信息期间必须同时处于运行状态。

  • 客户端必须知道每个服务实例的地址(比如URL),正如在上一篇文章中描述的,在现代应用中,这倒不是个重要的问题,一般客户端都需要使用服务发现机制来定位服务实例的位置。

 

开发者社区最近发现了接口定义语言对RESTful API的新价值,这方面有一些选择,包括RAML和Swagger。一些诸如Swagger的IDL允许定义出请求和响应消息的格式,其它一些诸如RAML的IDL则要求使用独立的规范,如JSON schema。在描述API的同时,IDL一般也有工具来给接口定义生成客户端桩和服务端骨架。

Thrift

Apache Thrift是REST的一种有趣的替代方案,它是开发跨语言RPC客户端和服务端的框架,Thrift提供C语言风格的IDL来定义你的API,使用Thrift编译器生成客户桩和服务骨架,编译器能够生成各种语言的代码,包括C++,Java,Python,PHP,Ruby,Erlang和Node.js。

 

一个Thrift接口包含一个或多个服务,定义服务与定义Java接口类似,是一些强输入方法的集合,Thrift方法可以定义城返回一个值(也可能是void的),或者定义成单向方法。返回一个值的方法都会实现请求/响应的交互风格。客户端等待请求,并且有可能抛出异常。单向方法其实是符合通知风格的交互,服务端不会发送响应。

 

Thrift支持多钟消息格式:JSON,二进制,紧凑的二进制。二进制格式通常比JSON更高效一些,因为解析它更快。对于紧凑二进制格式,如它的名字一样,它是节省空间的消息。而JSON,当然是对人和浏览器友好的一种格式。在Thrift中,也可以自己选择传输协议,其中包括原始TCP和HTTP。TCP一般比HTTP更高效一些,当然,HTTP是对防火墙、浏览器和人友好的。

消息格式

前面已经讨论过HTTP和Thrift,现在介绍消息格式的问题。如果使用消息系统或者REST,需要确定消息格式。其它一些如Thrift这种IPC机制只支持有限的集中消息格式,或许就一种而已。在任何一种情况中,使用跨语言的消息格式是很重要的。甚至你现在只是用一种语言来实现你的微服务,很可能你将来会使用其它的语言。

 

有两种主要的消息格式:文本和二进制码。基于文本的格式有JSON,XML这些。它们的优点在于是人可读的,而且是自描述的。在JSON中,对象的属性被表示成名称-值对的集合。类似的,在XML中,属性被表示成名字元素和值。这可以让消息消费者能够找到感兴趣的值,同时忽略其它的。而且,对格式的小量改动可以容易的兼顾到后向兼容性。

 

XML文档的结构是在XML schema文件中定义的,渐渐的社区的开发者意识到JSON也需要类似的机制,其中一个解决办法是使用JSON schema,以独立方式存在或者是如Swagger这种IDL的一部分。

 

基于消息的格式的一个缺点是比较繁琐,尤其是XML。因为消息是自描述的,除了包含属性的值之外,消息里还包含属性的名称。另外一个劣势是,解析消息文本需要开销。基于这些,你可能更想使用二进制码格式

 

有几种二进制格式可供选择。当用Thrift RPC,你可以选择二进制的Thrift。如果使用消息格式,比较流行的选择是Protocol Buffers和Apache Avro。这两种格式都提供输入的IDL来定义消息结构。不同之处在于,Protocol Buffers使用标签域,而Avro,它的消费者在翻译消息前,需要提前知道消息的schema。这篇博客完美解释了Thrift, Protocol Buffers和Avro的异同之处。

总结

微服务必须使用一种进程间通信机制,当设计你的服务如何通信时,需要考虑各种问题:服务如何交互,如何为每个服务设计API,如何演进API,以及如何处理部分失败问题。有两种微服务可用的IPC机制,异步的消息机制和同步的请求/响应机制。在这一系列文章的下一篇文章中,我们会研究在微服务架构中的服务发现问题。

 

英文原文:

https://www.nginx.com/blog/building-microservices-inter-process-communication/

更多架构师技术关知识请参考“架构师技术全店资料打包汇总(全)”电子书(32本技术资料打包汇总、详解目录和内容请通过“阅读原文”获取)。

温馨提示:

请识别二维码关注公众号,点击原文链接获取架构师技术全店资料打包汇总(全)”电子书资料详情。

640?wx_fmt=jpeg

640?wx_fmt=gif&wxfrom=5&wx_lazy=1

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

详解微服务技术中进程间通信 的相关文章

  • 判断一棵二叉树是否是平衡二叉树

    我们先来整理一下什么是平衡二叉树 xff1f 满足以下两点的就是平衡二叉树 xff1a 1 左右子树的高度差不能超过1 2 左右子树也是平衡二叉树 需要注意的是空树也是平衡二叉树 例如下面这棵树就不是平衡二叉树 因为对于B来说左右子树高度超
  • Fiddler实现手机抓包入门配置

    emmm 在此之前我也只是听过抓包抓包 xff0c 但是没有真正的接触到 xff0c Fiddler是一款很强大的抓包工具了 xff0c 但是一开始很多人可能会想我一样走上歧途 xff0c 所以在此总结一下 xff0c 希望看到的朋友少走弯
  • Idea使用Maven连接MySQL数据库

    连接MySQL数据库的步骤 xff1a 1 添加数据库 2 填写连接信息如图 3 填写连接信息完之后 xff0c 需要记一下jar包的位置 xff0c 用于添加jar包 4 显示jar包信息 5 添加文件夹命名java xff0c 并将其设
  • mybatis generator 生成中文字段注释

    首先 xff0c 试了好几次 xff0c 重写 DefaultCommentGenerator xff0c 创建MyCommentGenerator类实现CommentGenerator接口 xff0c 都是按照网上的方案来的 xff0c
  • org/apache/velocity/context/Context

    mybatisplus generator AutoGenerator 生成文件时报错如下 xff1a 请输入表名 xff1a alarm 14 38 28 569 main DEBUG com baomidou mybatisplus g
  • NVIDIA开源NeMo,CUDA,pytorch安装使用过程踩坑总结

    说明 Nemo cpu版本直接看文章最后一部分 xff08 linux版本的 xff09 xff0c windows下安装的话要先安装CUDA来支持GPU xff0c 在安装pytorch 在安装nemo 但是还是出问题了 xff0c 不建
  • 网络url转文件

    首先是获取springboot resource下面的文件 String fileUrl 61 ResourceUtils getURL 34 classpath 34 getPath 43 34 test txt 34 File file
  • ByteBuffer和String互转

    Charset charset 61 StandardCharsets UTF 8 String str 61 34 kj你好呀 kjlkjlkjlk 34 ByteBuffer buffer 61 ByteBuffer allocate
  • @Test单元测试注入bean 3种方式

    1 64 ContextConfiguration 64 ContextConfiguration Spring整合JUnit4测试时 xff0c 使用注解引入多个配置文件 64 RunWith SpringJUnit4ClassRunne
  • java 导出word

    目前来看 xff0c java导出word大致有6种解决方案 xff1a 1 xff1a Jacob是Java COM Bridge的缩写 xff0c 使用Jacob自带的DLL动态链接库 xff0c 并通过JNI的方式实现了在Java平台
  • JAVA visualVM 查看堆栈分配

    由于在做nlp xff0c 用到standFord nlp xff0c 堆栈分配总是一处 xff0c 所以需要这个 xff0c 正好学习一下 我用的是jdk8 5 xff0c 位于 JDK 根目录的 bin 文件夹下 xff0c 无需安装
  • Android Studio Gradle失败 Could not reolve play-services-vision-17.0.2.aar 等类似问题解决办法

    Android Studio Gradle失败 Could not reolve play services vision 17 0 2 aar 等类似问题解决办法 网上一些常规解决办法 如果上述常规解决办法尝试后 xff0c 都无法解决你
  • 利用fsl进行配准

    利用fsl进行配准 配准概念 配准就是将两个不同空间 体素 xff0c 扫描的位置不一致的nii xff0c 配准到同一个空间上 xff0c 使得两者在大脑上的相应位置就可以一一对应上了 通常MRI数据处理的步骤 xff1a 先配准到tem
  • 1.计算机概论

    学习linux前先来了解一下计算机概念 xff0c 如果了解相关内容 xff0c 可跳过本章节 1 1 电脑 电脑是一种计算机 xff0c 计算机实际是 xff1a 接受用户输入的命令与数据 xff0c 经由中央处理器的算术和逻辑单元运算后
  • SpringDataJPA——使用EntityManager利用原生SQL自定义复杂查询

    使用EntityManager 原生SQL查询方法记录以下学习过程中找到的其他文章地址 原生SQL查询方法 在这里进行记录以下使用过程 xff0c 注释已经很清晰 span class token annotation punctuatio
  • 操作系统(二十三)生产者消费者问题

    2 3 6 生产者消费者问题 生产者消费者问题 The proceducer consumer problem 是一个经典的进程同步的问题 xff0c 问题是这样描述的 xff1a 在操作系统中有一组生产者进程一组消费者进程 xff0c 生
  • Powershell脚本:一键优化windows 10(原版)

    本套Powershell脚本出自github开源项目 xff0c 包含原版WIN10系统大概300个一键优化 组件精简方案 例如彻底关闭Windows defender xff0c 关闭共享 打印机 xff0c 保留Windows upda
  • 安装ubuntu与windows双系统

    ubuntu程序的安装 开机进bios xff0c 在Security页面 xff0c 关掉secure boot xff1a 存储系统文件 xff0c 建议10GB 15GB xff1b swap xff1a 交换分区 xff0c 即Li
  • Windows编程经典书籍

    本人是刚刚开始学习windows编程的 感觉看雪学院的大牛很NB 想找一些书籍来看学习学习 可是不知道看哪些书好 驱动 对菜鸟们来说真是一个很深奥的话题 所以 我找来了这篇文章供大家分享 以后大家发现什么好书就在楼下跟贴吧 作者 xff1a

随机推荐

  • 经典Windows编程书单

    说好的这次写一个图形编程书单 但是看起来不是很好整理 xff0c 这类书散落的家里到处都是 先把经典Windows编程的书整理一下吧 xff0c 不过Windows的也到处都是很多都找不到了 xff0c 只能把找到的拍个照 xff0c 可能
  • ubuntu18.04开机循环输入密码无法进入桌面

    问题 xff1a 在profile和environment文件里配置了java环境变量后 xff0c 重启电脑后即使输入正确的用户名和密码 xff0c 也会重新跳到登录界面 xff0c 无法进入系统 xff0c 一直循环登录 原因 xff1
  • ubuntu 安装VS

    Table of Contents 一 前言 二 安装过程 1 下载VS Code 2 安装过程 3 下载C 43 43 模块 4 汉化 5 常用快捷键 一 前言 因为要用到在ubuntu系统中使用VS Code 来编写C 43 43 代码
  • Windows系统FTP服务器设置

    设置操作步骤 步骤一 xff1a 确认电脑是否开通联网共享服务 依次点击 控制面板 程序 启用或关闭Windows功能 按钮 xff0c 进入到 Windows功能 页面 xff0c 查看 Internet Information Serv
  • springboot thymeleaf 配置

    Springboot默认是不支持JSP的 xff0c 默认使用thymeleaf模板引擎 1 在application properties文件中增加Thymeleaf模板的配置 thymelea模板配置 spring thymeleaf
  • 【ubuntu】fatal: detected dubious ownership in repository at ...

    在ubuntu使用git的时候遇到了以下错误 xff1a fatal detected dubious ownership in repository at 39 home xxx 39 To add an exception for th
  • 有意思的Windows脚本(1)

    有意思的Windows脚本 1 因为不知道今天的博客写什么啦 xff0c 就放几个好玩的Windows脚本的源码吧 xff0c 大家千万不要干坏事情哦 xff0c 嘿嘿 1 vbs循环 xff08 桌面上建一个记事本 xff0c 输入下面代
  • 程序员3年5年10年三个阶段

    第一阶段 三年 三年对于程序员来说是第一个门槛 xff0c 这个阶段将会淘汰掉一批不适合写代码的人 这一阶段 xff0c 我们走出校园 xff0c 迈入社会 xff0c 成为一名程序员 xff0c 正式从书本上的内容迈向真正的企业级开发 我
  • 使用 matplotlib 轻松制作动画

    https www codenong com e264872efa062c7d6955 该链接讲了如何使用 matplotlib 轻松制作动画 xff0c 很好用
  • C#中使用IMemoryCache实现内存缓存

    1 缓存基础知识 缓存是实际工作中非常常用的一种提高性能的方法 缓存可以减少生成内容所需的工作 xff0c 从而显著提高应用程序的性能和可伸缩性 缓存最适用于不经常更改的数据 通过缓存 xff0c 可以比从原始数据源返回的数据的副本速度快得
  • 2021-09-13使用@Slf4j报错 程序包org.slf4j不存在

    导入两个maven依赖 然后就OK了 span class token tag span class token tag span class token punctuation lt span dependency span span c
  • PowerShell7.X的安装与美化

    参考链接1 xff1a https blog csdn net qq 39537898 article details 117411132参考链接2 xff1a https sspai com post 59380 很有参考价值 xff0c
  • Lab2 p3 围棋吃子的算法实现

    简单介绍下框架 xff1a 1 xff0e 声明一维数组block 作为一个临时变量记录一个块的大小 xff0c 声明一个整型blockLength记录这个块的长度 2 xff0e kill 为吃子的主函数 recersion int i
  • Python爬取皮皮虾视频

    背景 xff1a 今天闲着没事做 xff0c 然后想着刷刷视频 xff0c 然后发现前段时间学习了一下网络爬虫的一些基本应用 xff0c 就想着利用爬虫到网上去爬取一点视频来模拟人为的点击 下载操作 因为皮皮虾是手机端的app xff0c
  • 解决Result Maps collection already contains value for...BaseResultMap问题

    使用generatorSqlmapCustom逆向工程生成代码报错 假如使用generatorSqlmapCustom逆向工程生成代码 xff0c 即生成dao文件和mapper xml文件 xff0c 复制粘贴至工程中运行报错 Resul
  • IDEA2022.1的一些不常见问题解决方案

    文章目录 IDEA2022 1小问题解决方案 学习的时候尝鲜用了最新版本的IDEA 出现过以下老版本不会遇见的问题 Spring Initializer 创建的项目 无法新建module 显示Directory is already tak
  • 史上最全,Android P指纹应用及源码解析

    简单使用 源码分析 首先需要了解以下几点 指纹识别相关api是在Android23版本添加的 xff0c 所以过早版本的设备是无法使用的 xff1b android span class token punctuation span os
  • RNA-seq数据分析(HISAT2+featureCounts+StringTie)

    RNA seq数据分析 简介1 生物基础1 1 中心法则1 2 RNA seq Protocol1 3 RNA seq总的路线图 2 数据分析2 1 前期准备2 1 1 创建目录 amp 安装conda2 1 2 常用文件格式简介 2 2
  • Lottie动画的优劣及原理

    前言 Lottie是目前应用十分广泛的动画框架 在周会汇报的时候 xff0c 老板问能不能对Lottie进行优化 xff0c 于是就有了下文对Lottie原理的研究 毕竟要进行优化 xff0c 首先要深入了解原理嘛 Lottie实现 Lot
  • 详解微服务技术中进程间通信

    在单体应用中 xff0c 一个组件调用其它组组件时 xff0c 是通过语言级的方法或者函数调用 xff0c 而一个基于微服务的应用是运行于多个服务器上的分布式系统 xff0c 每个服务实例是一个典型的进程 所以 xff0c 如下图显示的 x