最新发布《数智时代的AI人才粮仓模型解读白皮书(2024版)》,立即领取! 了解详情
写点什么

如何查看我的订单 -REST 的流程 API 设计案例

  • 2011-08-31
  • 本文字数:10360 字

    阅读完需:约 34 分钟

这是一个关于订单的故事。四个月前,我在某刚刚上市不久的网上书城框框网购买了一包纸尿裤,因为尺寸不对,我选择了退货,由此开始了我糟糕的用户体验:首先是快递公司取回了纸尿裤却没有还款给我,接下来,在两个月的时间里,我不得一次又一次的向框框的客服投诉,客服很客气,她让我说出我的订单号然后说需要帮我查一查,两分钟后,她说需要和快递公司联系,稍后再打给我;随后的客服都很客气,但无一例外的,她们都不清楚我订单的处理情况,她们甚至很惊讶,快递公司还没有给你办吗?终于,我失去了耐心,我说,难道你们框框就只负责卖东西吗?送货外包了你们就不需要负责了吗?究竟是谁在处理我的订单,谁是责任人你们不知道吗?整个流程的处理状态你们清楚吗?客服支支吾吾半天,说,我们这就和快递公司联系。

框框的问题出在什么地方?顾客找不到这个订单的直接负责人,只能和客服打电话,而框框自己也失去该订单的状态了,于是只能再打快递公司的客服,这样问题透过组织结构层层传递下来,执行力可想而知。那为什么顾客找不到订单的直接负责人呢?因为顾客失去对自己订单的可视化了,我不知道我的订单现在处于一种什么样的状态,正处于哪一个步骤,谁是这个步骤的责任人,网站上没有,自然无从得知。想想我们的迭代和 show case,也正是一种对顾客的可视化手段。

在下面的章节中,我们将一起来应用 rest 的架构风格逐步搭建一个端到端的流程管理系统,看看如何解决这个问题,这个问题就是:看在上帝的份上,让我看看我的订单。为什么使用 rest?因为在下面的故事中,我们将会有大量的系统集成工作。为什么集成要用 rest?因为 REST 的实质是充分利用 HTTP 协议,将其作为一种应用协议,而不仅仅是传输协议,这样在 WEB 上做集成时能够最大程度上的达成一致,形成大家都能接受的约定,减少集成的工作量。另外,不可忽视的一点是,我们更多的是做数据的 mashup,这很适合 rest 的应用特点。不能使用 web service 吗?当然可以使用 web service,只是我们这里使用 rest 而已。

第一个需求,我想随时随地查看我的订单

好吧,很自然,这需要我们的程序支持移动设备。

图 1 昨天我们这样开发程序

似乎就在昨天,我们还在如图 1 一般开发程序,我们一边打开 firebug 进行调试,一边诅咒 IE 的不得好死,那时我们的关注点集中在前端,集中在如何使各个浏览器的行为和样式保持一致。而服务器端则是经典的 MVC 框架,直接将渲染好的 HTML 文档扔回客户端。

图 2 今天我们这样开发程序

然而到了今天,一切都发生了变化,我们开发的程序成了图 2 的样子,随着 IE 向标准的靠拢,HTML5 似乎有一统客户端之势,然而,移动互联网的兴起让我们编写程序重新变得复杂,昨天我只需要支持浏览器,现在则需要支持各种手机平台上的原生应用。自然,和昨天存在大量的 javascript 和 css 框架来抹平不同浏览器之间的差别一样,现在我们也有了 PhoneGap Titanium 来抹平不同平台之间的差别,尽管目前这些跨平台工具还存在用户体验不理想的问题,但最重要的变化来自两个方面:一是客户端重新变胖;二是服务器端由返回渲染完成的页面退化为返回数据,具体表现就是对客户端暴露出 API。

对移动设备的支持使得我可以在写代码时、吃饭时、睡觉时甚至坐马桶时随时查看我的订单(当然前提是你得有这些设备),而本文后续的架构变化也会围绕着图 2 逐渐演进。至于明天 HTML5 是否会最终代替原生应用,我觉得不会,不仅仅是技术原因(对设备硬件的使用)更重要的是商业原因,替代后的苹果变得和现在的微软 / 诺基亚一样尴尬,那么,也许后天?

