aci 一直这样了,有什么办法能日到女儿解决吗

五个解决方案让MongoDB拥有RDBMS的鲁棒性事务
发表于 16:14|
来源个人博客|
作者Antoine Girbal
摘要:在现实世界中,有很多场景都会用到事务这个特性,这也是NoSQL横行下传统RDBMS仍占统治地位的原因,近日MongoDB公司Antoine分享了5个解决方案,用以解决MongoDB文档间的事务问题。
【编者按】在分布式存储解决方案中谈事务一直是件很痛苦的事情,而事务也成了大部分NoSQL解决方案短板所在。近日,MongoDB公司的Antoine
Girbal在其个人博客上撰文,分享了在MongoDB文档间实施鲁棒可扩展事务的5个解决方案——同步字段、作业队列、二阶段提交、Log Reconciliation和版本控制。
免费订阅“CSDN大数据”微信公众号,实时了解最新的大数据进展!
CSDN大数据,专注大数据资讯、技术和经验的分享和讨论,提供Hadoop、Spark、Imapala、Storm、HBase、MongoDB、Solr、机器学习、智能算法等相关大数据观点,大数据技术,大数据平台,大数据实践,大数据产业资讯等服务。
以下为译文:
数据库支持数据块间的事务是有原因的。典型的场景是应用需要修改几个独立的比特时,如果只有一些而不是全部改变存储到了数据库,那么这就会出现不一致问题。因此ACID的概念是:
原子性:所有的改变要么都做了,要么都没做
一致性:数据保持一致性状态
隔离性:其它用户看不到部分改变
持久性:一旦向用户确认了事务,数据就处于安全的状态(通常存在硬盘上)
引入NoSQL数据库后,文档间ACID事务的支持通常就取消了。许多键/值存储仍有ACID,但它只适用于单个条目,取消ACID的主要原因是其可扩展限制。如果文档横跨几个服务器,事务将会很难实施以及性能。假设事务横跨数十个服务器,一些数据库是远程的,一些是不可靠的,想象下这会变的多难,多慢!
在单个文档等级上,MongoDB支持ACID。更准确的说,默认情况下是“ACI”,打开“j”WriteConcern选项后是ACID。Mongo有丰富的查询语言,横跨多个文档,因此人们一直在寻找多文档事务来使用他们的SQL代码。一个常见的办法是利用文档的性质:不需要很多行、很多关系,你可以将所有的东西嵌入到一个大文档中,Denormalization将带你回归事务。
这个技术解决了从一对一关系到一对多关系的很多事务问题。这也可能使应用更简单,数据库更快,所以这是双赢。不过当数据库必须分离时,该怎么办?
其实大部分应用都可以归结为:
原子性:实际上你希望所有的改变都完成
一致性:系统短时间不一致没关系,只要最终一致就行
隔离性:缺乏隔离性导致暂时的不一致,这并不理想,但是当今线上服务时代,很多用户对此都习惯了(如用户支持:“它要花几秒传输”)。
持久性:很重要,要支持。
这样问题就简化为鲁棒性、可扩性、最终一致性。
解决方案 1:字段同步
这种解决方案的使用场景最简单,最常见:文档间有些字段需要保持“同步”。例如,你有一个用户名为“John”的用户文档,文档代表John发表过的评论。如果用户可以更换用户名,那么这个改变需要发送给所有文档,即使进程中有应用错误或数据库错误。
为了实现这一目标,一个简单的办法是在主文档(这个情况下主文档是用户文档)中使用一个新字段(如“syncing”)。给“syncing”设置一个日期时间戳,记录用户文档的更新。db.user.update({ _id: userId }, { $set:{ syncing: currentTime }, { rest of updates ... } })
然后应用会修改所有的评论文档。结束后,需要移除标识:
db.user.update({ _id: userId }, {$unset: { syncing: 1 } })
现在假设进程中出现了问题:有些评论使用的是旧用户名。不过这些地方仍然会保留标识,所以应用知道哪些进程需要重新进行。因此,你需要后台进程在指定的时间(如1小时)检查“syncing”文件是否有未完成的地方。索引应设为“sparse”,这样只有实际设置的文档需要被索引,索引量就会比较小。db.user.ensureIndex({ syncing: 1 }, { sparse: true })
因此,系统通常可以保持事情在短时间内同步,在系统故障的情况下,时间周期为一个小时。如果时间不重要,当探测到“syncing”标志时,应用可以轻易修复文档。
解决方案2:作业队列
以上原理良好工作的前提是应用不需要很多内容,只依赖于通用进程(如:复制一个值)。一些事务需要执行特定变化,这些变化稍后很难识别。例如,用户文档包括一个朋友列表:
{ _id: userId, friends: [ userId1,userId2, ... ]}
现在A和B决定成为朋友:你需要把B添加到A的列表,也需要把A添加到B的列表。如果两者没有同时发生也没有关系(只要没有引发困扰)。针对这种情况和大多数事务问题的解决方案是使用作业队列,作业队列也存储在MongoDB。一个作业文档就像这样:
{ _id: jobId, ts: timeStamp, state: "TODO", type: "ADD_FRIEND", details: { users: [ userA, userB ]} }
或者是原始线程可以插入作业转发改变,或者是“worker”线程可以捡起工作。worker使用findAndModify()获取最原始的未加工的工作,findAndModify()是完全原子性的。操作中findAndModify()将工作标注为将被处理,同时也会表明worker
name、当前时间以便于追踪。{ state: 1, ts: 1 } 上的索引使这些调用很迅速。
db.job.findAndModify({ query: { state: "TODO" }, sort: { ts: 1 }, update: { $set: { state: "PROCESSING", worker: { name: "worker1", ts: startTime } } } })
之后worker以一种幂等的方式对双方用户文档进行修改,这些改变能应用很多次,并且有同样的效果——这很重要!为了这个目的,我们只需要使用一个$addToSet。一种更通用的替代方式是在查询端添加一个测试,检测修改是否执行了。
db.user.update({ _id: userA }, {$addToSet: { friends: userB } })
最后一步是删除作业或标注作业完成。再保留一段时间作业是一种安全的方式,唯一的缺点是随着时间的流逝,先前的索引会变得越来越大,尽管你可以在指定域{
undone: 1 } 上使用稀疏索引,并且根据实际情况修改查询。
db.job.update({ _id: jobId }, { $set: { state: "DONE" } })
如果进程在某一时刻故障了,作业仍然会在队列中,并标注为处理中。后台进程停止一段时间后会将作业标注为需要再次处理,然后作业会重新从头开始。
解决方案3 :二阶段提交
二阶段提交是一个众所周知的解决方案,很多分布式系统都采用了这种解决方案。MongoDB简化了这种解决方案的实施,因为灵活的框架,我们可以将所有需要执行的数据全都放入文档中。我几年前就写过关于这种方法的文章,你可以去MongoDB
Cookbook中查阅《
》(Perform Two Phase Commits)或者到MonoBD Manual中查阅《
》(Perform Two Phase Commits)。
解决方案4:&Log Reconciliation
很多财务系统常用的解决方案是&log reconciliation。这种解决方案将事务写作简单的日志,这避免了复杂性和潜在的故障。然后从上次良好状态以来所有的变化推测当前账户的状态。在极端情况下,你可以清空账户,然后通过实施从第一天以来所有的变化重建账户……这听起来很恐怖,但是可行。账户文件需要一个“缓存”来提高速度,还需要一个seqId,seqId计算如下:
{ _id: accountId, cache: { balance:10000, seqId: 115 } }
执行事务时,一个典型的财务系统会给事务写一个条目,会给与事务有关的账户写一个“账户变化”条目。这个方法需要进一步的写保证,“作业队列”解决方案可以实现写保证,事务中所有的作业在所有账户更改写入前都会保持不变。不过有了MongoDB,我们可以写一个包括事务和账户更改的文档。这个文档应该嵌入tx集合,如下:
{ _id: ObjectId, ts: timestamp , proc: "UNCOMMITTED", state: "VALID", changes: [ { account: 1234, type: "withdraw", value: -100, seqId: 801, cachedBal: null }, { account: 2345, type: "deposit", value: 100, seqId: 203, cachedBal: null } ] }
几个重点:
步骤:事务从“UNCOMMITTED” 状态开始,变为“COMMITTED”,此时涉及这些账户的所有先前事务也会变为 “COMMITTED”
,这表明这个事务也可以用作“anchor”来进行平衡计算。
状态:状态可能是 “VALID”、“CANCELLED等。如果不是VALID,即使是“COMMITTED”,平衡计算也会忽略事务。
seqId:这是账户的独有的seqId,这个seqId给账户更改一个确定的顺序。
cachedBal:账户的缓存平衡。如果事务时“COMMITTED”状态,那么缓存平衡(如果设置了)是一个有效值。
注意我们在 { changes.account: 1, changes.seqId: 1 }上使用一个独特的索引。reconciliation需要这个索引来提速,一个账户也不会有seqId副本。
关键是确保即使事务没有按顺序发生,缓存平衡也可以安全的计算/取消,还有就是事务状态可能改变。因此我们每个账户使用一个seqId,这确保了账户更改按确定的顺序发生,可以避免复杂的锁。在写事务前,应用首先通过简单地查询推断每个账户的下一个sqlId:
db.tx.find({ "changes.account": 1234 }, { "changes.$.seqId": 1 }).sort({ "changes.seqId": -1 }).limit(1)
然后每个sqlId都本地增长,然后写作事务的一部分。如果另一个线程也可能同时包括同样的seqId,独特的索引会确保写失败,线程会进行重试直到顺利完成任务。另一种方法是在账户集中保存一个当前seqId,然后用
findAndModify()获得下一个seqId,这通常会比较慢,除非你对账户有很多争用。注意如果因为某种原因事务没有写时,seqId可能会被跳过去,不过只有没有副本情况下才会成为。
下面我们谈谈reconciliation的基础。后台进程确保所有未提交的事务都会继续进行。只有所有账户的低seqId的事务都提交后一个事务才会被标注为提交。事务被标记为提交后就会变成不可变的。下面来谈谈好的方面:获得账户平衡。首先我们获得好的平衡,我们可以通过索引进行查询:
db.tx.find({ "changes.account": 1234, proc: "COMMITTED" }, { "changes.$": 1 }).sort({ "changes.seqId": -1 }).limit(1)
我们通过较大seqId的事务获得所有将发生的更改:
db.tx.find({ "changes.account": 1234, "changes.seqId": { $gt: lastGoodSeqId } }, { "changes.$": 1 }).sort({ "changes.seqId": 1 })
我们可以使用这些解决展示即将发生的损耗。如果我们只想简单的了解将来的平衡点在哪,我们可以让MongoDB收集所有变更展示总数:
db.tx.aggregate([{ $match: { "changes.account": 1234, "changes.seqId": { $gt: lastGoodSeqId }, state: "VALID" }},
{ $unwind: "changes" },
{ $match: { "account": 1234 }},
{ $group: { _id: "total", total: { $sum: "$value" } }}])
为了确保系统快速、计算量小,后台工作者要确保所有的事务都达到提交状态,平衡得到缓存。理想情况下一个事务是不可逆的,取而代之的是提交一个逆向事务来实施事务。不过只要所有的进一步事务状态和缓存都是正确设置的,取消是可行的。
解决方案5:版本控制
有时变得很复杂,以至于不能再JSON中表示,这些变更可能涉及很多有着复杂关系的文件(如树结构)。如果仅是部分变化(如破坏树)将会很混乱,这种情况下我们需要隔离。获取隔离性的一种方式是插入有着高版本号的新文档,取代对现有文档的更新。可以通过同日志和解同样的技术很容易、很安全的获得新版本号。通常{
itemId: 1, version: 1}上有一个独特的索引。
嵌入文档的应用从子文档开始,到主文档结束(如根节点)。当获取数据时,应用检查主文档的版本号,忽略高于版本号高于此版本号的文档。未完成的事务可以保持原状,可以忽略,可以清楚。
综上所述,我们提供了在文档间实施鲁棒可扩展事物的五种解决方案:
同步标志:最适用于仅从主文档复制数据的情况
作业队列:比较通用,适用于95%的情况,大部分系统至少需要一个作业队列
二阶段提交:这种技术确保每个实体都有为保持一致性状态所需的所有信息
Log Reconciliation:最鲁棒的技术,最适用于财务系统
版本控制:提供了隔离性,适用于复杂的结构
此外,我们还提到了很多次MongoDB最终将支持真正的原子性和文档间的隔离事务。这已经作为分区的一部分了,但目前还只是内部的……只有文档在同一分区时这一特性才可能实现,否则我们将回到不可扩展的SQL世界。
原文链接:
(编译/蔡仁君 责编/仲浩)
推荐阅读相关主题:
CSDN官方微信
扫描二维码,向CSDN吐槽
微信号:CSDNnews
相关热门文章拒绝访问 |
| 百度云加速
请打开cookies.
此网站 () 的管理员禁止了您的访问。原因是您的访问包含了非浏览器特征(3cbcb-ua98).
重新安装浏览器,或使用别的浏览器比特客户端
您的位置:
详解大数据
详解大数据
详解大数据
详解大数据
从ACI和OpFlex解读思科的SDN战略
关键字:ACI OpFlex SDN 技术前沿
  (作者简介:张卫峰,《深度解析SDN[注]》一书作者,盛科软件总监,和设计领域资深专家,有十几年的网络实践经验,对SDN、传统二三层、数据传输设备,从管理面到协议控制面一直到芯片转发面,都有着深刻的理解。)
  去年下半年,隆重推出了他们的秘密武器ACI(Application Centric Infrastructure),你说他是用ACI来对抗SDN也好,或者是拥抱SDN也好,反正他们自己宣称是既不同于 NSX,又不同于ONF OpenFlow的有思科特色的SDN。一时之间ACI成了一个大热的话题,众说纷纭,吸引了足够的眼球。
  最近思科又联合、Citrix、等向IETF提交了一个新的draft,名字叫OpFlex,号称是要取代OpenFlow(不知道是第三方的人这么讲的还是思科自己讲的)。但是跟ACI相比,算是低调多了,国内外讨论的人都比较少。甚至都看不到一篇正儿八经分析的文章。去年大张旗鼓推出硬的ACI,今年低调地推出软的OpFlex,两者之间有什么关系吗?
  OpFlex这个draft篇幅不长,一共只有21页,去掉一些标题,引用之类的之后,有效页数不超过16页。如果你之前从来都不了解ACI,来读这篇draft的话,我相信就算你是个资深网络技术人员也会觉得不知所云,不是看不明白每一句话,而是不明白它的目的是什么,要解决什么问题。但是如果之前比较深入地了解过ACI,再看这个draft就会明白了,OpFlex其实就是ACI内部的策略控制协议,现在思科把它给标准化了。所谓的用OpFlex替代OpenFlow云云,本质上还是用ACI这种SDN去跟OpenFlow以及Vmware的NSX之类架构去竞争。这里要顺便吐槽一下,OpFlex这个draft写得真是坑爹,一般RFC都会把背景情况,来龙去脉交代的一清二楚,而OpFlex这个draft很多背景都直接省略掉了,因为它是基于一个已经存在的东西,作者潜意识里面觉得背景已经很清楚了。
  这里简单分析一下ACI和OpFlex,我们先ACI的架构。
  ACI是把中的物理网络视为一个封闭的Fabric系统,对用户来说是个黑盒,用户不需要关心里面是运行了什么协议,如何连接。这个封闭的Fabric系统就是ACI(Application Centric Infrastructure)。那如何对这个封闭系统进行控制呢?这里的控制分为两部分,一部分是配置传统路由交换功能,使得ACI中的各个物理设备彼此可达,这个时候是靠传统方式去配置的,而且这种配置通常是一次性的,配好之后就不用再动了。另外一部分则是用户业务的策略控制(比如基于ACI部署[注]网络),这部分不是靠传统网管,而是靠一个SDN控制器,这个控制器也有个名字,叫APIC(Application Policy Infrastructure Controller)。
  APIC向上通过开放的Restful API跟各个第三方的系统或者工具进行集成,这些工具可以通过APIC来控制整个物理网络,应用策略,进而达到控制所有接入到该网络中的其它设备的目的。向下,APIC可以去控制整个ACI中的物理交换机(Spine/Leaf节点),不包括连接到这个物理网络中的其它设备(计算、、或者其它ACI之外的交换路由设备)。如上图中最下面那部分,外围的那一圈设备就不属于ACI的范畴,在APIC的架构中,这些接入到该Fabric中的用户设备,称为Endpoint(EP),相同类型的Endpoint组成一个Endpoint Group(EPG),比如所有的计算节点可以是一个EPG,或者所有具有相同Vlan的设备也可以是一个EPG,APIC并不规定EPG如何定义。
  APIC如何来控制ACI中的设备呢?它所采用的协议就是OpFlex(当然,不清楚思科是否把ACI到的所有细则都写到OpFlex标准中去了)。那接下来我们就分析一下OpFlex,看它如何应用于ACI。
  OpFlex协议中有AD(administrative Domain)、PR(Policy Repository)、PE(Policy Element)、EP(Endpoint)和 EPR(Endpoint Registry)这几个概念。它提到AD就是一个所有支持OpFlex的设备组成的管理域(该draft举例说,比如一个数据中心的物理Fabric网络就是一个AD);PR是在一个全局集中的地方,负责存放PEPR也是在一个全局集中的地方,负责管理Endpoint的注册,PE则位于ACI中每一台需要管理的设备上(是一个逻辑功能组件),它负责向上通告Endpoint的注册、变更,并应用来自APIC的Policy。而EP就是接入到该管理域的用户设备。显而易见,PR和EPR都(逻辑的或物理的)位于APIC上。
  上面这段文字可能比较抽象[注],我们换个更直白的说法,它的工作原理大概是这样的:用户在APIC上配置了一堆的Policy,比如“如果某个用户设备的是位于某地址段,就把它们划分到Compute Node EPG,统一应用策略A”;“如果某用户设备发出来的报文Vlan是10,就把它划分到Tenant1,统一应用策略B”等等。当ACI中的边界节点检测到某台用户设备接入的时候(具体检测方式并没有提到,估计是用户自定义,由APIC通过策略告知所有的边界设备),就把这个EP的主动上报,注意它并不是直接报给APIC,而是先报给上一级设备(Spine节点),如此层层上报,最终到达APIC。之所以要层层上报而不是直接报给APIC,是因为中间的设备也需要了解这些信息。举个例子说,如果检测到Vlan 10接入了,那就需要通知上联设备把相应的端口上使能Vlan 10。报给APIC之后,APIC分析接入的设备信息,决定它属于哪个策略组(EPG),然后把该EPG所对应的Policy下发给所有的相关设备,这些设备就在本地应用这些Policy。当然,也可能是APIC预先把这些Policy下发给这些设备了,这取决于应用场景。
  ACI带来的好处就是业务的部署,设备即插即用。你可以想象成你买了一个USB的设备,插到电脑上之后自动安装驱动和应用程序。当然ACI要做到这一点并不是那么容易,以上都是理论分析,实际网络要复杂得多,未必都能做到。
  OpFlex详细定义了各种消息格式(都是基于JSON),以及每种消息的通信主体。它试图把OpFlex尽量定义得更通用,不仅仅局限于数据中心云计算网络,而是任何适合需要自动化网络业务部署的场所。
  ACI和OpFlex技术本身的分析到此结束了,下面我们来分析一下思科的SDN思路。
  首先,ACI和OpFlex算不算是SDN?绝对算。当然我知道有人不同意,说明明他们的设备上都运行了传统协议,怎么能算SDN呢?但是你要明白,交换机上不运行路由协议,只是OpenFlow的要求(甚至就算是OpenFlow,都定义了output to normal这种动作,也就是说走完OpenFlow流程后再走传统转发流程),不是SDN的要求,SDN只是说软件参与到网络控制中。在ACI的这个架构中,最上层的应用和各种工具通过开放的Restful API来控制Controller,Controller则通过开放的OpFlex API来控制转发设备,用户通过Policy几乎可以控制一切。有人说,ACI设备之间的数据报文通信,还是要靠路由协议,用户无法直接控制。但问题是,用户为什么需要控制这个?能带来什么价值?SDN并不意味着用户需要控制一切,而是让用户可以控制他需要控制的东西。所以,尽管我不是思科的粉丝,但是我这个SDN的坚定支持者也必须承认ACI是SDN。
  其次,思科为什么要将OpFlex标准化?很显然,这是它整个战略中重要的一步。我不清楚他们是不是开始的时候就已经想好了这一步,但是很显然,ACI被很多人批评的一点就是它是一个完全私有、完全封闭的东西,不仅跟其他厂商设备不兼容,连跟自己之前的设备都不兼容,这是它最大的一个弊端。现在它希望通过开放并标准化它的控制协议来规避这个问题。虽然OpFlex这个draft上列了好几个公司的作者,但是明眼人很容易看出来,那几个公司的作者都是打酱油的,都属于思科的战友,来帮思科撑场子的,表示这个协议不是思科一家支持。
  最后,最关键的问题,思科的意图能达到吗?
  一方面,它感受到了SDN的威胁,想反击。但是另外一方面,它没办法阻挡这个潮流,只能也宣称拥抱SDN,但是拥抱的是最有利于自己的一种SDN实现方式。我个人的观点是,这个技术确实有很多亮点,但是要让其它设备商都跟进很难。第一,这个标准跟硬件设备能力强相关,思科为了这个ACI,专门开发了自己的芯片,别的厂商如果直接用商业芯片,某些功能做不到,所以就算支持OpFlex,功能上也不如思科强大。
  第二,OpFlex代表思科的利益,其他设备商都看得很清楚,谁会愿意跟进?OpFlex作者列表中的那些公司,都是搞软件的。
  第三,OpFlex这个协议从网络框架来说,相比较OpenFlow过于复杂了一些,小公司很难玩得转。而我们知道,技术从来都不是商业胜出的唯一要素,实现和部署的复杂性会影响它的推广,与Ethernet(以太网)的战争就是一个很好的例子。当然OpenFlow也未必就会赢,OpenFlow的问题也很多。
  归根结底一句话,ACI/OpFlex也许会叫好,但是极有可能不叫座……就像失败的FabricPath和QFabric一样?
