Go 版本升级 | 统计 Github 社区 Go 版本分布情况

背景

因为最近三年用的 Go 版本是 1.16,但最新的版本升级到了 1.23,很多依赖的三方包最新文件都已经升级,使用了泛型以及 GO 新版本的特性,导致我只能适配 Go1.16 的三方包旧版本,但这种问题发生的频率多了后,自然就感觉到了麻烦和落后,所以打算升级 Go 版本。

但升级到哪个版本?我的考虑的重心有一点在于主流 Go 服务在用哪个版本。
而社区环境,最佳的选择当然是 Github。
所以,首先目标是统计 Github 上 Go 项目中使用的版本分布情况。

统计目标

Github 上,Go 项目数量有一百六十多万:
在这里插入图片描述
抓取全部仓库数据进行统计,有点不现实。
所以我根据 starts 数量分析,最后确定抓取 stars 数量大于 200 的仓库数据:
在这里插入图片描述
总共只有一万多一点,所以主流和有价值的仓库占比,确实很少,大部分都是私人仓库而已。

数据抓取方式

Github 有开放 API,通过在 GitHub 账号重心生成 Token,就可以调用。
在这里插入图片描述
在这里插入图片描述
API 包括仓库搜索、仓库详情信息获取等,详细可以查阅 官方 API 文档。

使用 GITHUB_TOKEN 鉴权,调用 API 存在部分限制,比如:

  1. GitHub Search API 每次搜索,分页每页最多 100,每次查询结果分页,超过 1000 个结果就会报错 422。
  2. GitHub API 的速率限制为 5000 次请求/小时

所以,在使用 API 时,结合统计目标的总数量,要考虑到上述两个限制。

数据抓取脚本

