Mono 准备好迎接黄金时段了吗? [关闭]

Posted

技术标签:

【中文标题】Mono 准备好迎接黄金时段了吗? [关闭]【英文标题】:Is Mono ready for prime time? [closed] 【发布时间】:2010-09-06 07:35:03 【问题描述】:

有没有人在大型或中型项目中使用过 Mono,开源 .NET 实现?我想知道它是否已准备好用于现实世界的生产环境。它是否稳定、快速、兼容、......足够使用?将项目移植到 Mono 运行时是否需要付出很多努力,或者它是否真的,真的足够兼容以获取并运行已为 Microsoft 运行时编写的代码?

【问题讨论】:

Mindtouch.com 在 Debian 上的 Mono 上使用 C# 来构建一个非常强大的 wiki 平台。去看看他们。您甚至可以下载一个预配置的 VM,以便轻松设置和使用。 我发现单声道的最佳用途是能够说,“如果微软做 XXX,我们可以在 unix 上转向单声道......” 我认为这个问题值得重新提出,考虑到自这个问题以来发生的一切变化。 【参考方案1】:

嗯,单声道很棒,但据我所知,它不稳定。它可以工作,但是当你给单声道处理一个严肃的工作时就会出错。

TL;DR - 如果您这样做,请勿使用单声道:

在多线程环境中使用 AppDomain(程序集加载\卸载) 无法维持“让它失败”模型 在进程运行期间偶尔会遇到重负载事件

所以,事实。

我们在 RHEL5、Ubuntu 上使用 mono-2.6.7 (.net v 3.5),在我看来,它是 Novell 构建的最稳定的版本。它在卸载 AppDomains(段错误)方面存在问题,但是,它非常罕见地失败了,到目前为止,这是可以接受的(我们)。

好的。但是如果你想使用 .net 4.0 的功能,你必须切换到 2.10.x 或 3.x 版本,这就是问题所在。

相比2.6.7,新版本简直不能用。我编写了一个简单的压力测试应用程序来测试单声道安装。

这里有使用说明:https://github.com/head-thrash/stress_test_mono

它使用线程池工作线程。 Worker 将 dll 加载到 AppDomain 并尝试做一些数学工作。有些工作是多线程的,有些是单线程的。几乎所有的工作都是 CPU 密集型的,尽管从磁盘读取一些文件。

结果不是很好。事实上,对于 3.0.12 版本:

sgen GC 段错误几乎立即处理 带有 boehm 的单声道寿命更长(从 2 到 5 小时),但最终会出现段错误

如上所述,sgen gc 只是不起作用(从源代码构建的单声道):

* Assertion: should not be reached at sgen-scan-object.h:111

Stacktrace:


Native stacktrace:

    mono() [0x4ab0ad]
    /lib/x86_64-linux-gnu/libpthread.so.0(+0xfcb0) [0x2b61ea830cb0]
    /lib/x86_64-linux-gnu/libc.so.6(gsignal+0x35) [0x2b61eaa74425]
    /lib/x86_64-linux-gnu/libc.so.6(abort+0x17b) [0x2b61eaa77b8b]
    mono() [0x62b49d]
    mono() [0x62b5d6]
    mono() [0x5d4f84]
    mono() [0x5cb0af]
    mono() [0x5cb2cc]
    mono() [0x5cccfd]
    mono() [0x5cd944]
    mono() [0x5d12b6]
    mono(mono_gc_collect+0x28) [0x5d16f8]
    mono(mono_domain_finalize+0x7c) [0x59fb1c]
    mono() [0x596ef0]
    mono() [0x616f13]
    mono() [0x626ee0]
    /lib/x86_64-linux-gnu/libpthread.so.0(+0x7e9a) [0x2b61ea828e9a]
    /lib/x86_64-linux-gnu/libc.so.6(clone+0x6d) [0x2b61eab31ccd]

至于 boehm segfauls - 例如(Ubuntu 13.04,从源代码构建的单声道):

