消息与rsm消息_JavaEE or BigData?都是Data System, RSM内功心法踏山河!

87a7446f8ab58c3032baca2492aad881.png

本篇主题:

  • Data System
  • RSM
  • 可靠性
  • 可扩展
  • 可维护

数据系统

在说设计企业Data System的几个共同特性之前,我们先得了解我们现在了解下我们说开发的系统的特点。当然,我不会来你的系统是用PHP开发的、Java开发的、或者是基于SQL开发的。我们从资源角度来把应用分为两种,一、Data-Intensive即数据密集型应用,二、Compute-Intensive即计算密集型应用。

Data-Intensive

如果一个应用它主要的challenge是数据量、数据的复杂度、或者数据变化的速度等,那么这个应用就是Data-Intensive应用程序。Data-Intensive应用的瓶颈大多是在I/O上。维基百科是这样描述的:Data-Intensive应用是一类并行计算应用程序,它们使用数据并行计算地方法来处理大量数据,通常大小为TB或者PB级,通常称为大数据。了解过MapReduce的童鞋应该会有感觉。因为大量的性能地消耗是在I/O上,从内存到磁盘,将对象进行序列化通过网络传输,Shuffle等等。所以,大数据中的技术基本上都是Data-Intensive的。

3cce08d388447aac500f9a816f84fee4.png

其实,很多后端系统也是Data-Intensive应用,如果大家熟悉后端开发,看看后端服务的瓶颈在哪儿就知道了。很多时候后端瓶颈还是在I/O上,并不是在CPU上。

Compute-Intensive

计算密集型应用大量的时间都是在计算上。CPU频率是瓶颈。像一些科学计算类的、航空航天控制系统、生物基因工程、深度学习等等。这些应用大都是计算密集型的。

Data-Intensive和Compute-Intensive简单来区分就是IO密集还是CPU密集。

其实你开发的都是Data-Intensive应用

针对Data-Intensive应用程序,现在有很多工具和技术可以选择,NoSQL、消息队列、缓存、batch computing、streaming computing都受到了广泛的关注。而在企业中,一套成体系的架构需要将多种这些技术组合在一起应用。

作为一名架构师或者是资深软件开发,要利用这些技术构建Data-Intensive应用程序,就需要结合项目、业务权衡取舍,因此非常有必要深入去了解这些技术,而不仅仅是说出各种技术的名词。

相比于网络、内存、磁盘,我们发现CPU升级、发展的相对而言要小得多。其实如今大多数的应用程序都是Data-Intensive的,而不是Compute-Intensive的。一个Data-Intensive的应用程序通常需要很多组件来构建,例如:

1、我们使用RDBMS将数据存储起来,供其他应用读取

2、我们使用cache保存计算后的结果,以便将来高效、快速读取

3、我们full-text search让用户可以根据关键字来快速检索信息

4、我们基于Message Queue发送消息给其他进程,并基于Stream processing进行异步处理

5、我们基于batch processing定期处理大批量历史累积的数据

CRUD boy & SQL boy你们感到困惑吗

在构建数据应用时,绝大多数Dev都不会从头开始构建一个新的数据存储引擎。也就是基于这个原因,就诞生了许许多多地CRUD Boy、SQL Boy。也有很多的人会说,大数据就是写SQL。当聊到技术时,这些人马上会说,我们没有造轮子的必要啊,只要会用轮子就可以了,做一个调包侠不好吗?其实,这话没毛病。但有个问题值得思考:你想在IT这条路上走多远呢?这一切的一切,是因为我们说使用的系统做了非常好的抽象,让我们看起来写SQL语句就可以搞定一切了。

62a30dfd1849f607c4662aa34b06d031.png

但其实没那么简单。不同类型的数据系统有不同的特点,例如:构建缓存、索引有很多种方法,我们不可能仅仅通过用几个API就搞定了所有。如果你真的用几个API就搞定了所有,如果你自己还不钻研,那很不幸,你将会在未来几年欠下高额技术债务。在设计应用时,我们非常有必要弄清楚哪些技术和方法是最适合项目背景和业务场景的。如果我们对各种技术没有深入了解,设计数据系统发现单一的技术组件无法满足需求时,将很难将各种技术有效地结合到一起使用。