好吧,既然是 REST 的 API 设计,我们来看看 REST 的架构风格。RESTful 架构遵从以下几个原则:

  • 请求是客户 - 服务器 式的,并很自然地使用一种基于拉的交互风格。
  • 请求是无状态的。每个从客户端到服务器端的请求都必须包含理解此请求所需的全部信息,而且不能利用服务器上所存储的上下文。
  • 客户端和服务器都遵从统一的接口。所有的资源都可通过 Web 的普通接口进行访问 —— HTTP 及 HTTP 方法:GET、POST、PUT 和 DELETE。
  • 客户端通过 URI 与命名的资源进行交互。
  • 将 http 状态码作为系统的状态码。

REST 的实质是充分利用 HTTP 协议,形成大家都能接受的约定。

看例子,我们以订单列表作为整个应用的调用入口,我们首先会 GET:http: //api.kuangkuang.com/orders,服务器返回以下的数据:

复制代码
<orders>
<link rel="list" media-type="application/xml" url="http://api.kuangkuang.com/orders"/>
<order>
<id>1000</id>
<state>draft</state>
<link rel="detail" media-type="application/xml" url="http://api.kuangkuang.com/order/1000"/>
</order>
<order>
<id>1001</id>
<state>completed</state>
<link rel="detail" media-type="application/xml" url="http://api.kuangkuang.com/order/1001"/>
</order>
</orders>

在返回的数据中,我们看到了:

复制代码
<link rel="detail" media-type="application/xml" url="http://api.kuangkuang.com/order/1001"/>

这个链接引导我们查看具体的订单信息,我们 GET:http: //api.kuangkuang.com/order/1000,服务器返回以下的数据:

复制代码
<order>
<link rel="detail" media-type="application/xml" url="http://api.kuangkuang.com/order/1000"/>
<content>
<id>1000</id>
<state>draft</state>
<cost>88.0</cost>
<link rel="edit" media-type="application/xml" url="http://api.kuangkuang.com/order/1000"/>
<link rel="delete" media-type="application/xml" url="http://api.kuangkuang.com/order/1000"/>
</content>
</order>

这里我们看到了两个链接:

复制代码
<link rel="edit" media-type="application/xml" url="http://api.kuangkuang.com/order/1000"/>
<link rel="delete" media-type="application/xml" url="http://api.kuangkuang.com/order/1000"/>

它们告诉我们可以对这个处于草拟状态的订单进行修改和删除。我们 GET 另外一个已完成的订单看看:http: //api.kuangkuang.com/order/1001,返回数据:

复制代码
<order>
<link rel="detail" media-type="application/xml" url="http://api.kuangkuang.com/order/1001"/>
<content>
<id>1001</id>
<state>completed</state>
<cost>66.0</cost>
</content>
</order>

没有更多的链接,这意味着我们只能对该订单进行查看。

在这些交互中,最重要的是服务器端返回数据本身已包含了对其他资源访问和对现在资源操作的线索。这样的好处在于客户端只需要一个入口地址,其他所有的操作地址全部由服务器端确定,这使得客户端与服务器端解耦,客户端不必再硬编码入 URI,能够各自独立的进化,服务器端负责数据、权限以及交互 URI 的确定,客户端重新回归展现数据的单一职责。

第二个需求,实现一个简单的流程

在上面的例子里,我们看到了订单的 CRUD 操作,但这并不是实际生活中的真实情况,整个订单的生命周期如下图所示:

图 3 订单的完整流程

在实现这个流程时,我们分为两步:第一步对订单进行资源建模;第二步通过工作流对订单进行流程的生命周期管理。

图 4 订单的资源模型

图 5 使用工作流管理订单的生命周期

工作流的职责在于管理订单的生命周期,在其生命周期的不同阶段,我们会有不同的参与者,对订单不同的操作权限。我们的系统架构演变成下面的样子:

图 6 使用工作流管理资源模型的生命周期

看例子,这次将我们视角转移到框框网这边,看看框框如何处理我们已提交的订单。我们使用 GET:http: //api.kuangkuang.com/orders?status=waiting-review 来获取所有需要审核的订单,服务器返回以下的数据:

