速来报名!AICon北京站鸿蒙专场~ 了解详情
写点什么

审视微服务架构:影响因素、运维复杂性和替代方案 |InfoQ 圆桌

  • 2021-02-16
  • 本文字数:8456 字

    阅读完需:约 28 分钟

审视微服务架构:影响因素、运维复杂性和替代方案 |InfoQ 圆桌

本文要点:


尽管业界已有一些迁移到微服务的成功案例,但依然有大量企业尚未接触到微服务战略。


当前的微服务方式比以往都要复杂。我们正构建越来越复杂的系统,使用越来越复杂的架构,进而导致我们举步维艰,学习曲线陡峭。

 

除了复杂性之外,如何监控和追踪也是微服务面对的极大挑战。

 

事件驱动架构是构建微服务的很好方法,尤其是针对各服务间的通信。

 

近期,Wes Reisz针对“微服务的影响”议题组织了一场InfoQ圆桌直播。该直播的参与者包括:Nginx 解决方案高级架构师 Leif Beaton、独立的 AWS 和无服务器顾问Yan Cui 和 Skyscanner 首席工程师Nicky Wrightson 。大家共同探讨了运维的复杂性,以及微服务模型的替代方案。

 

“微 Web 服务”(micro-web-services)一词最早是由Peter Rodgers在 2005 年的“Web 服务前沿大会”(Web Services Edge Conference)上提出的,在当时颇具革命性意义。尽管在 2005 年 SOAP 面向服务的架构正处于顶峰,但 Rodgers 主张的是 RESTful 服务。Rodgers 在演讲中,给出了一种良好设计的微 Web 服务平台,介绍了它是如何在类 UNIX 调度和流水线中组合 Web 和 REST 服务的底层架构准则,通过面向服务的架构提供灵活性和易用性。

 

此后历经六年的创新和思考,在 2011 年的五月的一次软件架构研讨会上,与会者根据自身所见所为,冠之以“微服务”一词。在 2012 年春季,社区开始使用“微服务”描述此类架构模式。

 

对此,James LewisFred George曾做过演讲。Adrian Cockcroft将其称为“细粒度面向服务架构”,并在 Netflix 积极推动此类系统的开发落地。

 

微服务这一新领域中的其他先行者还包括Sam NewmanEvan BottcherMartin FowlerGraham Tackley等人。在微服务得以命名之前,最早的介绍可追溯为Qcon大会的演讲

 

随着 DevOps 的引入,微服务正大步向前推进。DevOps 在团队构建系统方法方面变得越来越流行,尤其是持续部署的系统。

 

但是,微服务也的确需要付出额外的代价。本文将重点关注此类额外成本,以及是否值得权衡考虑微服务。

 

Wes Reisz:我最喜欢的微服务定义是由 Adrian Cockcroft 提出的,即“限界上下文(bounded context)中细粒度的面向服务架构”。各位是如何定义微服务的呢?


Wrightson:这是我这些年来一直在思考的问题。在我投身微服务领域的前三年中,实际上并不了解任何 DDD 的相关知识,我们的架构师也同样缺少认识。由此,我对微服务产生了一个非常具体的认识,即“最小的、最可独立部署的代码”。但这并不正确。我认识到对微服务的定义应该回归到“限界上下文”的理念。虽然二者并非同一理念,但这一定义确实给出了领域的上下文,也给出了不存在外部依赖项的认知。微服务是完全独立的。

 

Cui:我也非常认同围绕限界上下文给出的微服务定义。实际中,我常常会互换使用微服务和限界上下文。当然,我认为从架构的角度看,Lambda 函数满足所有被认为是微服务的条件。但一个函数并不能完成所有功能,它只是某个更大系统中的一小部分。为实现一个系统,需要所有功能都能完美、同步地协同工作。从业务的角度看,只有实际提供的功能,才是用户希望得到的。因此在我看来,微服务就是一组独立完备的职责范围。某个团队或是某个人,可以完全具备给定范围内的所有权,并且可以根据自己的喜好对功能做出考虑、移动和重组。除此之外,一切都应通过消息完成。直接依赖关系应尽可能地少,否则将会创建硬耦合,从而导致在更为复杂的环境中出现级联故障。


Reisz:微服务自横空出世以来,历经八年的发展。各位认为微服务当前处于技术采纳曲线的哪个阶段,是早期使用者(early adopt)、早期大众(early majority)、晚期大众(late majority)还是落伍者(laggards)?