永远都在变化的API,而目标、原则不会变

我们设计数据系统时,都有几个共同的目标:

1、稳定可靠

2、高度可扩展

3、好维护

大家可以看一看开源社区的各种技术,经典的项目一定是会涉及这几个点。不管是Spring Boot、Spring Cloud、ZooKeeper、Hadoop、Kafka、ClickHouse、Flink、Spark等等。如今,新技术日新月异,更迭不穷。但其背后的原则不会变。我们去了解这些原则,就可以了解到某种技术或者是框架的本质,了解它们的适用场景。上述的三个目标是建设系统的不变主题。

数据系统

说到数据,我们首先会想到数据库、队列、缓存等技术。而很多时候,我们都把这些技术分开为不同的类别。例如:虽然数据库和队列是有共同特点的,例如:两者都会存储数据一段时间。但它们有差异很大的存储和访问模式,就意味着它们有不同的特性,实现方式也大不相同。

而近些年来,出现了很多许多的数据存储和处理工具。这些工具根据不同的业务场景被优化成不同的应用模式。例如:使用Redis可以作为消息队列使用、Apache Kafka消息队列可以用来持久化消息存储。这些数据存储之间的类别界限越来越模糊。而现今企业对数据存储和处理的需求也越来越广泛,很多时候我们无法用单一的工具来解决所有业务场景问题。所以,数据应用被分解成不同的部分,每个部分用不同的数据技术中实现。而这些数据技术组合在一起,就可以组成了一个数据系统。

下面的一张图就是企业中比较常见的一张简单架构图,我们可以看到不同的存储技术组成了一个「数据系统」。

34677359e4cc39605b1e00c417c15f12.png

设计这样的系统,同时要保证它能够正确、稳定地运行,会面临以下一系列问题:

1、在程序内部出错的情况下,如何保证数据的正确性、完整性、一致性?

2、在某个组件出现故障时,如何依然保证系统的性能?

3、数据系统如何扩展,以处理临时的高峰负载?

4、如何设计好一个好的服务API?

等等。

这些问题会因为不同的外部因素而受影响。包括技术人员的技术水平和经验、遗留系统、系统交付周期、业务对性能/故障的容忍程度。而作为一名架构师或者是高级技术人员,就是要在这一些因素之间做权衡。

说这里,推出与具体某个框架、某个组件无关的RSM。


RSM

1、可靠性(Reliability)

在硬件或者软件出现故障(甚至是人错误)时,依然能够正确运行。

2、扩展性(Scalability)

随着系统的并发量、数据量、或者复杂性的增长,有合理的扩展办法来处理。

3、可维护性(Maintainability)

系统运行时间越来越长,一个系统将会有很多人在开发,团队应该能够有效地工作。


可靠

可靠,这个词大家都认识,就是这个货不可靠、不靠谱、不值得信赖。对于计算机系统,典型的「可靠」应该包含以下几点:

1、应用程序实现的功能能够满足用户的预期。

2、应用程序能够容忍用户的一些错误、或者不恰当的使用方式。

3、在预期的数据量和负载下,应用程序依然能够保持良好的性能。

4、应用程序具备安全特性,在一些未经过认证的情况下,能够正确识别,并有效处理。

可靠性可以大致理解为:即使出现了一些错误,系统依然能够正确地运行。理想很美好,现实很残酷,我们想让系统能够处理每一种故障是不可能的。举个不靠谱的例子,如果地球发生了大爆炸,你如何处理这种意外呢?。所以,所谓的可靠性是要基于预算有限的情况,数据系统能够容忍某些类型的错误。

为了保证系统的可靠性,所以有必要能够通过一些机制来有意触发一些故障,这样可以确保系统是高度容错的,也让我们对建设的数据系统更有信心。

硬件故障

常见的硬件故障,以下几种场景可能会映入大家的眼帘:硬盘崩溃、内存故障、机房停电、断网。尤其是应用程序运行在一个机器特别多的集群中时,硬件出现故障的情况是比较常见的。

3d103cd5721f0673b2d49789c5175830.png