复制代码
<orders>
<link rel="list" media-type="application/xml" url="http://api.kuangkuang.com/orders"/>
<order>
<id>1000</id>
<state>waiting review</state>
<link rel="detail" media-type="application/xml" url="http://api.kuangkuang.com/order/1000"/>
</order>
</orders>

我们查看具体的订单信息,我们 GET:http: //api.kuangkuang.com/order/1000,服务器判断出我们是框框网员工,返回以下的数据:

复制代码
<order>
<link rel="detail" media-type="application/xml" url="http://api.kuangkuang.com/order/1000"/>
<content>
<id>1000</id>
<cost>88.0</cost>
<state>waiting review</state>
<squence>
<activity rel="review" media-type="application/xml" url="http://api.kuangkuang.com/review/order/1000"/>
</squence>
</content>
</order>

注意到这两行:

复制代码
<state>waiting review</state>
<squence>
<activity rel="review" media-type="application/xml" url="http://api.kuangkuang.com/review/order/1000"/>
</squence>

这段信息是由工作流加入的,它告诉我们当前订单的状态为等待审核以及下一步需要我们来审核。那么,我们 PUT http://api.kuangkuang.com/review/order/1000 告诉服务器我们审核通过,服务器返回数据:

复制代码
<order>
<link rel="detail" media-type="application/xml" url="http://api.kuangkuang.com/order/1000"/>
<content>
<id>1000</id>
<cost>88.0</cost>
<state>waiting send</state>
<squence>
<activity rel="send" media-type="application/xml" url="http://api.kuangkuang.com/sent/order/1000"/>
</squence>
</content>
</order>

同样,工作流加入了这两行数据:

复制代码
<state>waiting send</state>
<squence>
<activity rel="send" media-type="application/xml" url="http://api.kuangkuang.com/sent/order/1000"/>
</squence>

告诉我们当前订单状态为等待送货,而下一步需要我们来完成这一步。此时,如果顾客来查看自己的订单会得到什么数据呢?服务器会判断出当前请求的用户是顾客,那么:

复制代码
<order>
<link rel="detail" media-type="application/xml" url="http://api.kuangkuang.com/order/1000"/>
<content>
<id>1000</id>
<cost>88.0</cost>
<state>waiting send</state>
</content>
</order>

顾客能够看到自己的订单正处于等待送货状态,而不会有下一步的动作。恩,很好,框框网订单处理速度很快,而这正是网购的主要竞争力之一。

等等,如果订单审核不通过呢,继续看流程:

图 7 增加一个网关

作为框框网的员工,我们 GET:http: //api.kuangkuang.com/order/1000,服务器判断出我们是框框网员工,返回以下的数据:

复制代码
<order>
<link rel="detail" media-type="application/xml" url="http://api.kuangkuang.com/order/1000"/>
<content>
<id>1000</id>
<cost>88.0</cost>
<state>waiting review</state>
<squence>
<xor rel="review" media-type="application/xml" url="http://api.kuangkuang.com/review/order/1000">
<choice>pass</choice>
<choice>reject</choice>
</xor>
</squence>
</content>
</order>

注意到这两行:

复制代码
<squence>
<xor rel="review" media-type="application/xml" url="http://api.kuangkuang.com/review/order/1000">
<choice>pass</choice>
<choice>reject</choice>
</xor>
</squence>

系统告诉我们这是一个排他网关,我们需要作出选择,在客户端与服务器端就 media-type 达成一致的情况下(即客户端能够充分理解服务器端返回的数据格式,这个数据格式被标准化),我们的代码不需要作出任何的修改,pass 和 reject 作为工作流变量被 put 到服务器,由工作流引擎进行处理,同样不会影响到订单的资源建模。

在这些交互中,最重要的是我们通过工作流实现了 REST 社区所呼吁的“将超媒体作为应用状态的引擎(hypermedia as the engine of application state)”。更简单地说,URI 代表了状态机里的状态迁移。我们通过标签让客户端是通过跟随链接的方式来操作订单状态机的状态转移。

第三个需求,框框将物流部分外包