Wrightson:出乎意料,事情发展并未背离我一直以来的设想。虽然还有大量早期使用者,但微服务已经越过了早期使用者这一天堑。我认为用户正继续努力向前推进。并非仅是微服务本身,而是涉及微服务的所有一切。用户需要完全改变架构并转变思维。而许多组织依然采用的是本地化部署,甚至从未考虑过云及其伴生技术,也从未考虑过 DevOps 和持续交付。

 

需要注意的是,微服务仅是解决了组织上的问题,并未解决技术上的问题。在选择微服务方式后,用户必须能够适应这种组织上的转变。我认为这种转变会对新使用者产生阻碍,因为大多数用户身处非常传统的组织,运行着非常复杂的单体应用。

 

Cui:我认为目前依然早期使用者阶段。我曾与许多从本地部署甚至是单体应用直接迁移到无服务器的客户共事,他们必须要去理解微服务,以及如何分解问题域。微服务在监控、调试和可观察性等方面依然面临着许多挑战。当然,这几年我接触到越来越多的各行各业公司,它们并未转向微服务。我意识到,微服务并非适合所有的企业。


Reisz: Nicky,你提到了“组织的”。最初我试图理解微服务概念时,找到了Martin Fowler的这篇博客文章。我对下面这幅插图印象深刻:“必须达到如此认识高度,才能运维或运行微服务。” 当前的认识高度如何?比原先更高了,还是更低了?成功运维微服务,需要掌握哪些基础技能?

 


Wrightson:我之前曾提到过,在组织上,我们通过微服务实现分散的更改,从而能够尽快部署。在公司发展到一定规模时,就会出现问题,团队和人员希望具有细粒度的自治权。那么这一规模具体是多大呢?这依然是一个悬而未决的问题。

 

我认为从整体上看,某些方面上已经变得更加复杂。对于每个复杂的问题,都已推出了一些简单的解决方案。如果某个复杂问题存在 12 种简单的解决方案,那么在解决该问题时,我们就必须了解全部 12 种产品。这导致问题永无止境,我们现在有太多太多的工具。为了整体把握问题,我们必须极大地扩展自己的认知。我认为图中那条线依然徘徊在相似的高度,只是需要了解的知识面略为拓宽了。

 

Cui:我非常赞同 Nicky 对工具的看法。我当然对 DevOps 生态系统在工具上的推陈出新毫无兴趣,也不喜欢很多人痴迷于具体使用何种工具,可以说是非常反感。就在几天前,我看到的 KubeCon 推介活动足足花了一分半钟的时间介绍 Kubernetes 集群中各种用于监视、控制等功能的工具。

 

Reisz:但我们也必须做出抉择。

 

Cui:的确如此。我认为许多伴生技术本来目的为了帮助用户构建微服务架构,但实际上却使构建微服务变得更加复杂,学习曲线更加陡峭。同时,我认为我们也正在构建越来越复杂的系统。特别是在无服务器领域,我看到了很多非常有趣而且非常复杂的事件驱动系统。这些系统提出了很多挑战,即便是以 API 为中心的微服务,也可能无法解决跨各部署(例如 SNS、Kinesis 和 DynamoDB 流等)进行追踪这一重要问题。为解决这些挑战,一些工具正在不断改进中。至少从可观察性方面看,我觉得很有意思的一点是,人们正在为越来越复杂的问题而构建越来越复杂的解决方案。


Reisz: Leif,你认为要达到何种高度才能使用微服务?


Leif Beaton:那要看你想要什么高度?采纳微服务具有一定代价,因为从微服务角度开发应用的思维方式,相比原有的单体应用开发过程在本质上完全不同。相比单体应用,开发人员需要更清楚地定义事物。对于一家小型初创公司,可以从单体应用入手,并观察其发展。直接使用微服务,往往会有些毫无头绪(spaghetti soup)。我并不是说必须只有大型企业才能使用微服务。只要事先做好规划,并厘清应用各部分的范围,无论对于小微企业还是大厂它都是很好的做法。补充一点,有些应用的确应该维持单体应用。


Reisz:我们已经在微服务上遇到了一些问题,其中之一就是 Nicky 所提到的复杂性。我认为 CNCF(Cloud Native Computing Foundation,云原生计算基金会)整体上就是一个庞然大物,其中提供了多种多样的可能选择。例如,Kubernetes 的容器网络接口(CNI)就存在 Canal、Flannel、Weave、Cilium 等多种可选的容器联网方案。复杂性当然是微服务中的一个影响因素。此外还存在哪些挑战?