[ 责任编辑:李桢君 ]
去年,手机江湖里的竞争格局还是…
甲骨文的云战略已经完成第一阶段…
软件信息化周刊
比特软件信息化周刊提供以数据库、操作系统和管理软件为重点的全面软件信息化产业热点、应用方案推荐、实用技巧分享等。以最新的软件资讯,最新的软件技巧,最新的软件与服务业内动态来为IT用户找到软捷径。
商务办公周刊
比特商务周刊是一个及行业资讯、深度分析、企业导购等为一体的综合性周刊。其中,与中国计量科学研究院合力打造的比特实验室可以为商业用户提供最权威的采购指南。是企业用户不可缺少的智选周刊!
比特网络周刊向企业网管员以及网络技术和产品使用者提供关于网络产业动态、技术热点、组网、建网、网络管理、网络运维等最新技术和实用技巧,帮助网管答疑解惑,成为网管好帮手。
服务器周刊
比特服务器周刊作为比特网的重点频道之一,主要关注x86服务器,RISC架构服务器以及高性能计算机行业的产品及发展动态。通过最独到的编辑观点和业界动态分析,让您第一时间了解服务器行业的趋势。
比特存储周刊长期以来,为读者提供企业存储领域高质量的原创内容,及时、全面的资讯、技术、方案以及案例文章,力求成为业界领先的存储媒体。比特存储周刊始终致力于用户的企业信息化建设、存储业务、数据保护与容灾构建以及数据管理部署等方面服务。
比特安全周刊通过专业的信息安全内容建设,为企业级用户打造最具商业价值的信息沟通平台,并为安全厂商提供多层面、多维度的媒体宣传手段。与其他同类网站信息安全内容相比,比特安全周刊运作模式更加独立,对信息安全界的动态新闻更新更快。
新闻中心热点推荐
新闻中心以独特视角精选一周内最具影响力的行业重大事件或圈内精彩故事,为企业级用户打造重点突出,可读性强,商业价值高的信息共享平台;同时为互联网、IT业界及通信厂商提供一条精准快捷,渗透力强,覆盖面广的媒体传播途径。
云计算周刊
比特云计算周刊关注云计算产业热点技术应用与趋势发展,全方位报道云计算领域最新动态。为用户与企业架设起沟通交流平台。包括IaaS、PaaS、SaaS各种不同的服务类型以及相关的安全与管理内容介绍。
CIO俱乐部周刊
比特CIO俱乐部周刊以大量高端CIO沙龙或专题研讨会以及对明星CIO的深入采访为依托,汇聚中国500强CIO的集体智慧。旨为中国杰出的CIO提供一个良好的互融互通 、促进交流的平台,并持续提供丰富的资讯和服务,探讨信息化建设,推动中国信息化发展引领CIO未来职业发展。
IT专家新闻邮件长期以来,以定向、分众、整合的商业模式,为企业IT专业人士以及IT系统采购决策者提供高质量的原创内容,包括IT新闻、评论、专家答疑、技巧和白皮书。此外,IT专家网还为读者提供包括咨询、社区、论坛、线下会议、读者沙龙等多种服务。
X周刊是一份IT人的技术娱乐周刊,给用户实时传递I最新T资讯、IT段子、技术技巧、畅销书籍,同时用户还能参与我们推荐的互动游戏,给广大的IT技术人士忙碌工作之余带来轻松休闲一刻。
微信扫一扫
关注Chinabyte

我要回帖

更多关于 谷歌不能用解决办法 的文章

 

随机推荐