在我们实际的生活中,电商们并不自己送货,他们将这部分工作外包给了物流公司。从成本的角度考虑,外包送货是最合适的选择。实际上,整个订单从提交到最后的完成情况还要稍微复杂一些,如下图所示:

图 8 订单从提交到完成的整个流程

从图中我们可以看出,这个流程跨越了两家公司,同时也涉及到了三个系统的集成,这三个系统分别是框框网的前台网站、框框网的后台负责仓储、进出货和物流的ERP 系统以及外包物流公司的ERP 系统。三个系统各自有自己的处理流程,整个订单的端到端处理流程由这三个系统的三个流程所共同完成:当我们在框框网提交订单时,一个消息被发送到框框的后台ERP 系统,这个消息触发一个货物的出库流程,当货物打包完毕出库时,一个消息被发送到物流公司的ERP 系统,同时触发物流公司的包裹配送流程,当我们给物流公司的配送员付款完毕时,对我们顾客来说框框的购物流程已经结束,然而整个流程依旧还要继续,配送员回到公司完款,一个消息被发送回框框的后台ERP,物流公司的包裹配送流程结束,框框网的这个订单这才处理完成。

在本文的一开始,我们提到了那个糟糕的退货故事,问题就在于当订单交由物流公司进行货物配送时,我们包括框框失去了对配送流程的可视化,物流公司的处理情况在我们的流程中黑盒了。如何解决这部分的问题呢,有两种处理方法:一是在框框网订单处理流程中加入捕获事件,正如图8 中所示的,当框框后台ERP 和物流公司ERP 对订单进行处理时,每到一个任务节点就给框框网的订单处理流程发送消息,由此给我们标示出订单的实时状态。

图 9 通过系统集成传递订单实时处理消息

现在,让我们来看看自己的订单会得到什么数据呢,GET http://api.kuangkuang.com/order/1000 框框网前台网站返回数据:

复制代码
<order>
<link rel="detail" media-type="application/xml" url="http://api.kuangkuang.com/order/1000"/>
<content>
<id>1000</id>
<cost>88.0</cost>
<state>waiting send</state>
<history>
<activity rel="submit" time="2011-6-28 14:00" participant="ronghao"/>
<activity rel="review" time="2011-6-28 14:30" participant="xinpeng"/>
<activity rel="delivery package" time="2011-6-28 15:00" participant="haorong"/>
<activity rel="warehouse" time="2011-6-28 17:00" participant="pengxin"/>
</history>
</content>
</order>

订单状态为等待物流公司送货,注意到这段数据:

复制代码
<history>
<activity rel="submit" time="2011-6-28 14:00" participant="ronghao"/>
<activity rel="review" time="2011-6-28 14:30" participant="xinpeng"/>
<activity rel="delivery package" time="2011-6-28 15:00" participant="haorong"/>
<activity rel="warehouse" time="2011-6-28 17:00" participant="pengxin"/>
</history>

工作流加入了订单处理的历史信息,从这段信息可以看出,我们要明天上午才能收到自己的货物了。

很不错不是吗,但是现实情况又是怎样呢。我们先来看看当当,当当如是说:订单状态变为“已发货”后,您可以登录“我的订单”,点击订单号进入订单详情页查看快递公司的联系方式,用订单号查询即可。我们再来看看卓越,卓越如是说:宅急送配送的订单:登录宅急送网站或拨打 020-82252310-802 查询;港中能达配送的订单:登录港中能达网站或拨打 020-86443920 查询。也就是说物流公司的配送流程状态并没有集成到网站中来,如下图所示:

图 10 网站与物流公司系统没有集成

为什么没有集成呢?第一是物流公司的客户往往不止框框一家,第二是框框也不会把鸡蛋放在一家物流公司的篮子里,这些给系统集成带来了难度,我们会突然发现有大量的系统需要集成,而系统间的集成之间存在太多的集成点,调试以及约定,这些都需要大量的工作和成本,系统们被紧紧的耦合在一起。

既然第一种实现方式使得我们即时查看我们订单状态成本太大,那我们看看第二种方法:使用一个统一的流程管理系统来管理整个端到端的流程。