一个硬盘的平均故障时间为10-50年。如果在一个集群中使用了1W个硬盘,那么出现故障的概率就比较高了。针对这种情况,我们可以给硬件增加冗余备份,以此来降低系统的故障率。例如:通过RAID配置硬盘、双电源、热切换CPU、数据中心还有UPS、发电机作为备用电源。所以,当一个硬件出现故障时,冗余的硬件会取代之前的故障件。一些关键业务核心系统中(例如:银行),都有配备有这样的硬件冗余,可以保障机器连续运行很多年。有了冗余硬件,可以有效避免发生灾难性故障。

随着数据量和应用程序的计算需求暴涨,一些应用程序开始使用更多的机器来存储或计算海量的数据,而引入的硬件越多,出现故障的概率越大。尤其在一些云平台,虚拟机可能在没有任何警告的情况下突然宕机。而云平把弹性扩展放在第一位,而不是在于单机的可靠性。

基于此,越来越多的厂商开始使用基于软件的容错,或者是基于硬件容错的基础上再进行软件容错。通过软件容错的系统,整个机器宕机就系统的运行都不会有太大影响,更不会发生灾难性故障。而单机服务器每次出现故障都需要一定的停机时间,而通过基于软件的容错,不需要整个系统全部停机再去解决故障。

软件故障

一般来说,硬件故障是随机的,而且硬件与硬件故障之间往往是独立的。例如:一台服务器的硬盘坏了,并不意味着另一台机器的硬盘也会坏,硬件故障与硬件故障之间关联性比较弱。大多数时候,硬件不太可能都同时失效。

而另一类故障是软件系统内部的错误,这种故障很难预测,因为软件系统之间是跨节点连接的(集群),和硬件故障不一样,一个节点的软系统错误和另一个点的错误是存在一定的关联关系。例如:一个节点耗尽了CPU、内存、磁盘空间或者占用了大多数的带宽。系统依赖的某个服务无响应、更有一些系统可能会出现级联故障,一个节点出现故障,会导致一系列的故障发生。

87952bc222740922ad413bbd86e773ad.png

这些软件故障,可能会在大部分情况下是不会出现的。但在某一个特殊情况触发了故障,就需要花费大量精力排查问题,很多时候这种故障的解决并不像换一个网卡那么简单。这也反映了,软件系统的故障不太好快速解决的,所以在设计系统时,需要经过充分的测试。例如:允许进程崩溃或者重新启动。通过监控、度量来分析系统的行为。

人为错误

系统是人设计出来的,同时,也是人来保证系统正确运行。在很多系统中,人为错误(例如:人为的配置错误)是导致系统停机的主要原因。大多数时候,人都是被认为是不可靠的。

0a1e1aa01dfc66894b39389ad185cf9b.png

为了让系统更加可靠,针对人为操作有几下几点防治方法:

1、设计系统的时候尽量减少出错的概率。

2、提供功能比较齐全的非生产沙箱环境,可以在使用到真实、不影响用户的数据沙箱环境中进行测试,很多时候,因为开发环境和生产环境是不一致的,导致了各种问题。

3、充分的测试,从单元测试、系统集成测试、手动测试等都不能疏忽大意,特别一些非常关键的模块,需要进行充分的测试以保证系统正确地执行。

4、设计的系统能够快速地恢复回来,尽量减少故障的影响。例如:可以回滚配置、迭代部署,这样只影响一小部分用户、以及提供重新计算(重跑数据)的工具。

5、配置详细监控工具,例如:性能指标和错误监控。这种方式,也称作为遥测。监控可以向开发维护人员发出警报,当出现问题时,度量指标对诊断问题是非常重要的。

可靠的重要性

大家可以想象以下,如果我们开发的系统是一个商业系统,而因为系统故障导致数据不准确,这将会带来商业、法律上的风险。如果是开发的是一个电商系统,如果因为系统中断,会损失很大的收益,并且会影响品牌的口碑。

即使在一些非关键的应用上保证系统的可靠性也是非常关键的。大家可以想象下,如果你在百度云盘上保存了大量以前的照片、以及收集了很久的视频、电影。如果云盘突然挂掉,数据也丢了。你会是什么样的感觉?是不是一万匹草泥马飞过?