import requests
import base64
from collections import defaultdict
import time
import os
import csv
import configparser
from concurrent.futures import ThreadPoolExecutor, as_completed# 读取配置文件
config = configparser.ConfigParser()
config.read('config.ini')
GITHUB_TOKEN = config['GITHUB']['TOKEN']# 设置请求头,包含认证信息
headers = {}
if GITHUB_TOKEN:headers = {'Authorization': f'token {GITHUB_TOKEN}'}# 检查 API 速率限制
def check_rate_limit(headers):remaining = int(headers.get('X-RateLimit-Remaining', 0))reset_time = int(headers.get('X-RateLimit-Reset', time.time()))if remaining == 0:sleep_time = reset_time - time.time() + 1  # 等待到重置时间print(f"API 请求达到速率限制,等待 {sleep_time} 秒...")time.sleep(sleep_time)else:print(f"剩余请求次数: {remaining}")# 构建 GitHub API 查询 URL
def build_search_url(query_params, page=1, per_page=100):base_url = f"https://api.github.com/search/repositories"query = '+'.join(query_params)  # 将查询条件组装成字符串url = f"{base_url}?q={query}&sort=stars&order=desc&per_page={per_page}&page={page}"return url# 获取 Go 仓库信息,增加重试机制以避免数据丢失
def search_go_repos(query_params, page=1, per_page=100, max_retries=3):url = build_search_url(query_params, page=page, per_page=per_page)retries = 0while retries < max_retries:try:response = requests.get(url, headers=headers, timeout=10)  # 设置超时为 10 秒# 检查速率限制check_rate_limit(response.headers)if response.status_code == 200:return response.json()['items']else:print(f"API 请求失败,状态码: {response.status_code}")return []except requests.Timeout:retries += 1print(f"请求超时: {url},重试第 {retries} 次")time.sleep(2)  # 重试前等待 2 秒except requests.RequestException as e:print(f"请求发生错误: {e}")retries += 1time.sleep(2)  # 重试前等待 2 秒print(f"请求失败超过最大重试次数,跳过该页数据: {url}")return []# 获取仓库的 go.mod 文件内容,并提取 Go 版本号
def get_go_version(owner, repo):url = f"https://api.github.com/repos/{owner}/{repo}/contents/go.mod"response = requests.get(url, headers=headers)# 检查速率限制check_rate_limit(response.headers)if response.status_code == 200:content = response.json().get('content')if content:decoded_content = base64.b64decode(content).decode('utf-8')for line in decoded_content.splitlines():if line.startswith('go '):return line.split()[1]return None# 使用并发来加速处理仓库详细信息
def process_repos_with_concurrency(repos, version_counts, repos_data, max_workers=5):with ThreadPoolExecutor(max_workers=max_workers) as executor:future_to_repo = {executor.submit(get_go_version, repo['owner']['login'], repo['name']): repo for repo in repos}for future in as_completed(future_to_repo):repo = future_to_repo[future]owner = repo['owner']['login']repo_name = repo['name']repo_url = repo['html_url']stars = repo['stargazers_count']created_at = repo['created_at']updated_at = repo['pushed_at']try:version = future.result()if version:version_counts[version] += 1repos_data.append([repo_name, repo_url, stars, version, created_at, updated_at])else:repos_data.append([repo_name, repo_url, stars, '未检测到版本号', created_at, updated_at])except Exception as e:print(f"处理仓库 {repo_name} 时出错: {e}")# 统计 Go 版本分布并导出仓库信息到 CSV
def collect_and_save_repo_data(query_params, start_page=1, max_pages=1, per_page=10, max_workers=5):version_counts = defaultdict(int)repos_data = []all_repos = []  # 用于收集所有返回的仓库for page in range(start_page, start_page + max_pages):print(f"正在处理第 {page}/{start_page + max_pages - 1} 页的数据...")repos = search_go_repos(query_params, page, per_page)if not repos:breakall_repos.extend(repos)  # 将所有查询结果收集到一起# 并发处理仓库 go.mod 文件process_repos_with_concurrency(repos, version_counts, repos_data, max_workers)# 每处理一页数据就将数据写入 CSV 文件write_to_csv("go_repos_info.csv", repos_data)repos_data = []  # 清空 repos_data 以便处理下一页数据return version_counts, all_repos  # 返回版本统计和所有仓库信息# 判断文件是否存在,追加数据或创建新文件
def write_to_csv(filename, repos_data):file_exists = os.path.isfile(filename)  # 判断文件是否存在mode = 'a' if file_exists else 'w'  # 如果文件存在则以追加模式打开,否则以写模式创建with open(filename, mode=mode, newline='', encoding='utf-8') as file:writer = csv.writer(file)# 如果文件不存在,写入表头if not file_exists:writer.writerow(['仓库名', '仓库地址', 'Star 数量', 'Go 版本号', '创建时间', '最近更新时间'])# 写入仓库数据writer.writerows(repos_data)# 自动调整 star 范围并分页查询
def paginate_through_stars(start_star=131084, min_star=300, per_page=100, max_workers=5):current_star = start_starnext_star = Nonewhile current_star > min_star:query_params = [f'language:Go', f'stars:{min_star}..{current_star}']# 获取总仓库数和总页数total_repos, total_pages = get_total_repos_and_pages(query_params, per_page)print(f"正在查询 stars: <{current_star} 的范围,找到 {total_repos} 个仓库")if total_repos == 0:print(f"在 stars: <{current_star} 范围内没有找到仓库,程序终止。")break# 设置查询的最大页数max_pages = min(total_pages, 10)  # 一次最多查询 10 页version_counts, all_repos = collect_and_save_repo_data(query_params, start_page=1, max_pages=max_pages, per_page=per_page, max_workers=max_workers)# 获取所有仓库中最小的 star 数,确保正确的 star 数排序if all_repos:sorted_repos = sorted(all_repos, key=lambda repo: repo['stargazers_count'], reverse=False)next_star = sorted_repos[0]['stargazers_count'] - 1  # 获取最小 star 数,且减1。(stars:300..4175 查询的是 star 数 大于等于 300 且小于等于 4175 的仓库,所以要减1,避免重复)print(f"调整下一个查询的 star 范围为 stars: <{next_star}")current_star = next_star  # 更新查询范围else:print("无法找到下一个 star 范围,程序结束。")break# 输出统计结果
def print_version_stats(version_counts):print("\nGo 版本分布统计结果:")for version, count in sorted(version_counts.items(), key=lambda x: x[0]):print(f"Go 版本: {version}, 使用次数: {count}")# 获取总仓库数量和页数
def get_total_repos_and_pages(query_params, per_page=100):url = build_search_url(query_params, page=1, per_page=per_page)response = requests.get(url, headers=headers)# 检查速率限制check_rate_limit(response.headers)if response.status_code == 200:total_count = response.json()['total_count']total_pages = (total_count // per_page) + 1return total_count, total_pageselse:print(f"无法获取总仓库数量,状态码: {response.status_code}")return 0, 0# 获取当前 GITHUB TOKEN 的剩余次数和恢复时间
def get_rate_limit():url = "https://api.github.com/rate_limit"response = requests.get(url, headers=headers)if response.status_code == 200:data = response.json()remaining = data['rate']['remaining']reset_time = data['rate']['reset']  # 重置时间(UNIX 时间戳)reset_time_human = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(reset_time))  # 将重置时间转换为可读格式print(f"当前剩余请求次数: {remaining}")print(f"请求次数将在 {reset_time_human} 重置")# 返回剩余次数和重置时间,供其他地方使用return remaining, reset_time_humanelse:print(f"无法获取速率限制信息,状态码: {response.status_code}")return None, Noneif __name__ == "__main__":# 设置起始 Star 数和最小 Star 数# START_STAR = 141084   # 最大的 Star 数START_STAR = 475       # 最大的 Star 数MIN_STAR = 200          # 最小的 Star 数PER_PAGE = 100MAX_WORKERS = 10# 自动分页查询并处理数据paginate_through_stars(START_STAR, MIN_STAR, PER_PAGE, MAX_WORKERS)# 检查当前 TOKEN 状态# get_rate_limit()