图 11 使用统一的业务流程管理系统管理端到端流程

业务流程管理系统的职责有两个:一是由其管理起各个系统间的集成工作,这样避免了各个系统间的大量耦合;二是由其跟踪订单状态,完成订单在整个流程中的可视化。

图 12 框框订单的端到端流程

我们来看看具体的 api 调用,当我们在框框网站提交一个 ID 为 1000 的订单时,框框网站会发送一个消息到 http://api.kuangkuang-bpm.com/process-definition/1,由此触发整个的流程,启动一个新的流程实例。发送的消息:

复制代码
<order>
<link rel="detail" media-type="application/xml" url="http://api.kuangkuang.com/order/1000"/>
<content>
<id>1000</id>
<cost>88.0</cost>
</content>
</order>

业务流程管理系统给我们返回的消息:

复制代码
<process-instance>
<link rel="process-instance" media-type="application/xml" url="http://api.kuangkuang-bpm.com/process-instance/1"/>
<content>
<id>1</id>
<data>
<order-id>1000</order-id>
<order-cost>88.0</order-cost>
</data>
<definition>
<link rel="process-definition" media-type="application/xml" url="http://api.kuangkuang-bpm.com/process-definition/1"/>
</definition>
<current-activity>
<name> 订单提交 </name>
<link rel="activity-definition" media-type="application/xml" url="http://api.kuangkuang-bpm.com/activity-definition/1"/>
</current-activity>
</content>
</process-instance>

返回的消息中给我们指出了该订单所关联的流程实例 ID,当前正在执行的任务。流程系统创建流程实例后接下来继续往下执行,它发送一个消息到框框的后台 ERP 系统,触发后台 ERP 系统对订单的处理,同时告诉其访问当前流程实例的 URI。现在我们假设流程执行到物流公司的配送任务,我们在框框网站查看订单的即时状态系统会有哪些动作。第一步我们同样是 GET:http: //api.kuangkuang.com/order/1000,返回的数据:

复制代码
<order>
<link rel="detail" media-type="application/xml" url="http://api.kuangkuang.com/order/1000"/>
<link rel="process-instance" media-type="application/xml" url="http://api.kuangkuang-bpm.com/process-instance/1"/>
<content>
<id>1000</id>
<cost>88.0</cost>
</content>
</order>

返回的消息中多了一个访问流程实例的 URI,那么我们的客户端程序继续 GET: http://api.kuangkuang-bpm.com/process-instance/1,返回的数据:

复制代码
<process-instance>
<link rel="process-instance" media-type="application/xml" url="http://api.kuangkuang-bpm.com/process-instance/1"/>
<content>
<id>1</id>
<data>
<order-id>1000</order-id>
<order-cost>88.0</order-cost>
</data>
<definition>
<link rel="process-definition" media-type="application/xml" url="http://api.kuangkuang-bpm.com/process-definition/1"/>
</definition>
<current-activity>
<name> 物流配送 </name>
<type>sub-process</type>
<link rel="detail" media-type="application/xml" url="http://api.zjs-erp.com/order/2000"/>
<link rel="activity-definition" media-type="application/xml" url="http://api.kuangkuang-bpm.com/activity-definition/3"/>
</current-activity>
<history>
<activity name=" 提交订单 " type="start" time="2011-6-29 14:00" participant="ronghao"/>
<activity name=" 仓储出货 " type="sub-process" time="2011-6-29 15:30">
<link rel="detail" media-type="application/xml" url="http://api.kuangkuang-erp.com/order/1000"/>
</activity>
</history>
</content>
</process-instance>

我们看到当前正在执行的任务是物流配送,这是一个子流程任务,想具体了解这个子流程执行的情况,我们的客户端程序继续 GET: http://api.zjs-erp.com/order/2000,啊哈,框框将配送任务外包给了宅急送啊。

复制代码
<order>
<link rel="detail" media-type="application/xml" url="http://api.zjs-erp.com/order/2000"/>
<link rel="process-instance" media-type="application/xml" url="http://api.kuangkuang-bpm.com/process-instance/1"/>
<content>
<id>2000</id>
<cost>88.0</cost>
<current-activity>
<name> 配送 </name>
</current-activity>
<history>
<activity name=" 接受包裹配送单 " time="2011-6-29 15:40" participant="ronghao"/>
<activity name=" 包裹入库 " time="2011-6-29 15:45" participant="xinpeng"/>
</history>
</content>
</order>