Beaton:整体治理是挑战之一。对有计划采用微服务的用户,我们讨论过的监视、追踪等问题是非常重要。因为应用间的通信并非是在同一应用中运行的内存函数调用,而是与当前系统通信的本地或远端的网络调用。监视和追踪整个应用中的通信,这是至关重要的。否则,就像是戴着眼罩在黑屋子里抓一只黑猫。


Reisz:Nicky,你谈到了组织成熟度。为什么说组织成熟度是微服务面对的挑战之一?


Wrightson:我认为组织成熟度将推动团队的自治并具有所有权。在我看来,成熟度是非常有必要的,因为我们需要同时对一组微服务或领域采取行动。从组织上讲,成熟度意味着我们可拥有该领域。使用微服务,我们无需询问支付团队是否可以部署它,因为已经不再具有这些依赖关系。但从企业文化上讲,实现起来并非易事。


Reisz:Yan,我们最近探讨过编排,也探讨了在微服务环境中实现相互通信中面对的一些挑战。在此你能否介绍一下服务编排方面存在哪些挑战?


Cui:我想我们之间的探讨是围绕使用编制(orchestration)还是编排(choreography)实现业务工作流。其中,编排这一称法,主要用于以事件驱动为常态的无服务器领域。某些问题根本不值得付出使用事件驱动架构所导致的代价。因为在事件驱动架构中,所有的追踪和监视都是十分困难的。因为没有任何一处是受事件源控制的,所以我们无法找到一个中心点并说“好吧,工作流就是这样运行的”。系统的工作机制完全存在于某个人的心理模型中。

 

当然,如果处于某个微服务的限界上下文中,编排也是绝对可行的。这时可使用诸如 AWS Step Functions 之类的工具所提供的编排引擎,去解决很多问题。也能轻而易举地解决一些特定类型的问题,例如监视、调试和追踪。对于支付等关键业务工作流,我更喜欢使用 AWS Step Functions 等服务去获取实际的工作流,确保一切都是受控于事件源的。


Reisz:你提到了事件驱动架构。我的问题是,事件驱动架构与微服务二者间的关系如何?稍微打断一下,请你多解释一下事件驱动与微服务吗?


Cui:我认为人们原先都是这样理解微服务的,那就是一种服务通过某个 RESTful API 调用另一种服务,然后等待响应,因此可以看成是某种“请求-响应”模式。但大家很快就意识到,这种理解实际上存在着一些问题。如果在等待响应时发生失败,那么如何操作?恐怕我们得亲自去将应用停下来。为防止发生此类级联失败,我们必须做大量额外的工作。

 

此外,很多功能并不需要同步实现。事件驱动系统中,如果存在大量同步进程,则应尽量将它们添加到某个事件总线或事件队列中。例如,我只需处理订单,而不关心其它操作。我不必去让促销代码系统去生成促销代码,然后再让其他系统执行各自的功能。只需将事件发布到某个中心化的事件总线上,并显示“订单已成功处理”即可。此后,支付系统和促销代码系统会去监听他们各自关心的事件,进而执行相应的功能。

 

微服务和事件驱动架构二者是很好的结合。我们已经说过,微服务是独立自主的。如果某个功能必须持续不断地对其它系统的 API 调用做出响应,同时也必须去调用其它系统的 API,那么就会导致功能间更紧密的耦合。而如果每个模块都在监听事件,只要某个功能完成就发布事件,每个模块就是更加独立的,进而形成松散的耦合。因此,事件驱动是构建微服务的好方法,尤其是在不同服务间的通信方面。


Reisz:我们已经讨论了组织和复杂性问题,讨论了服务编排的相关问题。鉴于存在所有这些问题、所有的可选项,以及微服务所涉及的复杂性,我们是否应该回到构建单体应用上?


Beaton:部分单体应用的确需要继续保持,不应分解为微服务。话虽这么说,但微服务方法提供了很多优点。其中最显著的,就是对特定组件的工作机制具有更多的控制权,范围更加限定,并更易于做质量控制。整体而言,也更容易实现应用的质量控制。例如,更新一个搜索系统或支付系统时,不必对整个系统做全面的质量保证,只需聚焦于存在问题的组件即可。

 

还有一些不那么具体或许也不太明显的好处。面对手头的问题可以自由选择任何适用的技术或编程语言,这当然会令开发人员十分开心。相对于单体应用而言,开发人员可以在更大范围上匹配并组合使用技术。当然,这也意味着招聘适用的人才的难度降低了。如果有两种等效的工具或编程语言都可以解决特定的问题,那么适用开发人员的选取范围也会加倍。

 