这个脚本通过控制 stars 条件、分页 条件,查询所有 Go 仓库数据,获取 Go 仓库下 go.mod 中的版本定义。

并且加入了并发处理,保证查询速度,同时考虑到 Token 的限制,所以还加了一个 TOKEN 请求速度检查和等待机制。

直接在 python 环境下运行就可以,实践过的。

Go 分布统计结果

得到的数据表如下图所示:
在这里插入图片描述
对数据做了处理后,统计结果如下:
在这里插入图片描述
在这里插入图片描述
一目了然,目前社区中主流的版本为 1.22、1.21。

所以,我也打算把 Go 版本先升级到 1.22。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/57772.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分享几个办公类常用的AI工具

办公类 WPS AI讯飞智文iSlideProcessOn亿图脑图ChatPPT WPS AI 金山办公推出的协同办公 AI 应用&#xff0c;具有文本生成、多轮对话、润色改写等多种功能&#xff0c;可以辅助用户进行文档编辑、表格处理、演示文稿制作等办公操作。 https://ai.wps.cn/ 讯飞智文 科大讯飞推…

从零开始:AI制作PPT工具大比拼

现在真的万物皆可AI了&#xff0c;甚至是令人头疼的PPT&#xff0c;也可以直接用AI来搞定了。作为一个PPT新手&#xff0c;我最近对AI制作PPT这个话题产生了浓厚的兴趣。我决定亲自试一试市面上几款热门的AI制作PPT工具&#xff1a;笔灵AIPPT、轻竹PPT、博思白板AIPPT和KimiAI。…

了解Oracle表结构查询:获取列信息与注释

目录 1. 基本知识2. Demo3. 补充Mysql 1. 基本知识 Oracle数据库中&#xff0c;表结构信息包含列名、数据类型、长度、可空性、默认值以及字段注释等&#xff0c;这些信息对于理解数据库设计和维护非常重要 基本的属性要点如下&#xff1a; 表名&#xff08;TABLE_NAME&…

uniApp 加载google地图 并规划路线

uniApp 加载google地图 并规划路线 备注:核心代码实例 备注: 打开谷歌地图失败的话 参考google开发文档 https://developers.google.com/maps/documentation/urls/ios-urlscheme?hlzh-cn#swift核心代码 mounted() {this.loadGoogleMapsScript(); }, methods: {//加载loadGo…

LCD手机屏幕高精度贴合

LCD手机屏幕贴合&#xff0c;作为智能手机生产线上至关重要的一环&#xff0c;其质量直接关乎用户体验与产品竞争力。这一工艺不仅要求屏幕组件间的无缝对接&#xff0c;达到极致的视觉与触觉效果&#xff0c;还需确保在整个生产过程中&#xff0c;从材料准备到最终成品&#x…

深入理解与优化 Java JVM

一、引言 在 Java 开发中&#xff0c;Java 虚拟机&#xff08;JVM&#xff09;起着至关重要的作用。它负责将 Java 字节码转换为机器码并执行&#xff0c;同时管理着内存分配、垃圾回收等关键任务。理解和优化 JVM 对于提高 Java 应用程序的性能、稳定性和可扩展性至关重要。本…

不同类型的 LED 驱动电源在检测方法上有哪些不同?-纳米软件

1.传统 LED 驱动电源检测方法&#xff1a; 通常会提取 LED 驱动电源性能指标参数中较为重要的几个因子&#xff0c;如电压稳定性、电流波动范围等。利用诸如 k-means 聚类分析方法&#xff0c;实现对不同厂家、使用寿命不同的 LED 驱动电源快速有效的分类2。这种方法主要是通过…

海外媒体发稿:外媒宣发之《时代》杂志 TIME 的魅力

海外媒体发稿&#xff1a;外媒宣发之《时代》杂志 TIME 的魅力 海外媒体发稿&#xff1a;外媒宣发之《时代》杂志 TIME 在当今全球化的信息时代&#xff0c;媒体的影响力无远弗届。对于企业、组织和个人而言&#xff0c;能够在具有广泛影响力的世界媒体上发声&#xff0c;无疑…

Telegram机器人的手机部署