而在实际地项目开发中,很多时候为了降低开发成本和运营、维护成本,往往会选择牺牲系统的可靠性,但我们必须清楚地知道在什么样的组件上「偷工减料」,否则后果不堪设想。


可扩展

在设计数据系统时,我们不光光是考虑现在,还要考虑未来。一个系统今天可靠地运行着,但这不代表将来就一定都是可靠地运行着。一个常见的原因就是负载的增加。例如:1W个并发用户可能将来会到10W个并发用户、更多的甚至是从100W的负载到1000W的负载,再一个就是数据量的增长,当前要处理的是100GB的数据,将来可能要处理的是10TB的数据。

可扩展性是用来描述系统应对负载增加的能力。我们所说的可扩展是指的整个系统。例如:如果负载不断在增加,系统要扛住这些不断增加的负载,系统应该如何应对?我们如何来增加计算资源应对高负载?

负载

在考虑可扩展之前,我们得先明确当前系统的负载。只有对当前系统的负载有所衡量,才能描述将来增长的负载。我们可以用负载参数来描述负载。而负载参数取决于数据系统的体系结构。负载参数可能是:每秒web服务器能够处理的请求、数据库中读写的速率、同时访问web系统的用户并发数、或者是缓存的命中率等等。

性能

量化了当前系统的负载之后,我们就可以开始调查当负载发生变化的时候会发生什么。考虑两个问题:

1、当负载增加时, CPU、内存、网络带宽保持不变,系统的性能会受到什么样的影响?

2、当负载增加时,我们如何增加资源,让系统的性能不受影响?

这两个问题,都需要有性能指标的描述,所以我们有必要来描述系统的性能。在一个批处理系统,例如:Hadoop。我们通常比较关心的是吞吐量,也就是系统每秒能够处理的数据量。或者在一定规模的数据上执行作业所需的总时间。在一些web系统中,比较重要的是请求服务的响应时间,即客户端发送请求和接收响应的时间。

一个系统的响应时间是会有变化的。大多数的请求是比较快的,但有一些特殊情况可能会导致需要更长时间的处理。例如:某个id对应的数据量很大。还有一些是随机的延迟,例如:进程的上下文切换、网络丢包、TCP重传、垃圾回收等,这些原因都有可能会影响系统的响应时间。所以,很多时候,我们计算系统的响应时间,往往是通过算术平均值来计算。但如果我们想知道某个类型的请求/操作的响应时间,算术平均值并不是一个很好的度量方法。因为通过平均值,我们无法得知有多少用户是延迟的。

实际比较好的做法使用中位数来描述。将所有的响应时间从最小到最大排序,中位数就是排序后的中点。例如:某一个请求的中位响应时间是200毫秒,这意味着有一半的请求是大于200毫秒的、一半的请求是小于200毫秒的。中位数也称为50th百分位数,有时会缩写为p50。

为了找出一些异常值的延迟,我们可以查看更高的百分位数。95th、99th、99.9th位数的请求。例如:95%百分数的响应时间为1.5秒,这就意味着100个请求当中,95个是小于1.5秒的,100个请求中有5个的响应时间是比1.5秒更长的。

响应时间的高百分位数,有时候也称之为尾延迟。它们往往会直接影响用户的体验。我们可以根据高百分位数来衡量,是否需要投入资源去提升这部分用户的体验。亚马逊提出过这样的报告:每当响应延迟100毫秒,将会减少1%的销售量。还有一些人说,如果响应延迟1秒,会减少16%的用户。如果我们发现优化99.9th的百分位数需要非常大的资源投入,而这部分投入却没办法创造足够多的价值,而且99.9th百分位数的响应时间是比较难优化的,因为系统比较容易受到一些随机事件说影响。例如:在请求期间发生了GC。

可扩展

前面我们已经大致了解了负载和性能的度量,接下来我们就来了解下可扩展。当负载增加了,如何保证性能呢?

当前我们开发的负载假设能够满足1W的负载,当负载到达10W的时候,我们的体系结构基本上是不能满足的。所以,我们有必要考虑每一个数量级的负载的增加。

