`
herman_liu76
  • 浏览: 96727 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

分析几个有名中间件源码的核心类的设计

阅读更多
    分析过不少中间件产品后,自己也设计过产品后,也需要在更高的高度上总结一下。本文先总结了一下核心类的定义与主要的设计特点,再进一步用druid,rocketmq,spring-jms等源码的核心类进行说明(包括启动/停止),之后还介绍了一下dubbo的核心类在哪?(它更像一个产业链结构,核心类控制采购与销售,中间可能层层外包出去)

    在讲核心类之前,其实在软件系统有之前,我们的社会组织,或者企业组织,都是这么个结构,核心类就是中央,就是总经理。当然也有与dobbo对应的组织形态,控制头尾,中间层层分包出去。

    理解了核心类,也就很容易理解整个产品的的结构设计了,另外对于应用系统,对于分布式架构,对于微服务架构设计与组织,也有思想上的启发。类比一下,核心类类似DDD(领域驱动设计)中的聚合根,类似于组合多个resposity的service,类似于微服务中的聚合服务。



## 一、什么是核心类

我们说,擒贼先擒王,研究一个东西要抓核心,那一个软件产品的核心在哪?研究过一些产品后,发现产品都有核心类,自己也应用这个思想把控组件设计。

核心类是产品功能的最主要的实现类,也是类中的老大哥,它下面的兄弟众多,它可能只负责协调调用,也可能功能需要兄弟们协助,也可能根据不同的情况选择不同的兄弟,也可能安排助手去监督兄弟们的工作...

核心类不一定是最花时间的,但一定是稳定系统结构的,显示出整体的思想与高度。有助于在万事开头难的情况下跨出第一步。现在常说API接口优先,那是实现角度,复杂系统的功能是如何进行组织的,可能是更优先的设计。



核心类的基本结构与考量如下:

  • - 自身可能是工厂产生,或者是单例对象,一般复杂的都是工厂产生的。
  • - 有自身状态,有生命周期。构造,inti,优雅的启动与关闭,在构造中组建好自己的团队成员
  • - 它持有其它功能类成员,这些功能类一般不相互引用,但它把this传给功能类,可相互引用
  • - 特殊的成员比如,通讯成员,负责对外接收发送数据,也可能对内对外协议不同有不同的通讯员。如果外对支持多协议,还要插入一个通用API层。
  • - 成员基于接口引用,所以核心类持有的成员兄弟很多可替换,如果是策略模式,加载根据运行情况,可通过spi,或者aware spring容器去取,或者配置参数用工厂来产生,或者如dubbo统一建一个extension中心。
  • - 交给一些成员重要功能时,可能会给它设置一个引用自己的回调类,让核心类感知变化,协调资源
  • - 可能包含多个线程或者线程池,可能分工作线程与自身守护线程定时运行,可能需要锁进行同步,Synchronized优化偏向锁,自旋锁,锁再升级,性能可以的
  • - 包含共享变量,或者共享数据容器,注意volitile/concurrent/atom使用
  • - 核心类自身产生多个对象各自工作,一般用享元模式存放,即static map,当然也可能有个上层进行简单管理,比如多个jms消费容器,上头有注册器统一管理。




## 二、核心类例子

### 1. rocketmq中的BrokerController

rocketmq是阿里的消息中间件,它有一个重要模块是broker,功能是接收消息并存储,索引,分队列的,也是推送消息的客户端的。这个模块的核心类是org.apache.rocketmq.broker.BrokerController,它的特点有:

- 成员众多且不直接引用
  •   1. 有几个config成员,管不同组件的配置
  •   2. 有很多Manager成员,比如管消费偏移量,管客户端
  •   3. 有Service,比如扫描客户端通道状态与变化
  •   4. 有些Listener,比如监听消息到来
  •   5. 有负责内部通讯的romotingServer
  •   6. 有处理消息的Processor,有存储负责DefaultMessageStore等,各司其职。DefaultMessageStore也是存储方面的子核心类,结构关系很相似。
  •   7. 有很多BlockingQueue与ExecutorService,它们组合起来,再与Processor交给romotingServer用于处理接收不同类型的消息。

- 构造函数与initialize初始化方法
  •   1. 构造函数中有很多类似this.topicConfigManager = new TopicConfigManager(this);的语句,即产生了成员对象,也实现了核心类与功能类对象的相互引用。如果设计不好,走“捷径”,成员之间相互引用,就乱成毛线团了。而成员由于引用了核心类对象,找谁也找的到,也就无所不能了。
  •   2. initialize中,先由一些成员load加载数据,满足条件后,再进一步产生一些成员,比如存储管理,通讯管理等成员。

- init中安排成员工作
  •   1. 比如安排通讯成员工作。在初始化中,通讯成员启动后把Processor与ExecutorService交给它,通讯成员就开始工作了,监听tcp端口,处理请求了。那请求时需要核心类帮忙怎么办?通过SendMessageProcessor sendProcessor = new SendMessageProcessor(this);,处理器可以找到核心类,那工作都好做了。
  •   2. 比如安排存储成员工作。先产生了DLedgerRoleChangeHandler传给存储成员,存储发生变化时会通知这个handler,hander由于持有核心类对象,所以也无所不能。

- 生命周期管理
  •   1. 比如start()方法,让很多成员也start(),也让自己的定时守线线程启动起来。
  •   2. shutdown()让很多成员也shutdown(),线程池也shutdown了。

- 如何启动/停止核心类
  •   1. BrokerStartup用main方法开始,调用构造函数new 一个BrokerController。
  •   2. 并在Runtime停止时,用hook关闭核心类对象。
  •   3. 另外提一下,客户端client的核心类是懒加载的,有producer了才启动。没人用,不空转。


**通过上面的总结,一个简单的消息处理过程是这样的**:通讯成员接收客户端消息,它在线程池中用处理器处理时,处理器可以找到核心类,核心类会让存储存好数据,再返回结果,通讯成员就可以回复客户端消息接收成功了。

### 2. druid中的DruidDataSource

druid是阿里的数据库连接池产品,代理了几个常用的连接,并在所有操作中切入filter得到监控数据。这的最外层核心类是:com.alibaba.druid.pool.DruidDataSource,包括它父类。它的特点有:

- 成员众多
  •   1. 它不是上面那样很多功能类,而是很多基本数据,如:long弄的Count或者AtomicLong。正好符合统计监控的需要
  •   2. 重要的数据,即一堆真实的数据库连接。DruidConnectionHolder[]
  •   3. 有两个重要线程Thread,分别用于增加与减少真实的连接数。还有logStatsThread统计线程。
  •   4. 有个CountDownLatch,用于保证init操作在两个额外线程完成后完成。
  •   5. List<Filter>用于存放将插入监控的过滤器,Init中也反持有核心类。
  •   6. 有ReentrantLock用于控制共享数据的操作,其它略。

- 构造函数与初始化
  •   1. 由于它实现了jdbc的DataSource接口,可以就当一个DB数据源使用。在getConnection获取数据库连接时,进行Init()操作。
  •   2. Init中初始化一些值,包括连接的容器外,还启动了控制pool中连接数量的两个线程。是否可以用一个?
  •   3. 过滤器filter是插入sql操作中,真正进行监控的工具,核心类用List保存它们,而在核心类的Init中有:filter.init(this);说明每个过滤器都要持有核心类来工作,至少会把数据写在核心类的统计属性中。

- 关闭数据库连接
  •   1. close()中,interrupt这些线程,connHolder中的真正连接都close(),清空容器,清空filter的list。


**通过上面的总结,一个简单的连接过程是这样的**:调用DruidDataSource获取连接方法,如果DruidDataSource没初始化就进行初始化,从DruidDataSource中再拿到filter来处理统计,再用filter反持有的核心类DruidDataSource真正做事。实际中用了过滤链模式。这个druid原理很简单,但所有操作,所有对象要包装起来,琐碎工作量还是很大的。


### 3. spring-jms中的消费容器DefaultMessageListenerContainer

与前面的核心类相比,这个org.springframework.jms.listener.DefaultMessageListenerContainer并不是很突出,不过获取消息进行消费都是在这个容器中进行的。而且每一个注解@jmsListen都产生一个,它有多层父类,其特点有:

- 它有也不少属性成员
  •   1. 包括很多参数,重要成员有:Executor,transactionManager,messageListener,messageConverter等
  •   2. 还有放置runnable对象的容器Set<AsyncMessageListenerInvoker>,可放Executor中执行,线程之间有Monitor对象,用synchronized进行同步处理
  •   3. 它持有的线程启动后,很聪明的进行自我管理,可减少,可增加,可休息

- 它受spring生命周期管理,有明确的initialize()/start()/stop()方法。
- 它受上级管理
  •   1. 虽然它做核心功能,但由于它有很多相同的对象一起工作,所以有上层的管理者JmsListenerEndpointRegistry。
  •   2. JmsListenerEndpointRegistry用工厂JmsListenerContainerFactory构造出它后,用Map<String, MessageListenerContainer> listenerContainers保存它。算享元模式。
  •   3. 上级也试图对它们进行start()/stop()控制。


由于这个工具是在spring中,启动过程就是扫描注解,按@jmsListen产生这样的真正工作的容器,并接受spring的控制。

### 4.dubbo中的核心类

与前面几个产品中的核心类不同,回忆dubbo还真不如上面的这么明显存在一个核心类。

先整理下服务端接收调用过程例子:一个包含invocation的TCP请求过来,netty从早就建好的了channel中decode出来特定应用协议msg,看消息头,如果是业务处理msg,就找相应的handler处理消息,handler会从一个map表中找到invocation对应的invoker,invoker是由service统一包装出来的,再反射调用得到结果,再封装成msg,再由netty从channel中发回去。

服务端本来只有一个个service,通过dubbo产生上面的所有相关的东西。dubbo操作的过程是:

  • - 先把一个个service变成invoker
  • - 一个个invoker以invocation为key,存放在map中
  • - 启动一个netty通讯服务端,这个过程中要产生Netty用的decoder/encoder/msgHandler等类
  • - 要给msgHandler设置一个专门处理业务的内部handler
  • - 内部handler要能从msg中拿到invocation,再从map中拿到invoker
  • - 最后调用到service,得到结果,后面不提了。


这里,被spring加工的对象就是service,结果是让外部的各种请求可以调的到它。那么是否有设计一个核心类来做上面这一系列工作呢?没有。

或者说,由于dubbo的微核心化,大多数功能的实现类都是不确定的,在运行时由参数来确定。实现类统一放在extension中存放,甚至extension本身也有两种实现。在spring中用的话,那么上面的功能实际上由两个主要的类来实现。

核心类丙个:一个是ServiceBean,一个是协议如DubboProtocol。spring启动前者开始工作,前者再根据配置启动DubboProtocol工作,当然最主要的工作还是后者来做了。而且两个核心类的角度不同,ServiceBean对应一个service,但可以用多个Protocol暴露,而一个Protocol持有多个service的invoker,就是多对多的关系。

DubboProtocol持有的成员,与工作有:

  • - 持有一个New构造的真正处理RPC请求的requestHandler
  • - 持有exporterMap,让requestHandler可以找到对应的Invoker
  • - 提供参数启动具体的传输层,并把requestHandler交给它用,由于requestHandler是内部类,不用象上面用this引用核心类,就可以使用核心类了。
  • - 传输层具体怎么做就不管了,反正参数与最终的处理对象都给了它,算是分包出去了。
  • - 传输层会根据参数产生具体的netty,产生具体的encode/decode,再产生具体的serilize对象来处理tcp包中的dubbo协议包。


虽然dubbo的核心类不清晰,但顺着业务用例的流动,可以梳理出一个核心类调用另一个核心类工作的结构。如果不是要设计的这么灵活,特定的协议下,也可以写出一个清晰的核心类来做上面的工作。

### 5. 图

看着也蛮简单的,主要还是很多细节工作要做,而且细节要考虑的很仔细。比如rocketmq主要是存储,索引,队列和高可用这样的细节工作。



## 三、总结

其实还有些小例子就不一一举例了,我在设计组件时,也基本上采取这样的设计,感觉比较清楚,再复杂都可以可以把握全局,如同现实生活中,做好业务就有一个好的公司架构,也有强大的核心。

自己设计中,就可以把相似的功能复制过来用,比如rocketmq的内部通讯可以拿来用;或者把局部好的设计抄过来用,比如线程的自我控制,通讯处理器与带有阻塞队列线程池的组合,细粒度控制;设置监听了解成员的工作;另外成员其实也是局部功能中心,它也可以是层次结构;成员可以在运行中替换,比如dubbo中动态生成适配器成员,按参数持有真正成员;成员可以统一存放,用多种方式加载,比如工厂,比如SPI...

话说现在最喜欢的源码还是rocketmq,1主多从小集合比kafka全对等好,这与看到的某支3城5中心中的10个组,以及某信抢红包的一个set,都是相同的理念;另外对文件的处理值得参考;还有通讯设计可以直接用...

了解了结构也可以更好的使用,给使用者提供简单明了的操作入口。
1
1
分享到:
评论

相关推荐

    C 语言实现 linux pwd 命令内含源码以及说明书可以自己运行复现.zip

    C 语言实现 linux pwd 命令内含源码以及说明书可以自己运行复现.zip

    2024年中国变焦LED手电筒行业研究报告.docx

    2024年中国变焦LED手电筒行业研究报告

    node-v8.11.2-darwin-x64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    node-v4.8.2-x86.msi

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    ffmpeg 结合 SDL 编写播放器内含源码以及说明书可以自己运行复现.zip

    ffmpeg 结合 SDL 编写播放器内含源码以及说明书可以自己运行复现.zip

    2024-2030中国OPzV胶体电池市场现状研究分析与发展前景预测报告.docx

    2024-2030中国OPzV胶体电池市场现状研究分析与发展前景预测报告

    node-v4.9.0-win-x64.zip

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    2024年中国齿轮离心鼓风机行业研究报告.docx

    2024年中国齿轮离心鼓风机行业研究报告

    python教程-03-标签属性的获取和设置.ev4.rar

    python教程-03-标签属性的获取和设置.ev4.rar

    狂雨小说CMS-多功能小说系统

    安装说明 系统要求 PHP要求5.6版本以上,低于5.6版本无法运行 支持php7 addons,application,config,extend,public,runtime,template,uploads 目录必须要有写入权限 777 网站必须配置好伪静态(.htaccess为Apache伪静态配置文件,kyxscms.conf为Nginx伪静态配置文件) 宝塔面板要在 软件 php 设置里安装扩展 fileinfo

    基于java的高校毕业设计选题管理系统论文.docx

    基于java的高校毕业设计选题管理系统论文

    信创微服务平台建设指南(PDF)

    当前,以微服务、DevOps、容器、多云业务管理为代表的云原生技术已经广泛成熟应用,成为加速企业数字化业务高效创新、实现企业数字化转型的最佳技术支撑。而信创支持、国产化支持,是中国企业数字化转型不得不满足的基本要求。更有专家指出,在关乎企业生存的必选项“数字化转型”以及国家信创战略的共同冲击下,企业需要改变现有业务和IT的架构,更快速地应对挑战、响应变化,增强自身的竞争力。 信创微服务平台建设指南应当综合考虑当前的技术发展趋势、企业及用户的需求,以及平台的可持续发展能力。 《信创微服务平台建设指南》可参考文章:https://mp.weixin.qq.com/s/cfJH72JFxDTHCPsfFWMUqA

    node-v8.14.0-linux-ppc64le.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    Python爬虫.pdf

    上文提供了一个基础的Python爬虫示例,旨在从CSDN(一个知名的技术博客平台)上爬取特定文章的内容。这个示例代码展示了使用requests库发送HTTP请求,以及使用BeautifulSoup库解析HTML内容的基本流程。 步骤详解 导入必要的库: requests库用于发送HTTP请求。 BeautifulSoup库用于解析HTML内容。 time库用于控制请求之间的延迟。 设置目标URL和请求头: 目标URL是想要爬取的CSDN文章的URL,这里用占位符'https://blog.csdn.net/some_user/article/details/some_article_id'表示,实际使用时需要替换为真实的URL。 请求头(headers)通常用于模拟浏览器行为,避免被目标网站识别为爬虫。这里设置了User-Agent字段。 发送请求并获取响应内容: 使用requests.get()方法发送GET请求到目标URL,并传入请求头。 使用response.raise_for_status()方法检查请求是否成功。如果响应状态码不是200(成功),则抛出异常。 解析HTML内

    node-v8.2.0-linux-ppc64le.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    毕业设计写作技巧.pdf

    说明: 上文提供了一份关于毕业设计论文写作技巧的总结,旨在帮助学生在完成这一重要学术任务时更加系统、高效地进行。以下是对这份总结的详细说明: 一、目的与重要性 毕业设计论文不仅是学生学术生涯中的一个重要里程碑,也是检验学生综合运用所学知识、独立研究和解决问题能力的重要标准。因此,掌握一定的写作技巧对于顺利完成论文写作至关重要。 二、写作技巧总结 明确研究主题和目标: 学生首先需要确定自己的研究领域和研究方向,确保研究的主题具有实际意义和研究价值。 明确研究目标有助于学生在整个研究过程中保持清晰的方向,确保研究的深入和全面。 充分收集和整理资料: 学生需要通过广泛阅读相关领域的文献,了解研究现状和发展趋势,为论文写作提供充分的理论支撑。 对收集到的资料进行整理和分类,有助于提取有用信息,为论文写作提供有力的论据和支撑。 撰写论文大纲: 论文大纲是论文写作的蓝图,它有助于学生清晰地规划论文的结构和内容。 通过制定合理的大纲,学生可以在写作过程中保持逻辑严密、条理清晰。 论文写作注意事项: 引言部分需要简要介绍研究背景、意义、目的和范围,引起读者的兴趣。 正文部分需要详细阐述研究方

    node-v9.6.1-darwin-x64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    单片机开发资源:基于51单片机的开发程序

    单片机开发资源,基于51单片机的开发程序,供学习参考。

Global site tag (gtag.js) - Google Analytics