mono: mini-amd64.c:492: amd64_patch: Assertion `0' failed.
Stacktrace:
at <unknown> <0xffffffff>
at System.Collections.Generic.Dictionary`2.Init (int,System.Collections.Generic.IEqualityComparer`1<TKey>) [0x00012] in /home/bkmz/my/mono/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:264
at System.Collections.Generic.Dictionary`2..ctor () [0x00006] in /home/bkmz/my/mono/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:222
at System.Security.Cryptography.CryptoConfig/CryptoHandler..ctor (System.Collections.Generic.IDictionary`2<string, System.Type>,System.Collections.Generic.IDictionary`2<string, string>) [0x00014] in /home/bkmz/my/mono/mcs/class/corlib/System.Security.Cryptography/Crypto
Config.cs:582
at System.Security.Cryptography.CryptoConfig.LoadConfig (string,System.Collections.Generic.IDictionary`2<string, System.Type>,System.Collections.Generic.IDictionary`2<string, string>) [0x00013] in /home/bkmz/my/mono/mcs/class/corlib/System.Security.Cryptography/CryptoCo
nfig.cs:473
at System.Security.Cryptography.CryptoConfig.Initialize () [0x00697] in /home/bkmz/my/mono/mcs/class/corlib/System.Security.Cryptography/CryptoConfig.cs:457
at System.Security.Cryptography.CryptoConfig.CreateFromName (string,object[]) [0x00027] in /home/bkmz/my/mono/mcs/class/corlib/System.Security.Cryptography/CryptoConfig.cs:495
at System.Security.Cryptography.CryptoConfig.CreateFromName (string) [0x00000] in /home/bkmz/my/mono/mcs/class/corlib/System.Security.Cryptography/CryptoConfig.cs:484
at System.Security.Cryptography.RandomNumberGenerator.Create (string) [0x00000] in /home/bkmz/my/mono/mcs/class/corlib/System.Security.Cryptography/RandomNumberGenerator.cs:59
at System.Security.Cryptography.RandomNumberGenerator.Create () [0x00000] in /home/bkmz/my/mono/mcs/class/corlib/System.Security.Cryptography/RandomNumberGenerator.cs:53
at System.Guid.NewGuid () [0x0001e] in /home/bkmz/my/mono/mcs/class/corlib/System/Guid.cs:492

或者(RHEL5,单声道取自这里的rpm ftp://ftp.pbone.net/mirror/ftp5.gwdg.de/pub/opensuse/repositories/home%3A/vmas%3A/mono-centos5)

Assertion at mini.c:3783, condition `code' not met
Stacktrace:
at <unknown> <0xffffffff>
at System.IO.StreamReader.ReadBuffer () [0x00012] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.IO/StreamReader.cs:394
at System.IO.StreamReader.Peek () [0x00006] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.IO/StreamReader.cs:429
at Mono.Xml.SmallXmlParser.Peek () [0x00000] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/Mono.Xml/SmallXmlParser.cs:271
at Mono.Xml.SmallXmlParser.Parse (System.IO.TextReader,Mono.Xml.SmallXmlParser/IContentHandler) [0x00020] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/Mono.Xml/SmallXmlParser.cs:346
at System.Security.Cryptography.CryptoConfig.LoadConfig (string,System.Collections.Generic.IDictionary`2<string, System.Type>,System.Collections.Generic.IDictionary`2<string, string>) [0x00021] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.Security.Cryptog
raphy/CryptoConfig.cs:475
at System.Security.Cryptography.CryptoConfig.Initialize () [0x00697] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.Security.Cryptography/CryptoConfig.cs:457
at System.Security.Cryptography.CryptoConfig.CreateFromName (string,object[]) [0x00027] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.Security.Cryptography/CryptoConfig.cs:495
at System.Security.Cryptography.CryptoConfig.CreateFromName (string) [0x00000] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.Security.Cryptography/CryptoConfig.cs:484
at System.Security.Cryptography.RandomNumberGenerator.Create (string) [0x00000] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.Security.Cryptography/RandomNumberGenerator.cs:59
at System.Security.Cryptography.RandomNumberGenerator.Create () [0x00000] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.Security.Cryptography/RandomNumberGenerator.cs:53
at System.Guid.NewGuid () [0x0001e] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System/Guid.cs:483
at System.Runtime.Remoting.RemotingServices.NewUri () [0x00020] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.Runtime.Remoting/RemotingServices.cs:356
at System.Runtime.Remoting.RemotingServices.Marshal (System.MarshalByRefObject,string,System.Type) [0x000ba] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.Runtime.Remoting/RemotingServices.cs:329
at System.AppDomain.GetMarshalledDomainObjRef () [0x00000] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System/AppDomain.cs:1363

这两种故障都以某种方式与 AppDomains 逻辑有关,因此,您应该在单声道中远离它们。

顺便说一句,经过测试的程序在 MS .NET 4.5 环境中的 Windows 机器上运行了 24 小时,没有任何失败。

所以,总而言之,我想说 - 谨慎使用单声道。乍一看它有效,但任何时候都很容易失败。你会留下一堆核心转储和对开源项目的重大信心损失。

【讨论】:

您是否尝试在Xamarin bugzilla 中提交错误? bugzilla.xamarin.com/show_bug.cgi?id=15064 bugzilla.xamarin.com/show_bug.cgi?id=16493【参考方案2】:

它涵盖了 .NET 4.0 的广泛覆盖范围,甚至包括 .NET 4.5 API 的一些功能,但由于 API 已被弃用、正在创建新的替代方案或范围太大。以下 API 在 Mono 中不可用:

Windows Presentation Foundation Windows Workflow Foundation(两个版本都不是) 实体框架 标准 Web 服务堆栈的 WSE1/WSE2“附加组件”

此外,我们的 WCF 实现仅限于 Silverlight 支持的内容。

检查您的特定项目的最简单方法是运行Mono Migration Analyzer (MoMA)。好处是它将通知 Mono 团队有关阻止您使用 Mono(如果有)的问题,从而让他们优先考虑他们的工作。

我最近在 SubSonic 上运行 MoMA 并发现只有一个问题 - Nullable 类型的奇怪使用。那是一个很大的代码库,所以那里的覆盖率令人印象深刻。

Mono 正在several commercial as well as open source products 中积极使用。它已在一些大型应用程序中使用,例如 Wikipedia and the Mozilla Developer Center,并已用于 Sansa MP3 播放器等嵌入式应用程序,并为数千个已发布的游戏提供支持。

在语言级别,the Mono compiler is fully compliant with the C# 5.0 language specification。

【讨论】:

【参考方案3】:

在桌面端,如果您承诺使用 GTK#,Mono 会非常出色。 Windows.Forms 实现仍然存在一些问题(例如,TrayIcon 不起作用),但它已经走了很长一段路。此外,GTK# 是比 Windows 窗体更好的工具包。

在 web 端,Mono 已经实现了足够多的 ASP.NET 来完美地运行大多数网站。这里的困难是找到一个在 apache 上安装了 mod_mono 的主机,或者如果你对你的主机有 shell 访问权限,你就自己做。

无论哪种方式,Mono 都很棒,而且很稳定。

创建跨平台程序时要记住的关键事项:

使用 GTK# 而不是 Windows.Forms 确保正确区分文件名 使用Path.Separator代替硬编码"\",也使用Environment.NewLine代替"\n"。 不要对 Win32 API 使用任何 P/Invoked 调用。 不要使用 Windows 注册表。

【讨论】:

Path.Separator 是个好建议,除了 OS X 上的 Mono 有 ':',而不是 '/'!哈!那是旧的 Mac OS ( 我不关心 Environment.NewLine 或 Path.Separator,只需使用 / 和 \n。当前流行的每个桌面系统(除非我遗漏了一些)都使用 / 和 \n。 Windows 更喜欢 \ 和 \r\n,但会很乐意使用 unix 的。【参考方案4】:

不,单声道还没有准备好进行严肃的工作。我使用 F# 在 Windows 上编写了一些程序并在 Mono 上运行它们。这些程序非常密集地使用磁盘、内存和cpu。我看到单声道库(托管代码)中的崩溃、本机代码中的崩溃以及虚拟机中的崩溃。当单声道工作时,程序至少比 Windows 中的 .Net 慢两倍,并且使用更多的内存。远离单声道进行严肃的工作。

【讨论】:

这是作为事实呈现的轶事证据,在我看来是 FUD 实际上 ASP.NET 在 nginx/fast-cgi 下比在 IIS 上运行得更快。这完全取决于框架的哪一部分被移植/经过良好测试:mono-project.com/Compatibility。必须同意@firegrass。 这是一个人的亲身经历。除了以“我认为”作为前缀之外,这是对本次讨论的有效贡献。【参考方案5】:

有几个场景需要考虑: (a) 如果您正在移植现有应用程序并想知道 Mono 是否足以胜任这项任务; (b) 你开始写一些新的代码,你想知道 Mono 是否足够成熟。

对于第一种情况,您可以使用Mono Migration Analyzer tool (Moma) 来评估您的应用程序与在 Mono 上运行的距离。如果评估结果非常好,您应该开始测试和 QA 并准备发货。

如果您的评估返回报告突出显示 Mono 中缺失或语义存在显着差异的功能,您将必须评估代码是否可以调整、重写,或者在最坏的情况下,您的应用程序是否可以在功能减少的情况下工作.

根据我们基于用户提交的 Moma 统计数据(来自内存),大约 50% 的应用程序开箱即用,大约 25% 需要大约一周的工作量(重构、调整)另外 15% 需要对重做代码块的认真承诺,其余的只是不值得麻烦移植,因为它们与 Win32 紧密相关。那时,您要么从零开始,要么业务决策将推动使您的代码可移植的努力,但我们正在谈论几个月的工作(至少从我们拥有的报告来看)。

如果您从头开始,情况会简单得多,因为您只会使用 Mono 中存在的 API。只要您继续使用受支持的堆栈(几乎是 .NET 2.0,加上 3.5 中的所有核心升级,包括 LINQ 和 System.Core,以及任何 Mono 跨平台 API),您就可以了。

有时您可能会遇到 Mono 中的错误或限制,您可能必须解决它们,但这与任何其他系统没有什么不同。

至于可移植性:ASP.NET 应用程序更容易移植,因为它们几乎不依赖 Win32,您甚至可以使用 SQL 服务器或其他流行的数据库(有很多与 Mono 捆绑的数据库提供程序)。

Windows.Forms 移植有时会比较棘手,因为开发人员喜欢逃离 .NET 沙箱并 P/Invoke 他们的大脑来配置一些有用的东西,比如改变光标闪烁率,在 wParam 中以 BCD 形式编码的两个贝塞尔点表示.或者一些类似的垃圾。

【讨论】:

这家伙以为他是谁?单声道的创造者???!!! ...哦等等.. @Drahcir:LINQ 在 Mono 上工作。它不是特定于 Windows 的。所以继续尝试 Linux。 " 与更改光标闪烁率一样有用,表示为 wParam 中以 BCD 形式编码的两个贝塞尔点"lol 非常感谢 Mono... miguel,很高兴能得到这篇文章的更新;-)【参考方案6】:

如果你想使用 WPF,那你倒霉了 Mono 目前还没有实施它的计划。

http://www.mono-project.com/WPF

【讨论】:

那真是太糟糕了。 WPF 是一个不错的 UI 工具包。 @JP Richardson 我明白你的意思了 - 编程时很好 - 但如果它是从概念构建并打算成为一个非便携式工具包,我不会称它为“体面的” . @Wyatt8740 我的评论是 10 年前写的。 @JP Richardson 哈哈,我的错。但即使在十年前,它仍然不适合便携。【参考方案7】:

我个人在黄金时段环境中使用 Mono。 我运行处理千兆字节 udp/tcp 数据处理相关任务的单声道服务器,我非常高兴。

有一些特殊性,最烦人的事情之一是,由于 Mono 的当前状态,您不能只“构建”您的 msbuild 文件:

MonoDevelop(IDE)有一些部分 msbuild 支持,但基本上会在简单的 hello-world(自定义构建任务、动态“属性”如 $(SolutionDir)、真正的配置名称之外的任何“真实”构建配置上失败)一些死胡同) xbuild 应该是 mono-supplied-msbuild-fully-compatible-build-system 更可怕,所以从命令行构建实际上比使用 GUI 更糟糕,对于 Linux 环境来说,这是一种非常“非正统”的联盟状态......

一旦/在实际构建你的东西​​时,你可能会看到一些荒野,即使是应该支持的代码,例如:

编译器对某些结构感到厌烦 以及某些更高级/新的 .NET 类向您抛出意想不到的废话(XLinq 有人吗?) 一些不成熟的运行时“功能”(x64 上的 3GB 堆限制... WTF!)

但是他说一般来说事情开始很快,解决方案/变通办法很丰富

一旦你克服了最初的障碍,我的经验就是单声道 ROCKS,并且每次迭代都会变得更好

我的服务器使用单声道运行,每天处理 300GB 的数据,有大量的 p/invokes,一般来说,我做了很多工作,并保持 5-6 个月的正常运行,即使使用“前沿”单声道也是如此。

希望这会有所帮助。

【讨论】:

你能告诉我(如果可以的话)你在说什么网站吗?【参考方案8】:

只需查看 www.plasticscm.com。一切(客户端、服务器、GUI、合并工具)都是用单声道编写的。

【讨论】:

【参考方案9】:

不幸的是,对于我们正在构建的应用程序类型 Mono 似乎还没有准备好投入生产。我们对其整体印象深刻,对其在 Windows 和 EC2 机器上的性能印象深刻,但是,我们的程序在 Windows 和 linux 上都因垃圾收集错误而崩溃。

错误消息是:“GC 中的致命错误:堆部分过多”,这是其他人以稍微不同的方式遇到问题的链接:

http://bugzilla.novell.com/show_bug.cgi?id=435906

我们在 Mono 中运行的第一段代码是我们开发的一个简单的编程挑战......该代码将大约 10mb 的数据加载到一些数据结构(例如 HashSets)中,然后对数据运行 10 次查询。我们运行了 100 次查询,以便对它们进行计时并获得平均值。

代码在 Windows 上的第 55 个查询附近崩溃。在 linux 上它可以工作,但是一旦我们转移到更大的数据集,它也会崩溃。

这段代码非常简单,例如将一些数据放入 HashSets,然后查询那些 HashSets 等,所有原生 c#,没有不安全,没有 API 调用。在 Microsoft CLR 上它永远不会崩溃,并且可以在庞大的数据集上运行 1000 次就好了。

我们的一个人向 Miguel 发送了电子邮件,并附上了导致问题的代码,但尚未回复。 :(

似乎许多其他人也遇到了这个问题而没有解决方案 - 有人建议使用不同的 GC 设置重新编译 Mono 的一种解决方案,但这似乎只是增加了崩溃之前的阈值。

【讨论】:

Bugzilla 是报告错误的地方:米格尔非常忙,没有人能跟上每个人向他发送个人错误报告的速度。如果您无法发布示例代码,您仍应在 bugzilla 中报告该问题,并在此处注明您已将示例发送给 Miguel 或我 (lupus@ximian.com)。【参考方案10】:

是的,它确实是(如果你小心的话) 我们在 Ra-Ajax 中支持 Mono(在 http://ra-ajax.org 找到的 Ajax 库)并且我们几乎没有遇到任何问题。您需要小心 .Net 中的一些“最疯狂的事情”,例如 WSE 等,而且您现有的一些项目可能不会 100% 兼容 Mono,但是如果您在开发过程中测试它们,新项目将主要是与 Mono 兼容而不会出现问题。通过使用 Mono 来支持 Linux 等的好处真的很酷;)

我认为支持 Mono 的很大一部分秘诀是从一开始就使用正确的工具,例如ActiveRecord、log4net、ra-ajax 等...

【讨论】:

【参考方案11】:

已接受答案的建议现在有点过时了。

Windows 窗体的实现现在非常好。 (请参阅 Paint-Mono 了解 Paint.net 的一个端口,这是一个相当复杂的 Windows 窗体应用程序。只需要一个用于某些 P-Invoke 和不受支持的系统调用的仿真层。 Path.Combine 和 Path.Seperator 用于连接路径和文件名。 windows 注册表没问题,只要您仅将它用于存储和检索应用程序中的数据(即,您无法从中获取有关 Windows 的任何信息,因为它基本上是 Mono 应用程序的注册表)。

【讨论】:

+1 跟进...不过,看起来此页面可能又过时了。 是的,以这些人的工作速度,两年是 Mono 的一生。【参考方案12】:

正如其他人所建议的那样,MoMA 是一个很好的工具。当今最大的不兼容来源是 DllImport(或 P/Invoke)到 Win32 库的应用程序。有些程序集没有实现,但其中大多数是仅限 Windows 的,在 Linux 上确实没有意义。我认为可以相当肯定地说,大多数 ASP.NET 应用程序只需进行有限的修改即可在 Mono 上运行。

(披露:我为 Mono 本身以及在其上运行的编写应用程序做出了贡献。)

【讨论】:

这就是 Mono Migration Analyzer,任何人都想知道这与现代艺术博物馆有什么关系。【参考方案13】:

我们一直在将它用于需要在 Linux 上运行但重用我们在托管 C++ 中构建的一些 .NET 库的工作项目。我对它的效果感到非常惊讶。我们的主要可执行文件是用 C# 编写的,我们可以毫无问题地引用我们的托管 C++ 二进制文件。 Windows 和 Linux 的 C# 代码的唯一区别是 RS232 串口代码。

我能想到的唯一大问题发生在大约一个月前。 Linux 版本存在 Windows 版本中未发现的内存泄漏。在进行了一些手动调试(Linux 上 Mono 的基本分析器并没有太大帮助)之后,我们能够将问题缩小到特定的代码块。我们最终修补了一个解决方法,但我仍然需要花一些时间回去找出泄漏的根本原因。

【讨论】:

那么,如何为同时处理这两种情况的串口编写代码呢? CLR/Mono 的全部意义在于独立于平台,对吗?这是在配置文件中完成的吗?【参考方案14】:

我想,如果您的应用程序包含一些 3rd 方组件,您可能会被塞满。我怀疑很多供应商会在开发时考虑到 Mono

示例:http://community.devexpress.com/forums/p/55085/185853.aspx

【讨论】:

【参考方案15】:

您知道 Mono 2.0 预览版对 Windows Forms 2.0 的支持有多好吗?

从我玩过的一点点来看,它似乎相对完整并且几乎可以使用。它只是在某些地方看起来不太对劲,而且总体上还是有点受欢迎或遗漏。让我感到惊讶的是,它和我们的某些表格一样有效,尽管老实说。

【讨论】:

【参考方案16】:

这实际上取决于您在 .NET 框架中使用的命名空间和类。我有兴趣将我的一个 Windows 服务转换为在我的电子邮件服务器 Suse 上运行,但是我们遇到了几个尚未完全实现的 API 的硬性障碍。 Mono 网站上某处有一张图表,列出了所有课程及其完成程度。如果您的应用程序被覆盖,那就去吧。

当然,与任何其他应用程序一样,在做出完全承诺之前进行原型设计和测试。

我们遇到的另一个问题是许可软件:如果您正在引用其他人的 DLL,您无法编写代码来解决隐藏在该程序集中的不兼容性。

【讨论】:

【参考方案17】:

在许多情况下,您可以使用现有代码并在 Mono 上运行它,尤其是在您移植 ASP.NET 应用程序时。

在某些情况下,您可能需要全新的代码部分才能使其工作。例如,如果您使用 System.Windows.Forms,应用程序将无法在未经修改的情况下运行。同样,如果您使用任何特定于 Windows 的代码(例如,注册表访问代码)。但我认为最严重的违规者是 UI 代码。这在 Macintosh 系统上尤其糟糕。

【讨论】:

以上是关于Mono 准备好迎接黄金时段了吗? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

Android 蓝牙低功耗 (BLE) API 尚未准备好迎接黄金时段

技术的新面孔

Eclipse 有宏记录器吗? [关闭]

exness:美指徘徊于90下方,黄金六连涨到头了吗?

Spring Cloud 数据流文档

老铁们,准备好迎接自动驾驶了吗?