我为什么放弃了 Python ,选择了 Go?

根据维基百科数据统计(https://en.wikipedia.org/wiki/List_of_programming_languages),一共有 600 余种不同的编程语言。对于五花八门的开发语言,每门语言都有自己的优缺点。而励志成为一名博学多广的开发者,术业也有专攻,怎奈何也不会学习完百余种语言。

在这些语言中,Java 作为 22 年的常青藤,无论是新爆发的 Kotlin 还是 C、C++、Python、JavaScript 等老牌语言想要在短期之间撼动其地位也是不大现实,相信这一点毋庸置疑。吃完I安从近几个月的 TIOBE 编程语言排行榜来看,继 Java 之后,C、C++、C# 使用率虽有所下降,但依旧稳坐排行榜的前五名。

除了以上四种头牌语言,还有一种即 Python(蟒蛇)。随着人工智能、物联网、数据科学等领域的兴起,Python 迅速升温,成为诸多开发者的首选入门语言。然而就在一片追随声中,不少开发者从 Python 转向了 Go 语言,这究竟是什么原因?接下来,来自 Stream 团队的 Thierry Schellenbach 给出了九大理由,并对比了 Python,解析优缺点,希望对更多的开发者有所助益。

**

为什么开始使用 Go 语言?

**

原因 1 ——性能

Go 极其地快,其性能与 Java 或 C++ 相似。在我们的使用中,Go 一般比 Python 要快 30 倍。

原因 2:语言性能很重要

对很多应用来说,编程语言只是简单充当了其与数据集之间的胶水。语言本身的性能常常无关轻重。

但是 Stream 是一个 API 提供商,服务于世界 500 强以及超过 2 亿的终端用户。数年来我们已经优化了 Cassandra、PostgreSQL、Redis 等等,然而最终抵达了所使用语言的极限。

Python 非常棒,但是其在序列化/去序列化、排序和聚合中表现欠佳。我们经常会遇到这样的问题:Cassandra 用时 1ms 检索了数据,Python 却需要 10ms 将其转化成对象。

原因 3:开发者效率&不要过于创新

看一下绝佳的入门教程《开始学习 Go 语言》(http://howistart.org/posts/go/1/)中的一小段代码:

package main

type openWeatherMap struct{}

func (w openWeatherMap) temperature(city string) (float64, error) {

    resp, err := http.Get("http://api.openweathermap.org/data/2.5/weather?APPID=YOUR_API_KEY&q=" + city)

    if err != nil {

            return 0, err

    }



    defer resp.Body.Close()



    var d struct {

            Main struct {

                    Kelvin float64 `json:"temp"`

            } `json:"main"`

    }



    if err := json.NewDecoder(resp.Body).Decode(&d); err != nil {

            return 0, err

    }



    log.Printf("openWeatherMap: %s: %.2f", city, d.Main.Kelvin)

    return d.Main.Kelvin, nil

如果你是一个新手,看到这段代码你并不会感到吃惊。它展示了多种赋值、数据结构、指针、格式化以及内置的 HTTP 库。

当我第一次编程时,我很喜欢使用 Python 的高阶功能。Python 允许你创造性地使用正在编写的代码,比如,你可以:

在代码初始化时,使用 MetaClasses 自行注册类别

置换真假

添加函数到内置函数列表中

通过奇妙的方法重载运算符

毋庸置疑这些代码很有趣,但也使得在读取其他人的工作时,代码变得难以理解。

Go 强迫你坚持打牢基础,这也就为读取任意代码带来了便利,并能很快搞明白当下发生的事情。

注意:当然如何容易还是要取决于你的使用案例。如果你要创建一个基本的 CRUD API,我还是建议你使用 Django + DRF,或者 Rails。

原因 4:并发性&通道

Go 作为一门语言致力于使事情简单化。它并未引入很多新概念,而是聚焦于打造一门简单的语言,它使用起来异常快速并且简单。其唯一的创新之处是 goroutines 和通道。Goroutines 是 Go 面向线程的轻量级方法,而通道是 goroutines 之间通信的优先方式。

创建 Goroutines 的成本很低,只需几千个字节的额外内存,正由于此,才使得同时运行数百个甚至数千个 goroutines 成为可能。你可以借助通道实现 goroutines 之间的通信。Go 运行时间可以表示所有的复杂性。Goroutines 以及基于通道的并发性方法使其非常容易使用所有可用的 CPU 内核,并处理并发的 IO——所有不带有复杂的开发。相较于 Python/Java,在一个 goroutine 上运行一个函数需要最小的样板代码。你只需使用关键词「go」添加函数调用:

package main

import (

    "fmt"

    "time"

)

func say(s string) {

    for i := 0; i < 5; i++ {

            time.Sleep(100 * time.Millisecond)

            fmt.Println(s)

    }

}

func main() {

    go say("world")

    say("hello")

}

https://tour.golang.org/concurrency/1

Go 的并发性方法非常容易上手,相较于 Node 也很有趣;在 Node 中,开发者必须密切关注异步代码的处理。

并发性的另一个优质特性是竞赛检测器,这使其很容易弄清楚异步代码中是否存在竞态条件。下面是一些上手 Go 和通道的很好的资源:

https://gobyexample.com/channels

https://tour.golang.org/concurrency/2

http://guzalexander.com/2013/12/06/golang-channels-tutorial.html

https://www.golang-book.com/books/intro/10

https://www.goinggo.net/2014/02/the-nature-of-channels-in-go.html

原因 5:快速的编译时间

当前我们使用 Go 编写的最大微服务的编译时间只需 6 秒。相较于 Java 和 C++呆滞的编译速度,Go 的快速编译时间是一个主要的效率优势。我热爱击剑,但是当我依然记得代码应该做什么之时,事情已经完成就更好了。

Go 之前的代码编译

原因 6:打造团队的能力

首先,最明显的一点是:Go 的开发者远没有 C++和 Java 等旧语言多。据知,有 38% 的开发者了解 Java,19.3% 的开发者了解 C++,只有 4.6% 的开发者知道 Go。GitHub 数据表明了相似的趋势:相较于 Erlang、Scala 和 Elixir,Go 更为流行,但是相较于 Java 和 C++ 就不是了。

幸运的是 Go 非常简单,且易于学习。它只提供了基本功能而没有多余。Go 引入的新概念是「defer」声明,以及内置的带有 goroutines 和通道的并发性管理。正是由于 Go 的简单性,任何的 Python、Elixir、C++、Scala 或者 Java 开发者皆可在一月内组建成一个高效的 Go 团队。

原因 7:强大的生态系统

对我们这么大小的团队(大约 20 人)而言,生态系统很重要。如果你需要重做每块功能,那就无法为客户创造收益了。Go 有着强大的工具支持,面向 Redis、RabbitMQ、PostgreSQL、Template parsing、Task scheduling、Expression parsing 和 RocksDB 的稳定的库。

Go 的生态系统相比于 Rust、Elixir 这样的语言有很大的优势。当然,它又略逊于 Java、Python 或 Node 这样的语言,但它很稳定,而且你会发现在很多基础需求上,已经有高质量的文件包可用了。

原因 8:GOFMT,强制代码格式

Gofmt 是一种强大的命令行功能,内建在 Go 的编译器中来规定代码的格式。从功能上看,它类似于 Python 的 autopep8。格式一致很重要,但实际的格式标准并不总是非常重要。Gofmt 用一种官方的形式规格代码,避免了不必要的讨论。

**原因 9:gRPC 和 Protocol Buffe**rs

Go 语言对 protocol buffers 和 gRPC 有一流的支持。这两个工具能一起友好地工作以构建需要通过 RPC 进行通信的微服务器(microservices)。我们只需要写一个清单(manifest)就能定义 RPC 调用发生的情况和参数,然后从该清单将自动生成服务器和客户端代码。这样产生代码不仅快速,同时网络占用也非常少。

从相同的清单,我们可以从不同的语言生成客户端代码,例如 C++、Java、Python 和 Ruby。因此内部通信的 RESET 端点不会产生分歧,我们每次也就需要编写几乎相同的客户端和服务器代码。

使用 Go 语言的缺点

缺点 1:缺少框架

Go 语言没有一个主要的框架,如 Ruby 的 Rails 框架、Python 的 Django 框架或 PHP 的 Laravel。这是 Go 语言社区激烈讨论的问题,因为许多人认为我们不应该从使用框架开始。在很多案例情况中确实如此,但如果只是希望构建一个简单的 CRUD API,那么使用 Django/DJRF、Rails Laravel 或 Phoenix 将简单地多。

缺点 2:错误处理

Go 语言通过函数和预期的调用代码简单地返回错误(或返回调用堆栈)而帮助开发者处理编译报错。虽然这种方法是有效的,但很容易丢失错误发生的范围,因此我们也很难向用户提供有意义的错误信息。错误包(errors package)可以允许我们添加返回错误的上下文和堆栈追踪而解决该问题。

另一个问题是我们可能会忘记处理报错。诸如 errcheck 和 megacheck 等静态分析工具可以避免出现这些失误。虽然这些解决方案十分有效,但可能并不是那么正确的方法。

缺点 3:软件包管理

Go 语言的软件包管理绝对不是完美的。默认情况下,它没有办法制定特定版本的依赖库,也无法创建可复写的 builds。相比之下 Python、Node 和 Ruby 都有更好的软件包管理系统。然而通过正确的工具,Go 语言的软件包管理也可以表现得不错。

我们可以使用 Dep 来管理依赖项,它也能指定特定的软件包版本。除此之外,我们还可以使用一个名为 VirtualGo 的开源工具,它能轻松地管理 Go 语言编写的多个项目。

Python vs Go

我们实施的一个有趣实验是用 Python 写排名 feed,然后用 Go 改写。看下面这种排序方法的示例:

{

    "functions": {

            "simple_gauss": {

                    "base": "decay_gauss",

                    "scale": "5d",

                    "offset": "1d",

                    "decay": "0.3"

            },

            "popularity_gauss": {

                    "base": "decay_gauss",

                    "scale": "100",

                    "offset": "5",

                    "decay": "0.5"

            }

    },

    "defaults": {

            "popularity": 1

    },

    "score": "simple_gauss(time)*popularity"

}

Python 和 Go 代码都需要以下要求从而支持上面的排序方法:

解析得分的表达。在此示例中,我们想要把 simple_gauss(time)*popularity 字符串转变为一种函数,能够把 activity 作为输入然后给出得分作为输出。

在 JSON config 上创建部分函数。例如,我们想要「simple_gauss」调用「decay_gauss」,且带有的键值对为”scale”: “5d”、”offset”: “1d”、”decay”: “0.3”。

解析「defaults」配置,便于某个领域没有明确定义的情况下有所反馈。

从 step1 开始使用函数,为 feed 中的所有 activity 打分。

开发 Python 版本排序代码大约需要 3 天,包括写代码、测试和建立文档。接下来,我么花费大约 2 周的时间优化代码。其中一个优化是把得分表达 simple_gauss(time)*popularity 转译进一个抽象语法树。我们也实现了 caching logic,之后会预先计算每次的得分。

相比之下,开发 Go 版本的代码需要 4 天,但之后不需要更多的优化。所以虽然最初的开发上 Python 更快,但 Go 最终需要的工作量更少。此外,Go 代码要比高度优化的 python 代码快了 40 多倍。

以上只是我们转向 Go 所体验到的一种好处。当然,也不能这么做比较:

该排序代码是我用 Go 写的第一个项目;

Go 代码是在 Python 代码之后写的,所以提前理解了该案例;

Go 的表达解析库质量优越。

Elixir vs Go

我们评估的另一种语言是 Elixir。Elixir 建立在 Erlang 虚拟机上。这是一种迷人的语言,我们之所以想到它是因为我们组员中有一个在 Erlang 上非常有经验。

在使用案例中,我们观察到 Go 的原始性能更好。Go 和 Elixir 都能很好地处理数千条并行需求,然而,如果是单独的要求,Go 实际上更快。相对于 Elixir,我们选择 Go 的另一个原因是生态系统。在我们需求的组件上,Go 的库更为成熟。在很多案例中,Elixir 库不适合产品使用。同时,也很难找到/训练同样使用 Elixir 的开发者。

结论

Go 是一种非常高效的语言,高度支持并发性。同时,它也像 C++和 Java 一样快。虽然相比于 Python 和 Ruby,使用 Go 建立东西需要更多的时间,但在后续的代码优化上可以节省大量时间。在 Stream,我们有个小型开发团队为 2 亿终端用户提供 feed 流。对新手开发者而言,Go 结合了强大的生态系统、易于上手,也有超快的表现、高度支持并发性,富有成效的编程环境使它成为了一种好的选择。Stream 仍旧使用 Python 做个性化 feed,但所有性能密集型的代码将会用 Go 来编写。

喜欢小编就关注一下哟!

<think>好的,我需要回答用户关于设备端需要准备什么的问题,特别是关于是否需要Python环境和MQTT。用户之前已经询问过云平台和设备升级的方案,现在关注设备端的具体要求。 首先,用户想知道设备端是否需要Python环境。根据之前的方案,设备端需要执行升级包的下载、验签和安装。Python环境可能是一个选项,但嵌入式设备可能资源有限,所以需要考虑其他语言如C/C++或Go。需要说明是否必须使用Python,或者有其他替代方案。 其次,用户提到MQTT。在之前的回复中,建议使用MQTT进行状态上报,因此需要确认设备端是否需要MQTT客户端,以及是否有其他协议选项,比如HTTP或WebSocket。还要考虑设备端的资源限制,比如内存和处理能力,选择合适的通信方式。 接下来,需要整理设备端的主要准备内容,包括软件环境和硬件要求。可能包括操作系统、存储空间、网络模块、安全模块(如密钥存储)等。同时,升级流程中的各个步骤需要哪些组件,比如下载工具、验签库、安装脚本等。 然后,考虑不同的设备类型,比如资源丰富的设备(如工业网关)和资源受限的设备(如传感器)。可能需要不同的解决方案,比如轻量级MQTT库或HTTP长轮询。 还要注意安全方面,设备端需要预置密钥或证书,用于签名验证和身份认证。需要说明如何安全存储这些密钥,避免泄露。 此外,是否需要特定的升级代理或守护进程来管理升级流程,以及如何保证升级过程的可靠性和容错能力,比如断点续传和回滚机制。 最后,提供代码示例时,是否需要Python或其他语言的示例,或者伪代码说明逻辑。根据用户之前的示例使用Python,可能需要保持一致性,但也要说明其他可能性。 总结,设备端的准备工作需要覆盖软件环境、通信协议、安全措施、升级组件和资源适配等方面,确保在不同场景下都能安全可靠地执行升级任务,并实时上报状态。</think>在设备端实现云平台升级功能时,需根据设备资源条件(如算力、内存、网络)选择合适的软件栈。以下是设备端需要准备的核心组件及实现方案: --- ### **一、设备端基础准备** #### 1. **运行环境** | **资源场景** | **推荐方案** | **是否需要Python** | |---------|----------------------------------------| | **高资源设备** | Linux/RTOS + 独立进程 | 可选(如用Python则需预装解释器) | | **低资源设备** | 轻量级C/C++固件(无OS) | 不需要(需交叉编译为二进制) | **说明**: - **Python环境**:仅在设备资源充足(如工业网关、储能控制器)时适用,适合快速开发,但需注意依赖管理和打包体积(可编译为`PyInstaller`单文件)。 - **C/C++方案**:更适合MCU或嵌入式设备,需集成轻量级HTTP/MQTT库(如[Paho MQTT](https://github.com/eclipse/paho.mqtt.embedded-c)、[libcurl](https://curl.se/libcurl/))。 --- ### **二、核心功能组件** #### 1. **网络通信协议** | **协议** | **用途** | **推荐库(Python示例)** | **资源消耗** | |--|-------------| | **MQTT** | 接收指令、上报状态 | `paho-mqtt` | 低 | | **HTTP(S)** | 下载升级包、轮询任务 | `requests` 或 `urllib3` | 中 | | **WebSocket** | 长连接实时上报(替代轮询) | `websockets` | 高 | **设备端MQTT连接示例(Python)**: ```python import paho.mqtt.client as mqtt client = mqtt.Client() client.tls_set(ca_certs="/path/to/cloud_ca.pem") # 单向TLS验证云平台 client.connect("mqtt.example.com", 8883) client.subscribe("upgrade/command/#") # 订阅升级指令Topic def on_message(client, userdata, msg): if msg.topic == "upgrade/command/BMS-001": handle_upgrade_command(msg.payload) # 解析升级指令 client.on_message = on_message client.loop_start() # 启动后台线程 ``` #### 2. **安全验证模块** - **验签工具**: - 验签云平台下发的固件包(如ECDSA签名校验)。 - 资源受限设备可使用硬件安全芯片(如ATECC608)加速验签。 - **密钥管理**: - 预置设备唯一密钥(如烧录到安全存储区)。 - 动态令牌缓存(如JWT令牌,通过HTTPS获取)。 **Python验签示例**: ```python from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import ec # 加载预置的公钥(云平台证书) public_key = ec.EllipticCurvePublicKey.from_encoded_point( ec.SECP256R1(), bytes.fromhex("3059...") ) # 验证固件签名 signature = bytes.fromhex("sig_hex_data") firmware_data = open("firmware.bin", "rb").read() public_key.verify( signature, firmware_data, ec.ECDSA(hashes.SHA256()) ) # 验签失败会抛出异常 ``` #### 3. **升级执行引擎** - **下载器**: - 支持HTTPS断点续传(HTTP头`Range: bytes=0-1024`)。 - 校验下载完整性(如SHA256校验和)。 - **安装器**: - 执行固件刷写(如调用`fw_setenv`更新U-Boot变量)。 - 支持原子化操作(避免半升级状态)。 **Python下载逻辑示例**: ```python import requests from tqdm import tqdm # 进度条(调试用) def download_firmware(url, save_path): headers = {"Authorization": "Bearer YOUR_JWT_TOKEN"} response = requests.get(url, headers=headers, stream=True) total_size = int(response.headers.get("content-length", 0)) with open(save_path, "wb") as f: for chunk in tqdm( response.iter_content(chunk_size=1024), total=total_size // 1024, unit="KB" ): if chunk: f.write(chunk) report_progress(f.tell() / total_size * 100) # 上报下载进度 ``` --- ### **三、设备端最小化依赖(无Python场景)** #### 1. **C语言固件实现要点** ```c // 使用libcurl实现HTTPS下载(需交叉编译) #include <curl/curl.h> void download_upgrade_package(const char *url) { CURL *curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); // 启用证书验证 curl_easy_setopt(curl, CURLOPT_CAINFO, "/etc/ssl/certs/cloud_ca.pem"); // 写入文件回调 FILE *fp = fopen("firmware.bin", "wb"); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); CURLcode res = curl_easy_perform(curl); if (res != CURLE_OK) { fprintf(stderr, "Download failed: %s\n", curl_easy_strerror(res)); } curl_easy_cleanup(curl); fclose(fp); } ``` #### 2. **资源优化技巧** - **裁剪MQTT协议**:仅保留QoS0/QoS1支持,禁用遗嘱消息等高级特性。 - **静态链接库**:将依赖库编译进固件,减少运行时动态加载开销。 - **内存池管理**:预分配网络缓冲区,避免频繁内存分配。 --- ### **四、设备端部署流程** 1. **环境预配置**: - 烧录设备唯一ID及密钥。 - 预置CA证书(用于验证云平台HTTPS证书)。 2. **软件安装**: - Python方案:部署虚拟环境(`venv`)并安装依赖包。 - C方案:交叉编译二进制文件并刷入设备。 3. **服务自启动**: - 添加Systemd服务(Linux)或启动脚本(如`rc.local`)。 --- ### **五、验证与调试** 1. **离线测试**: - 使用模拟云平台(如Postman Mock Server)验证下载逻辑。 - 通过`strace`或`gdb`跟踪系统调用(C/C++场景)。 2. **日志收集**: - 关键日志项:网络连接状态、验签结果、安装阶段耗时。 - 日志上报:通过MQTT/HTTP同步异常信息到云平台。 --- ### **典型问题排查** | **现象** | **可能原因** | **解决方案** | |---------------|---------------------------------------| | 下载进度卡在0% | CA证书过期或设备时钟不同步 | 同步NTP时间,更新CA证书 | | MQTT频繁断开 | 设备心跳间隔设置过长 | 调整`keepalive`参数(如60秒) | | 验签失败但文件完整 | 设备存储空间不足导致签名被截断 | 增加存储预留空间,校验文件长度 | --- 根据设备实际资源情况选择合适的实现方案。若设备无Python环境,优先采用C/C++方案;若需快速迭代,Python+MQTT组合可缩短开发周期。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值