目的 一直有读 epub 电子书的习惯&#xff0c;摘录段落复制下来段落很难看&#xff0c;把自己写的排版器的逻辑复制下来&#xff0c;写成了一个排版机器人所有发给机器人的文字&#xff0c;都会经过排版&#xff0c;后转发到读书频道 前提 本来最好方法是直接把机器人架在服…

开源呼叫中心系统与商业软件的对比

开源呼叫中心系统与商业软件的对比 作者&#xff1a;FreeIPCC 在当今的商业环境中&#xff0c;呼叫中心系统已成为企业与客户之间沟通的重要桥梁。而在选择呼叫中心系统时&#xff0c;企业面临着两种主要的选择&#xff1a;开源呼叫中心系统和商业软件。这两种系统各有其独特的…

各数据表字段的数据类型与相关属性应该如何设计?分类列出并说明原因

在设计数据库表字段的数据类型与相关属性时&#xff0c;可以根据不同的数据类型进行分类。以下是常见的数据类型及其相关属性的分类和说明&#xff1a; 数值型数据 整型 (INT, BIGINT, SMALLINT, TINYINT) 用途: 存储整数值。 原因: 整型适合存储计数、ID等不需要小数的数值&…

面试中的一个基本问题:如何在数据库中存储密码?

面试中的一个基本问题&#xff1a;如何在数据库中存储密码&#xff1f; 在安全面试中&#xff0c;“如何在数据库中存储密码&#xff1f;”是一个基础问题&#xff0c;但反映了应聘者对安全最佳实践的理解。以下是安全存储密码的最佳实践概述。 了解风险 存储密码必须安全&am…

【Java小白图文教程】-05-数组和排序算法详解

精品专题&#xff1a; 01.《C语言从不挂科到高绩点》课程详细笔记 https://blog.csdn.net/yueyehuguang/category_12753294.html?spm1001.2014.3001.5482 02. 《SpringBoot详细教程》课程详细笔记 https://blog.csdn.net/yueyehuguang/category_12789841.html?spm1001.20…

less和sass基本使用

变量 变量在LESS和SASS中都以符号定义&#xff0c;可以在全局范围内使用&#xff0c;也可以在局部范围内定义和使用。 LESS示例&#xff1a; primary-color: #3498db; padding: 15px;.article {background-color: primary-color;padding: padding; }SASS示例&#xff1a; $…

day02|计算机网络重难点之HTTP请求报文和响应报文

day02|计算机网络重难点之HTTP请求报文和响应报文 3.HTTP请求报文和响应报文是怎样的&#xff0c;有哪些常见的字段&#xff1f; 3.HTTP请求报文和响应报文是怎样的&#xff0c;有哪些常见的字段&#xff1f; HTTP请求报文主要是由 请求行、请求头部、空行和请求体 四部分组成…

电商平台店铺运营:巧用 API 接口的策略之道

一、商品管理策略 实时库存同步 通过 API 接口&#xff0c;将店铺的库存管理系统与电商平台连接起来&#xff0c;实现实时库存同步。这样可以避免超卖现象的发生&#xff0c;提高顾客满意度。当库存发生变化时&#xff0c;系统自动更新平台上的库存数量&#xff0c;确保信息的准…

Redis 发布订阅 总结

前言 相关系列 《Redis & 目录》&#xff08;持续更新&#xff09;《Redis & 发布订阅 & 源码》&#xff08;学习过程/多有漏误/仅作参考/不再更新&#xff09;《Redis & 发布订阅 & 总结》&#xff08;学习总结/最新最准/持续更新&#xff09;《Redis &a…

ChatGPT的科研写作能力提升专题

在当今信息爆炸的时代&#xff0c;科研工作者不仅需要在各自的领域中产生高质量的研究成果&#xff0c;还需要面对大量文献阅读、写作和发表任务。为了应对这些挑战&#xff0c;越来越多的科研人员开始借助人工智能&#xff08;AI&#xff09;工具&#xff0c;而GPT&#xff08…

简单工厂(Simple Factory)

简单工厂&#xff08;Simple Factory&#xff09; 在创建一个对象时不向客户暴露内部细节&#xff0c;并提供一个创建对象的通用接口。 说明&#xff1a; 简单工厂把实例化的操作单独放到一个类中&#xff0c;这个类就成为简单工厂类&#xff0c;让简单工厂类来决定应该用哪…

supermall项目上拉加载bug分析

1.bug分析 bug出现的过程是这样的&#xff1a;better-scroll框架会计算滚动内容的高度(通过BScroll对象的scrollerHeight属性记录滚动内容的高度) 由于内容中的图片资源还未加载成功 就已经完成计算 导致计算结果错误 而计算之后 图片资源随之加载完成 这时候better-scroll框架…