Wrightson:我认为并没有所谓正确或错误的答案,不必非此即彼。事实上,公司可以采用适合自身的方式去运营自身的业务领域。问题又回到了组织因素上。是否可以快速部署,快速迭代?有时可能最终虽然开发出了一个看上去很漂亮的微服务系统,但却无法真正实现所追求的持续部署和持续交付。通常,人们会认为单体应用能更快完成开发。只要能掌握微服务与单体应用间的通信合约,并尽可能地使用消息传递,完全可以同时使用二者。我认为公司希望着手去采纳微服务时,需要非常谨慎。人们总是喜欢尝试新的事物。我看到了有人不假思索就立刻深深扎入微服务底层,导致团队最终耗费了 95%的时间纠缠于基础架构问题,只有 5%的时间用于开发功能,完全本末倒置了。


Reisz:Sam Newman 说过类似于“如可能,尽量构建单体应用”的观点。如果单体应用确实无法满足需求,那么再考虑微服务,没必要无缘无故地引入复杂性。Yan,对此你怎么看?


Cui:我们刚讨论了用适合的工具去完成工作。微服务之类的仅仅是工具,每种工具都需要权衡利弊。我们必须根据自身的需求去评判工具,确认是否值得为工具自身的不足而付出精力。对于一家想要实现快速推进的大型公司,当然希望能尽量降低一个团队暴露给其他团队的失败数量。微服务可以实现团队间的相互隔离,使得某个服务的宕机不会破坏整个企业的运行,因此非常适用于此类情况。否则,需要安排更多的人彼此相互独立地在系统上开展工作。

 

如果一个企业中仅有两三位系统运维人员,可能完全不应考虑微服务,至少在很长一段时期内无需考虑。所有的功能和部署,都可按单体应用的方式完成,除非企业需要进一步扩大规模。Nicky 一开始就说过,微服务仅支持解决组织问题,而不是技术问题。很多人通常认为,一旦需要扩展,就必须使用微服务。事实并非如此。以 Supercell 为例,该企业制作的每款游戏都有过亿的日活用户,一切仅用一个 JAR 软件包就能完成。全部仅是一项服务,只需一个 JAR,单体应用也是完全可扩展的。事实上,如果对扩展单体应用束手无策,那么微服务同样也难以扩展。只有在适当的场景下,微服务才是很好的工具。


Reisz:前面我们一直将微服务和单体应用绝对对立,但二者之间也存在一些中间方式,下面我们讨论一下模块化单体应用。例如,将多个 Jar 组合在一起,就构成了模块化单体应用。Yan,你如何看待模块化单体应用这样的中间方式?这是一个自然的发展阶段,还是应该直接转向微服务?


Cui:我曾构建过一些颇具规模的游戏应用后端,服务于大量的用户。这些后端都是单体应用,在核心层做了很好的模块化。如果采用模块化,可在多个不同级别上实现。无需对部署做切分,尤其适用于必须同时更改所有部署的情况。如果采用微服务,而每次部署更改都必须更改其它几个微服务,并需同时部署所有更改的微服务,那么这些微服务并不是真正相互独立的。我当然认为不必费力追求实现最纯粹的微服务,即绝对不存在共享的数据库,并且所有内容都是可独立部署和可扩展的。现实中,实现起来困难重重。

 

至少就目前而言,我个人是非常支持采用中间方式的。只要我们理解架构需要实现的特性,就应选择达成目标的最佳方法。也许决策能够一步到位,也许首先需要通过采用一些中间方式。我们必须做出务实的决定,否则就会陷于未知的无底洞中,无法从所做的技术决策中脱身。

 

Wrightson:我坚信实用性决定设计。有时我们会因为期望实现完美的工程化,而放弃了对实用性的追求。我看到大批比我年轻的大学毕业生,参与到如此复杂的分布式系统中,但并未意识到自己是在编写微服务。他们的方式是从编写小型模块化单体应用和微服务入手,然后在其变得愈发笨重之前做拆分。这是一种做法。另一方面,我也看到了一些采用模块化单体应用的做法。此类做法的问题在于项目的 Pull 请求会不断堆积,循环发布的周期可达一个月。最终,在工程化和实用性上都没有做好。


Reisz:正如 Yan 所说,使用正确的工具去做正确的事。假设你开发的应用需面对大量的短暂峰值流量,如何使用微服务应对可能仅持续数毫秒的扩容需求?