关于扩展我们经常会提到垂直扩展和水平扩展。垂直扩展指的是将系统移植到性能更好的机器上,或者对当前的机器进行硬件的扩容。而水平扩展是使用更多的机器来均衡负载。通过多台机器扩展来均衡负载也称之为shared-nothing架构。一个运行在单机上的系统往往是比较简单的,但是高性能的机器价格不菲,有的甚至非常昂贵,而非常密集的负载要使用这些昂贵的机器来负载成本非常高。

在实际项目中,一些比较好的体系架构会混合使用垂直扩展和水平扩展。例如:有的时候,使用几台性能好的机器要比使用大量的小型虚拟机更加地简单和实惠。有的系统是弹性的,这些系统当检测到系统负载增加时,这些系统可以自动添加计算资源,不需要手动地扩展机器。这种系统针对一些负载不可预测的场景,弹性系统可能是有用的。但其实,手动扩展的系统更简单,而且可以有效避免一些因为错误操作导致的问题。

虽然在多台机器上分布无状态服务是比较直接的,但将有状态的数据系统从单机节点扩展到分布式架构会引入更多的复杂性。所以,大部分的数据系统都把这些状态数据保持在一个节点上(例如:Hadoop的NameNode),除非是要做高可用或者是该节点负载已经到了必须扩展的时候了。随着分布式系统技术越来越完善,某些技术开始做调整,想象以下未来,分布式数据系统将是默认的数据系统,即使我们当前没有大的负载、或者没有大量的数据、流量。大规模的系统往往只适应于特定的应用场景,几乎没有一种通用的、一刀切且高度可扩展的体系结构,设计一个系统需要考虑:读取的数据量、写入的数据量、存储的数据量、数据的复杂度、响应时间的要求、数据读取的模式,这些问题都是一个系统说需要权衡的。

举个例子:一个系统设计的时候是以能够处理每秒10W个请求、每个请求为1KB为设计目标的。设计这样的一个系统与设计一个每分钟3个请求、每个请求2GB的系统差距是相当大的。

一个具备有高度可扩展的体系结构是建立在假设的基础之上的,例如:假设哪些操作是常见的,读多,还是写多?哪些操作是罕见的。

作为一名架构师,我们需要具备假设的能力。但如果因为没有经验,我们做了错误的假设,这就很尴尬了。比较好的情况下,仅仅是造成一些资源的浪费,坏的情况会适得其反,把简单的事情搞复杂。

所以,在早期启动或者对某些技术还是不熟练的情况下,对于产品来说还是要能够快速迭代产品功能,而不是要扩展到未来某一天的负载。简单一句话来说,就是别想太多。


可维护

就像我们养孩子一样,孩子出生的时候其实花不了太多钱,但要把一个孩子抚养成人,就需要花费大量的成本了。很多软件,并不是在最初的开发过程。而是在对软件进行持续维护的过程。我们要不断地修复系统的错误,保持系统的运行,排查故障,并且让系统能够适应新的平台架构,为了能够处理一些业务case去修改它,添加新的功能。还有一点,就是因为一开始我们对某项技术不熟悉,导致了一些不恰当的使用方式,后续我们还要为这些「技术债务」买单。

大多数开发人员都是不喜欢维护遗留的系统,因为他不得不去解决别人犯下的错误。还有就是大家都不愿意去搞一些已经过时的平台。不管是什么样的遗留系统,都是被人以各种方式喷成一堆狗屎(小猴也有被喷过的经验),而且很多时候要处理遗留系统的问题也很难。所以,基于此,我们应该以以下几个原则来设计我们的系统。很多我们开发的系统,将来维护这个系统的很有可能还是我们自己。不管是我们、还是其他的Dev,我们要特别注意以下三点原则:

1、可操作性

能够让运维团队操作起来很简单

2、简单

为了能够让新的工程师容易理解系统,我们应该尽量减少系统的复杂度

3、敏捷性

能够方便将来开发人员对系统进行迅速响应以满足新的业务需求

可操作性

有句话是这么说的:“一个糟糕的软件但它有好的操作性,它依然可以存活。但一个设计良好的软件但操作起来很繁琐,往往存活的周期很短”。尽管现在大家都说各种自动化运维,但其实实际的项目中,还是少不了要人为地去设置,并确保自动化脚本能够正确执行。很多初级Dev,在技术与实现把握不好度。