好了,有了这三段数据,我们就可以清楚的看到订单所经过的各个环节以及当前的状态。从中可以看到两点:一是我们通过 kuangkuang-bpm.com 所提供的流程服务将各个系统进行了数据和流程的集成;二是各个被集成的系统需要实现 rest 的 api 以供我们的客户端程序进行数据的 mashup。

故事完了吗?还没有,京东 618 活动简报:收获订单 40 多万份,订购金额超 2 亿,已经发货一个多亿,尚有十几万份订单积压,大约三日左右可以处理完毕。不足之处:流量多次超过 4 个 G,服务器运行缓慢;图书备货量严重不足。与服务器相比,我更加关心如何及时将这几十万份订单处理完毕以及库存如何应对促销而产生的水平震荡(图书备货量严重不足,无法预测哪些书籍畅销哪些滞销,由此带来的订单迟迟无货),这显然不是一家物流公司可以完成的,需要多家物流公司一起消化这些订单,那么,问题来了,当我们 mashup 数据时,如何对这些物流公司返回的不同的数据格式进行处理?为每家公司实现一个适配器?NO!作为行业的老大,作为一家一流的企业,我们得制定标准。这时就需要标准化 media type 了,建立行业标准,企业级 rest 等于自定义、创造和标准化 media type。

第二个问题,kuangkuang-bpm.com 算是云服务否?是的,目前算作框框私有云,对自己和业务伙伴提供流程服务。

最后一个需求,框框要开放平台

最后一个需求有些跑题,但是那帮人现在都在搞什么开放平台,框框自然也是要跟风一下的。那么,开放平台都开放了个啥?

图 13 框框的开放平台

第一是用户的开放,这个通过网站实现,我们增加上百货、品牌频道,吸引商家入驻,这些入驻的商家能够分享我们的网站用户,同时,品牌的用户也能够被吸引到我们的网站上。同时,开心、人人、新浪微博、腾讯微博的账号能够直接登录我们网站,同时,我们网站的产品、评价也能分享到这些 SNS 网站里。

第二是服务的开放,这个通过流程实现,我们能给商家提供仓储、物流、投诉等一系列的服务。不使用我们的网站,自己有网站?没关系,只要数据格式满足我们定义的 media type 就行。

图 14 流程服务的开放

这算完了吗?不算!小马哥说,要开放就全面开放。我们将仓储环节和物流配送环节也开放出来,允许任何服务提供商使用我们的流程服务。

图 15 物流环节的开放

只要系统提供数据格式满足我们定义的 media type 就可以加入到我们的流程中来,分享我们的客户,为上帝提供服务。我们则从中提成。

所以,真正的开放是整个流程服务各个环节的开放,不仅仅对流程消费者,也对流程服务提供商,我们在其中起一个协调的作用。由此,我们将 kuangkuang-bpm.com 服务开放出来变成一个公有云服务,后来,我们惊讶的发现,kuangkuang-bpm.com 比 kuangkuang.com 更赚钱,因为它建立起了一个生态链。

小结

在上面的故事里,我们一步步看到了框框的系统架构演进,不难看出,架构演进的过程实际上系统不断分解的过程。

对移动设备的支持,使得我们将页面渲染逻辑从服务器端剥离出来交由客户端完成,服务器只负责提供数据。通过 rest 的超媒体特性,客户端和服务器端程序能够各自独立演进。

对订单流程的支持,使得我们采用工作流技术,将资源模型(订单)与其生命周期模型分离,分别交由原有系统和工作流系统管理,这样当某部分需求发生变化(例如增加一个审核不通过步骤),能够隔离变化,容易修改。

对端到端跨系统流程的支持,使得我们引入一个独立的业务流程管理系统,由此来协调各个系统间的集成工作,避免系统间的大量耦合。同时,我们看到定义和标准化一个大家都能理解的 media type 是如此的重要。