Beaton:这正是我们的最高追求。我们一直在为实现超低延迟而努力,我们的客户涉及所有以超低延迟为生命线的行业,从医疗保健、金融科技到贸易等。如何应对瞬息万变的行情?这需要可配置的数据平面,并且内置大量的智能。不仅能够确定系统是否已启动并正在运行,而且还需要确定系统中的应用代码是否能够及时给出可预测的响应,并逐步淘汰达不到上述要求的代码。如果考虑着手为系统匹配适用技术并混合使用,例如我们一直在讨论的单体应用与微服务的共存,这时的重中之重在于具有一个非常灵活的数据平面。也许可以引入某种涉及三方的模式,尽快推出一些可替代单体应用部分功能的微服务,诸如此类。


Reisz: Nicky,我们知道 Skyscanner 使用了数以千计的微服务,你们是如何解决延迟问题的?


Wrightson:我们在不同区域间保持平衡。一旦发现某各个区域出现了不好的苗头,我们就会迅速将业务切换到另一区域。我们正迁移到 Kubernetes,此前使用的是 AWS ECS。但达到规模的快速扩展并非易事。为高性能地运行负载,需要尽量确保系统整体上具有足够的弹性。我们也一直在对所有的内容做负载测试。旅游行业时常会出现一些意想不到的峰值,因此我们正推行的是一种独特的流量模式。该模式需要经常性地执行负载测试,尽管这完全无法令我工作在舒适区,但能使我们很好地把握问题所在。在其它区域,我们同样实现了弹性扩展。


Reisz:针对如何分解单体应用,我们看过一些演讲、论文和技术内容。这些资料宣称导致分解单体应用并转到微服务的原因,在于团队间的不协调或是推进速度等问题。但是我们也看到,诸如 Istio 又将 istiod 的架构转回到更类似于单体应用的事情发生。当出现哪些迹象的时候,我们应该回到单体应用上呢?


Wrightson:我曾任职英国《金融时报》,或许是因为并未正确采用 DDD,我们加载并堆砌了大量服务。我在其它地方也看到了类似情况,对现有架构做任何更改,都要付出艰辛努力。如果一个为 89 个服务提供支持的团队,面对不断推出的功能,并且每个功能都具有安全补丁,那么团队最终耗费在发布安全补丁上的时间,要远高于实际运营业务所用的时间。真实情况是,团队更多的时间是在维护代码,而不是开发新的功能。这时我认为应重新考虑单体应用的合理性,这在我看来就是最可能的迹象。

 

Beaton:我想再次强调一下 Nicky 前面说过的,导致错误做法中至关重要的一点是:新技术并不一定是更好的。通常并非如此,事实上几乎完全不会。但有时的确如此,总体而言采用某项技术不能仅仅因为它是新技术,而是需要有好的理由。如果这项技术能解决你的问题,当然,我赞同。如果仅仅因为该项技术做了更新换代就采用它,那这个理由不成立。


Reisz:最后总结一下。这里重提 Leif 的观点,我们选择采用某项技术,不能仅仅因为该项技术是新颖的。我们需要能实际解决问题。Nicky,还有什么补充吗?


Wrightson:对于是否采用微服务,我认为并不存在全有或全无的答案。总存在一些适用微服务的场景。当前好的一点是,采用微服务的试错代价低,至少可以去尝试一下是否能采用微服务的一些优点。但不要低估需在组织和文化上付出的成本。


Reisz:我非常认同 Yan 强调的“微服务解决的是组织问题,而不是技术问题”。Yan,再补充一下?


Cui:做事的目标,应始终是所期望的业务产出,而非技术本身。并非说技术不重要,而是说我们的目标是创造商业价值,而非为了技术而做事。以此为出发点,然后确定最好的做事方式,进而创造最大的商业价值。


嘉宾简介:


Wes Reisz 担任 VMware 平台架构师,是边缘计算平台 Section 的前技术副总。Reisz 曾主持 QCon 旧金山大会,在入职 VMware 之前曾任全球 QCon 所有英语大会的产品负责人、HP Enterprise Systems 的首席架构师,并在路易斯维尔大学担任兼职教授达 13 年。此外,Reisz 一直联合主持InfoQ Podcast

 

Leif Beaton 是位于爱尔兰 Cork 市的 NGINX 公司的高级解决方案架构师。他具有 20 年的 IT 部门工作经验,已形成包括安全性、网络、软硬件体系架构以及开发在内的多元化背景。Leif 日常工作是与 NGINX 客户互动,为将客户需求转换为现代架构提供帮助。

 