运维团队对于保证系统能够稳定运行至关重要, 而具备有良好的可操作性就意味着让日常的维护任务变得简单,让运维团队集中精力在做更有价值的事情上。我们设计的数据系统可以做很多事情让维护任务变得简单:

1、给系统运行过程提供良好的监控方式

2、提供自动化与标准化集成工具

3、避免依赖于单个机器,就是某个机器出现故障,系统可以继续运行,运维人员可以把机器取下来进行维护

4、提供较好的操作文档,和易于理解的操作模型

5、提供能够工作的默认配置,也提供给运维人员进行配置的方式

6、能够在适当的情况下系统自己修复问题(自愈术),也允许运维人员以手动地方式控制系统,修复问题

7、尽可能让所有的行为都是可预测的,不要搞一些surprise….

简单性

一般小的项目代码看起来简单令人愉快。但项目越来越大后,系统开始变得非常复杂,且难以理解。系统复杂度不断增加,会让团队里面的每个人的工作成本增加、同时也会增加维护成本。开发人员如果陷入到复杂的软件项目中,就好比跳进了一个大坑,根本跳不出来。

系统的复杂不限于:杂乱的有状态数据、模块与模块之间紧密耦合、依赖关系复杂、命名和术语不一致等等。当系统的复杂度变得难以维护时,项目的预算和时间都会超标。在一些复杂的系统中,往往我们尝试去修改它时,非常容易引起错误。系统难以理解,开发人员往往容易忽略掉其中的某些细节,导致不可控的故障发生。如果复杂度降低了,会大大提高软件的可维护性。所以,简单性是构建系统的关键目标。

复杂很多时候是人为导致的。而解决复杂性的最好的工具就是:抽象。对业务进行一个好的抽象,往往可以将大量的实现细节,隐藏在一个简单易懂的逻辑中。通过抽象,我们才能设计出更高质量的软件。举例来说:高级语言对于程序员更友好,因为它对机器代码、CPU、寄存器和系统调用等进行了抽象,屏蔽了很多实现细节、SQL也是一种抽象,它隐藏了复杂的内存和磁盘中的数据结构以及算法。但其实,我们在使用高级语言编程时,底层运行地仍然是机器码,我们只是不直接使用它,因为编程语言提供给我们的抽象使我们不需要要考虑它。但要找到抽象绝非易事,在分布式领域,有很多好的算法,但很难进行抽象。我们需要不断探索、探讨,提取关键点,有的时候需要对现实世界进行类比,找到好的抽象!

敏捷性

我们所设计的系统是不能不发生变化的,绝大多数的系统都是在不断变化的。例如,有的新的业务扩展、有特殊的user case、业务的优先级调整了、用户提出了新的需求、新的平台取代了旧的平台等等。

所以在程序设计时,敏捷的开发模式提供了一个框架,社区还开发了一些工具和模式。这些工具和模式在频繁变化的开发环境中很有帮助,例如:测试驱动开发、重构等等。


总结

不管设计JavaEE系统还是BigData系统,我们大都是在设计Data-Intensive系统。而Data-Intensive系统,一定会考虑可靠、可扩展、可维护三个特性。而伴随着技术的发展,后端开发与数据开发的界限会越来越模糊。所以将来的架构师、甚至是开发人员,他们要面临是企业的一站式解决方案,而不是单一的软件系统或者平台。而作为一名优秀的架构师或者资深开发人员,能够根据项目背景、业务场景对将来做出合理的假设很重要,这也是体系一个架构师的经验所在。权衡利弊,能够在大量组件、大量解决方案中找到最哇塞的那个。世界很美好!上面也提到了敏捷、迭代、简单设计,其实小猴2015年开始在团队里强调,很遗憾,但是没人能够感知。先work out,再不断turning,做什么事情都一样。你也会发现,世界很美好。细品!你会发现其中有很多的道理,会比写API更有趣,更值得人内省。

以上

参考文献:

Martin Kleppmann O’REILLY 《Designing Data-Intensive Applications》
Martin – The PMC member on Apache Avro and Apache Samza https:// en.wikipedia.org/wiki/D ata-intensive_computing
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值