对开放平台的支持,使得我们将业务流程管理系统开放出来,作为公有云服务平台,同时流程中的各个环节能够开放出来,作为服务 / 应用接入和接出的接口,真正做到松耦合,由此看到开放平台的实质是流程服务的开放。

在同事陈金洲的《架构腐化之谜》中,文章最后提到解决这一问题的方法是:创建应用程序的生态环境,而非单一的项目。深得我心,而我们这里想表达的是,随着系统的演进,我们需要不断进行系统的分解,做到服务的独立演化。

至于 rest,它的作用就在于充分利用 HTTP 协议,将其作为一种应用协议,而不仅仅是传输协议,这样在 WEB 上做集成时能够最大程度上的达成一致,形成大家都能接受的约定,减少集成的工作量,对外接口一致,内部独立演化。


感谢张凯峰对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。

2011-08-31 00:0024889

评论

发布
暂无评论
发现更多内容

QCN9074 802.11ax 4x4 MU-MIMO 6GHz wifi6E//qcn9072 qcn9024 qcn64 wallys

wallysmeng

QCN9074 QCN9024 QCN9072 qcn9064

Wallys/MIMO/Industrial-grade/2x 2 900M high powerRadio /902-928Mhz/AR9223/ AR9582/AR9531/AR9344/

Cindy-wallys

云上智慧化办公,华为云桌面成为首选!

与时俱进的时代

从零开始,开启属于你的 RTE 漫游之旅!丨漫游指南 x 即将启航

声网

音视频

【附下载】政企数智办公平台研究报告,何以数智化?

融云 RongCloud

数智化 百幄

阿里云斩获2022全球分布式云大会两项大奖

云布道师

阿里云

华为云CDN节点扩增力度加大,构建全球智能边缘网络

爱尚科技

华为云CDN为芒果TV加速,打造丝滑“追剧观综”的观看体验

爱尚科技

神州云科打出“组合拳”,双轨超高可用架构引领信创高质量发展

云科通明湖

华为云CDN通过全站加速引领网站性能全面优化

爱尚科技

华为云CDN,为企业提供极致低时延用户体验

IT科技苏辞

连续7年领跑!在华为云桌面,藏了一盘数字办公的大棋

i生活i科技

用华为云桌面有多爽?问问设计师就知道了!

与时俱进的时代

论坛回顾|蚂蚁供应链安全建设实践

墨菲安全

软件供应链安全

【FAQ】在华为鸿蒙车机上集成华为帐号的常见问题总结

HMS Core

HMS Core

向云而行华为云桌面成数字办公首选

i生活i科技

华为云CDN,助力中小企业提升用户体验

IT科技苏辞

天花板级别ZooKeeper+Dubbo笔记,通俗易懂,颠覆认知

小小怪下士

Java zookeeper dubbo

什么样的技术,能让黄河开口说话?

白洞计划

在华为云桌面Workspace上,启泰智能工业设计效率翻倍

i生活i科技

为什么说华为云CDN更值得选择?

爱尚科技

南开大学团队采用全场景AI框架昇思MindSpore,打造“皮肤病大规模可信综合辅助诊断系统”

Geek_2d6073

哈啰出行高质量故障复盘法:“3+5+3”(附模板)

TakinTalks稳定性社区

不会PS怎么办?教你3种方法一键更换证件照背景色

互联网民工阿强

word ps 人像抠图 背景替换 证件照

数据价值深度挖掘,分析服务上线“探索”能力

HMS Core

HMS Core

华为云CDN助力企业用户体验全面优化,让企业“惠”加速

爱尚科技

华为云CDN,助力电商平台无惧流量洪峰

IT科技苏辞

DeFi模式NFT游戏开发技术

薇電13242772558

NFT链游

以英雄之名为S9总决赛助攻!虎牙直播and华为云CDN,team work才会赢

爱尚科技

AI 训练加速原理解析与工程实践分享

Baidu AICLOUD

异构计算 云原生AI 百度百舸

华为云CDN,为企业内容加速打开新局面!

IT科技苏辞

如何查看我的订单-REST的流程API设计案例_Java_荣浩_InfoQ精选文章