Yan Cui 是一位经验丰富的工程师,自 2009 年以来一直任职于 AWS。他是银行、电子商务、体育流媒体和移动游戏等多个行业的架构师和首席开发人员。Cui 以其在 Medium 上的无服务器文章以及个人博客theburningmonk.com而闻名。他的部分工作已纳入 AWS 无服务器优选架构白皮书(Serverless Well Architected)。他具备 C#、F#、Scala、Node.js 和 Erlang 等语言的专业编程能力。

 

Nicky Wrightson 具有丰富的大规模云原生架构交付经验。她曾任职《金融时报》,现就职于 Skyscanner。她专注于推动可操作性为大型分布式系统开发的头等问题。运维 Skyscanner 的超大规模数据平台,意味着在整体解决一整套新问题的同时,仍需要尽力实现可操作性、成本效益和可维护性。

 

原文链接: 

 

Reviewing the Microservices Architecture: Impacts, Operational Complexity, and Alternatives

2021-02-16 22:073522

评论 2 条评论

发布
用户头像
微服务本质是组织结构,不是技术架构,这文章再次说明了这一点
2021-02-22 22:46
回复
用户头像
不光是微服务,重构是另一个常见的场景,到最终你都想不起来为什么要重构了

我看到了有人不假思索就立刻深深扎入微服务底层,导致团队最终耗费了 95%的时间纠缠于基础架构问题,只有 5%的时间用于开发功能,完全本末倒置了。

2021-02-22 06:49
回复
没有更多了
发现更多内容

SAP UI5 框架的 manifest.json

汪子熙

SAP Fiori SAP UI5 ui5 7月月更

LeetCode-155. 最小栈(java)

bug菌

Leet Code 7月月更

CSS 基于文字的图片马赛克你见过吗

南城FE

CSS 前端 马赛克 7月月更

C#/VB.NET 给PDF文档添加文本/图像水印

在下毛毛雨

C# .net PDF 添加水印

Fedora/REHL 安装 semanage

HoneyMoose

前端知识链条中少不了的一环--Ajax

是乃德也是Ned

ajax 前端 7月月更

CRMEB 商城系统如何助力营销?

CRMEB

SAP Fiori 应用索引大全工具和 SAP Fiori Tools 的使用介绍

汪子熙

SAP Fiori SAP UI5 ui5 7月月更

在QWidget上实现窗口阻塞

小肉球

qt 7月月更

解构运算符的理解与运用

是乃德也是Ned

7月月更

看抖音直播Beyond演唱会有感

Empty

Android 查看签名

沃德

程序员 7月月更

中移动、蚂蚁、顺丰、兴盛优选技术专家,带你了解架构稳定性保障

博文视点Broadview

用Java写学生登陆认证系统

魏铁锤

三步就能在OpenHarmony中实现车牌识别

OpenHarmony开发者

OpenHarmony

devkit入门

乌龟哥哥

7月月更

C++|TCP 服务端中接收文件

中国好公民st

c++ TCP通信 7月月更

分布式不来点网关都说不过去

zxhtom

7月月更

基于STM32+华为云IOT设计的智能路灯

DS小龙哥

7月月更

node の SQLite

空城机

sqlite Node 7月月更

Python|数据结构——字典和集合

AXYZdong

Python 7月月更

【LeetCode】装满石头的背包的最大数量Java题解

Albert

LeetCode 7月月更

面试突击62:group by 有哪些注意事项?

王磊

Java MySQL 面试

Android 无限循环ViewPager滑动空白Bug及报错跳坑

芝麻粒儿

android 手机 7月月更

小程序在产业互联网中的作用

Geek_99967b

小程序 小程序容器

猿桌派第三季开播在即,打开出海浪潮下的开发者新视野

融云 RongCloud

使用标签模板解决用户恶意输入的问题

猪痞恶霸

前端 js ES6 7月月更

ORACLE进阶(四)表连接讲解

No Silver Bullet

oracle 7月月更 表连接

视频化全链路智能上云?一文详解什么是阿里云视频云「智能媒体生产」

阿里云CloudImagine

人工智能 媒体 音视频

面试突击63:MySQL 中如何去重?

王磊

Java MySQL 面试

基本磁盘与动态磁盘 RAID磁盘冗余阵列区分

Albert Edison

7月月更

审视微服务架构:影响因素、运维复杂性和替代方案 |InfoQ 圆桌_架构_Wesley Reisz_InfoQ精选文章