2024年1月

前言

原本应用的日志是全部输出到os的stdout,也就是控制台输出。因其它团队要求也要保留日志文件,便于他们用其他工具统一采集,另一方面还要保留控制台输出,便于出问题的时候自己直接看pod日志。具体需求如下:

  1. 日志支持同时控制台输出和文件输出
  2. 控制台的输出级别可以高点,比如WARNING,个人这边的实际情况是WARNING或ERROR就能判断大部分问题。日志文件的输出级别设置为INFO,如果控制台日志找不到问题,可以具体看日志文件的内容。
  3. 因为用到了多进程,所以写文件的时候要保证多进程安全,避免日志内容不会缺失。
  4. 日志文件可以设置自动分割,避免长时间不清理导致硬盘存储资源浪费。

因为不允许随便使用第三方包,所以只能用标准库的logging。一开始想的方法比较挫——对文件加锁,但改来改去发现根本不能给别人review。翻python官方文档的时候发现logging库有个
QueueHandler

QueueListener
,简单试了下感觉逻辑还算清楚,遂简单整理了下代码。

示例代码

目录结构如下,main.py是入口脚本,logs目录和app.log将有程序运行时自动生成,主要日志功能放在
pkg/log.py
文件中。
pkg/__init__.py
为空文件,仅用于标识为python包。

.
├── main.py
├── logs
│   └── app.log
└── pkg
    ├── __init__.py
    └── log.py

pkg/log.py
内容如下,主要提供
logger
已经配置好的日志对象,该对象先将日志记录到QueueHandler,然后QueueListener从队列中取日志,并分别输出到控制台和日志文件中。
close_log_queue()
方法将在主进程结束时调用。

import logging
from logging.handlers import TimedRotatingFileHandler, QueueHandler, QueueListener
import sys
import os
# from queue import Queue
from multiprocessing import Queue

log_queue = Queue(-1)
queue_listener = ""


logdir = "logs"
logfile = f"{logdir}/app.log"
if not os.path.exists(logdir):
    os.makedirs(logdir, exist_ok=True)

def set_formatter():
    """设置日志格式化器"""
    fmt = "%(asctime)s | %(levelname)s | %(name)s | %(filename)s:%(lineno)d | %(funcName)s | %(message)s"
    datefmt = "%Y-%m-%d %H:%M:%S"
    return logging.Formatter(fmt, datefmt=datefmt)

def set_queue_handler():
    # 不要给QueueHandler重复设置formatter, 会引起重复嵌套
    handler = QueueHandler(log_queue)
    handler.setLevel(logging.INFO)
    return handler
def set_stream_handler(formatter: logging.Formatter):
    # 输出到控制台的日志处理器
    handler = logging.StreamHandler(sys.stdout)
    handler.setLevel(logging.WARNING)
    handler.setFormatter(formatter)
    return handler

def set_timed_rotating_file_handler(formatter: logging.Formatter):
    # 输出到文件的日志处理器, 每天生成一个新文件, 最多保留10个文件
    handler = TimedRotatingFileHandler(logfile, when="midnight", backupCount=10, encoding="utf-8")
    handler.setLevel(logging.INFO)
    handler.setFormatter(formatter)
    return handler

def close_log_queue():
    # 关闭队列监听器
    global queue_listener
    if queue_listener:
        queue_listener.stop()

def get_logger(name: str = "mylogger", level: int = logging.INFO):
    logger = logging.getLogger(name)
    logger.setLevel(level)

    formatter = set_formatter()

    stream_handler = set_stream_handler(formatter)
    file_handler = set_timed_rotating_file_handler(formatter)
    queue_handler = set_queue_handler()

    logger.addHandler(queue_handler)

    global queue_listener
    if not queue_listener:
        queue_listener = QueueListener(log_queue, stream_handler, file_handler, respect_handler_level=True)
        queue_listener.start()

    return logger


logger = get_logger()

if __name__ == "__main__":
    logger.info("test")
    close_log_queue()

main.py
内容如下,主要是创建子进程调用logger,观察日志输出是否正常。

from multiprocessing import Process
from pkg.log import logger, close_log_queue
import os

class MyProcess(Process):
    def __init__(self, delay):
        self.delay = delay
        super().__init__()

    def run(self):
        for i in range(self.delay):
            logger.info(f"pid: {os.getpid()}, {i}")

if __name__ == '__main__':
    logger.info(f"main process pid: {os.getpid()}")
    for i in range(10):
        p = MyProcess(10000)
        p.start()
        p.join()

    logger.info("main process end")
    close_log_queue()

执行输出大致如下所示:

$ tail logs/app.log 
2024-01-22 23:10:17 | INFO | mylogger | main.py:12 | run | pid: 7908, 1
2024-01-22 23:10:17 | INFO | mylogger | main.py:12 | run | pid: 7908, 2
2024-01-22 23:10:17 | INFO | mylogger | main.py:12 | run | pid: 7908, 3
2024-01-22 23:10:17 | INFO | mylogger | main.py:12 | run | pid: 7908, 4
2024-01-22 23:10:17 | INFO | mylogger | main.py:12 | run | pid: 7908, 5
2024-01-22 23:10:17 | INFO | mylogger | main.py:12 | run | pid: 7908, 6
2024-01-22 23:10:17 | INFO | mylogger | main.py:12 | run | pid: 7908, 7
2024-01-22 23:10:17 | INFO | mylogger | main.py:12 | run | pid: 7908, 8
2024-01-22 23:10:17 | INFO | mylogger | main.py:12 | run | pid: 7908, 9
2024-01-22 23:10:17 | INFO | mylogger | main.py:21 | <module> | main process end

补充

logging还内置很多其它handler,比如按文件大小自动切割,日志通过HTTP请求输出,日志输出到syslog等,可按照自己需求进行定制。

前言

今天分享一款前端基于Vue3,后端基于.NET8开源的免费(MIT license)、简单、快速、强大的前后端分离后台权限管理系统:中台Admin(Admin.Core)。

项目官方介绍

中台Admin(Admin.Core)是前后端分离权限管理系统,前端 UI 基于Vue3开发,后端 Api 基于.NET 8.0开发。支持多租户、接口权限、数据权限、动态 Api、任务调度、OSS 文件上传、滑块拼图验证、国内外主流数据库自由切换和动态高级查询。集成统一认证授权、事件总线、数据验证、分布式雪花Id、分布式缓存、分布式事务、IP 限流、性能分析、集成测试、健康检查、接口文档等。

功能介绍

中台Admin后台权限管理系统包括如下模块:用户管理、角色管理、部门管理、权限管理、租户套餐、租户管理、字典管理、任务调度、缓存管理、接口管理、视图管理、文件管理、登录日志、操作日志。

项目源代码

后端代码

前端代码

新建项目

使用项目源码新建项目

# 克隆项目
git clone https://github.com/zhontai/Admin.Core.git

# 进入项目
cd Admin.Core

# 打开项目
打开 ZhonTai.sln 解决方案

# 运行项目
设置 ZhonTai.Host 为启动项目 Ctrl + F5 直接编译运行项目
或 在 ZhonTai.Host 目录打开 cmd 输入 dotnet run 命令运行项目

# 打包发布
选择 ZhonTai.Host 右键菜单点击发布

使用项目模板新建项目

# 安装模板
dotnet new install ZhonTai.Template

# 查看帮助
dotnet new MyApp -h

# 新建项目
dotnet new MyApp -n MyCompanyName.MyProjectName

# 运行项目
设置 MyCompanyName.MyProjectName.Host 为启动项目 Ctrl + F5 直接编译运行项目
或 在 MyCompanyName.MyProjectName.Host 目录打开 cmd 输入 dotnet run 命令运行项目

# 打包发布
选择 MyCompanyName.MyProjectName.Host 右键菜单点击发布

项目功能截图

在线体验地址:
https://admin.zhontai.net/login

项目源码地址



更多项目实用功能和特性欢迎前往项目开源地址查看

新年到了,也是提醒我们 Visual Studio 支持生命周期中即将到来的日期的好时机。对 Visual Studio 2013 的支持将于今年4月9日结束。如果您使用的是旧版本的Visual Studio,我们希望您在使用 Visual Studio 时保持高效和安全,并强烈建议您升级到 Visual Studio 2022。

此外,Visual Studio 2019 将于2024年4月9日从主流(Mainstream)过渡到扩展支持(Extended Support )。

此外,如果您正在使用 Visual Studio 2017 或 2019 的社区版,我们建议您升级到 Visual Studio 2022 社区版,这是我们的第一个64位 IDE,它还包含编辑器中关键的生产力改进,内置对 Git 版本控制的支持,与 GitHub 的丰富集成,人工智能驱动的代码完成以及 .NET 8 和 .NET MAUI 的工具。

旧版本支持时间表

如果你还在使用旧版本的 Visual Studio,这里有一个关键支持生命周期日期的提醒。

  • Visual Studio 2013 的扩展支持将持续到2024年4月9日。在扩展支持期间,我们将只针对安全问题提供修复程序。您必须使用 Update 5 才能继续获得支持。
  • Visual Studio 2015 的扩展支持将持续到2025年10月14日。在扩展支持期间,我们将只针对安全问题提供修复程序。您必须使用 Update 3 才能继续获得支持。
  • Visual Studio 2017 的扩展支持将持续到 2027年4月13日。在扩展支持期间,我们将只针对安全问题提供修复程序。您必须使用15.9版本才能继续获得支持。
  • Visual Studio 2019 版本16.11在2024年4月9日之前是主流支持,然后将过渡到2029年4月10日之前的扩展支持。您必须使用版本16.11才能继续获得支持。
  • Visual Studio 2019 预览通道不再接收更新。我们建议用户迁移到 Visual Studio 2019 发布通道(版本16.11)或 Visual Studio 2022 预览版,以保持安全并接收最新的功能更新。

Visual Studio 2013 退役

2024年4月9日,对所有 Visual Studio 2013 版本、相关产品、运行时和组件的支持将终止,并且它们将不再接收安全性或任何其他更新。这些包括:

  • Visual Studio 2013 Ultimate、Premium、Professional、Community、Express for Web、Express for Windows、Express for Windows Desktop、Team Explorer 和 Test Professional 版本。
  • Visual C++ Redistributable for Visual Studio 2013
  • Visual Studio 2013 Shell(集成和隔离)。
  • Visual Studio 2013 版本的组件包括 Deployment Agents,Agent IntelliTrace Collector, Microsoft Azure Tools,Multibyte MFC Library,Remote Tools,Release Management,SDK, SQL Server Data Tools, Team Explorer 和 Web Tools Extensions。

升级到 Visual Studio 2022

Visual Studio 2022 是我们有史以来最高效的 IDE,我们建议所有客户升级到它。在 Visual Studio 2022 中,我们提供了三个通道:预览通道,以便您可以提供有关最新功能的早期反馈。当前通道,当前通道提供了被广泛使用的新功能,用户应该安装当前通道的每个小更新,以保持与新的发布功能同步。最后,对于企业和专业用户,我们提供长期服务通道(LTSCs),让您的开发团队在采用新功能版本时有更多的控制权。我们在  LTSCs 发布后的18个月内提供质量和安全修复。

我们建议用户升级到最新版本的 Visual Studio 以继续获得支持。有关所有版本 Visual Studio 的终止支持日期,请参阅 Microsoft 产品生命周期中心。今天升级!

联系我们!

我们感谢您花时间报告问题/建议,并希望您在使用 Visual Studio 时继续给我们反馈,告诉我们您喜欢什么以及我们可以改进什么。您的反馈对于帮助我们使 Visual Studio 成为最好的工具至关重要!您可以通过开发者社区与我们分享反馈:通过报告任何错误或问题,并分享您对新功能或改进现有功能的建议。

通过在 YouTube, Twitter, LinkedIn, Twitch 和 Microsoft Learn 上关注我们与 Visual Studio 团队保持联系。

原文链接:https://devblogs.microsoft.com/visualstudio/visual-studio-2013-retirement-support-reminder-for-older-versions-of-visual-studio/

在这个快节奏的生活中,我们努力地在平衡工作、生活和个人发展,但常常感到时间不够用。如何在繁忙的日程中找到一丝丝“喘息”的机会,这个名叫 cal.com 开源项目能让你更轻松地管理日程。不管每天再怎么忙也不要忘记给自己充电,这份系统设计入门教程 system-design-101,能让你利用碎片时间给自己充电,将成为架构师的事情提上日程。

众所周知,市面上的游戏引擎内核大多是采用 C/C++ 开发,云风(及其公司的小伙伴)竟然用 Lua 搞游戏引擎,一出手就是一周涨了 2k Star 的开源游戏引擎,这或许就是大师的手笔。程序员喜欢用代码将“琐碎”的事情程序化,open-interpreter 可以省去写脚本的步骤,用自然语言更好操作电脑。

这一通操作下来,空出来时间用来干点什么呢?嗯...要不打把游戏吧,刚出炉的「GitHub Game Off」最佳游戏绝对值得一玩!

  • 本文目录
    • 1. 开源新闻
      • 1.1 GitHub Game Off 结果出炉
    • 2. GitHub 热搜项目
      • 2.1 强大的日程安排平台:cal.com
      • 2.2 系统设计入门教程:system-design-101
      • 2.3 专注移动平台的游戏引擎:ant
      • 2.4 生成 PDF 文件的库:QuestPDF
      • 2.5 开放解释器:open-interpreter
    • 3. HelloGitHub 热评
      • 3.1 多显示器鼠标平滑移动的工具:LittleBigMouse
      • 3.2 一款开箱即用的 wiki 平台:BookStack
    • 4. 往期回顾

下面,让我们一起看看,过去一周开源领域都发生了什么,关注开源最新动态、品热搜开源项目。

1. 开源新闻

1.1 GitHub Game Off 结果出炉

Game Off 是一场由 GitHub 主办的年度游戏开发挑战赛,旨在鼓励游戏开发者创作、开源和展示他们的作品。挑战赛通常于每年 11 月初开始,为期一个月。GitHub 会在挑战赛开始前发布一个特定的主题,2023 年的主题是 {Scale},参赛者需要基于这一主题创作他们的游戏。


近期,2023 年 GitHub Game Off 的游戏评审已结束,下面分享本次比赛(600 多款游戏)评分前 3 名的游戏


Redis24篇集合

1 背景

互联网产品为了保证高性能和高可用性,经常会使用缓存来进行架构设计。最常用的就是使用Redis了,也有部分企业会选择使用Memcache。
所以了解 Redis 和 Memcache 的区别、共性以及各自应用场景,有助于我们在做技术选型的时候,有合理的判断依据。

2 Redis 和 Memcache 的区别和共同点

Redis和Memcache都是非常流行的内存数据存储系统,但它们在设计和使用上有一些关键的区别。

2.1 区别

2.1.1 数据结构

Redis支持更丰富的数据类型(即更复杂的应用场景),包括字符串、哈希表、列表、集合、有序集合等,使得它不仅仅是一个简单的键值对存储系统。而Memcache只支持简单的键值对存储,不支持复杂的数据结构。
image

2.1.2 存储方式

Redis将数据存储在内存中,但也可以将数据持久化到磁盘中,从而保证了数据的可靠性和持久性。而Memcache只将数据存储在内存中,当服务器重启或发生故障时,数据可能会丢失。
image

2.1.3 灾难故障恢复机制

因为可以把缓存中的数据持久化到磁盘上,所以Redis可以对大部分数据进行恢复,比如

  • RDB内存快照:指的是 Redis 内存中的数据在某一刻的状态。就好比如是拍照一样,你把那一刻的数据都定格下来,持久化到磁盘上。打游戏的同学可以想象为游戏存盘。 快照文件我们称之为 RDB 文件,即 Redis DataBase 的缩写。
  • AOF缓存日志:指的是使用日志存储 Redis 服务器的顺序指令序列,AOF 日志记录对内存进行修改的指令记录。

但是需要提醒的是,我们可不建议redis当作数据库用,因为:

  • Redis的定期快照RDB依旧有数据丢失的可能性
  • AOF损耗性能,降低效率,且数据量不能太大

就像Redis官方建议的那样,专业的事情还是交给就专业的,比如持久就是用MySQL、Redis。

2.1.4 性能

Redis的性能通常优于Memcache,因为它支持多种数据结构和高级功能,同时还可以通过多种持久化方式在数据量较大时提高性能。

  1. 高效率的存储类型

在 Redis 缓存中,常用的主要数据类型有五种,如下:

  • 字符串/REDIS_STRING
    :适用于 缓存、计数、共享Session、IP统计、分布式锁等。
  • 列表/REDIS_LIST
    : 链表、消息队列、栈、有序的对象列表(如朋友圈的点赞顺序列表、评论顺序列表)。
  • 哈希表/REDIS_HASH
    : 购物车信息、用户信息、Hash类型的(key, field, value)存储对象等。
  • 集合/REDIS_SET
    :无序的唯一的键值结构: 好友、关注、粉丝、感兴趣的人集合等。
  • 有序集合/REDIS_ZSET
    :访问排行榜、点赞排行、粉丝数排行等。
    上面这5种Redis 支持的数据类型,能够满足不同业务场景下的数据结构需求。而对于这几类数据类型的区分和支持,目的无非也是为了效率,具体的业务中使用恰当的数据结构才能保证得到应有的效率。
  1. 单线程和 I/O 多路复用模型,有如下优势
    • 单线程模式,内核持续监听 socket 上的连接及数据请求,一监听就交予Redis线程处理,达到单个线程处理多个I/O 流的效果。
    • epoll 提供了基于事件的回调机制。不同事件调用对应的事件处理器。Redis可以持续性的高效处理事件,性能同步提升。
    • Redis 不阻塞任一客户端发起的请求,所以可以同时和多个客户端连接并处理请求,提升并发执行的能力。

更多内容请参考笔者这篇:
Redis系列1:深刻理解高性能Redis的本质

2.1.5 高可用集群模式

Memcache 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 Redis 目前是原生支持 主从、Sentinel(哨兵)、Cluster(集群) 模式的。
image
参考笔者这篇
Redis系列5:深入分析Cluster 集群模式

2.1.6 线程模型对比

Memcache 是多线程,非阻塞 IO 复用的网络模型;Redis 使用单线程的多路 IO 复用模型,在Redis 6.0 的时候针对网络数据的读写引入了多线程模型。
image

更多内容参考笔者这篇:
追求性能极致:Redis6.0的多线程模型

2.1.7 扩展性对比

Redis和Memcache都具有良好的可扩展性,可以通过添加更多的节点来扩展集群。然而,Redis的扩展性更好,因为它支持多种数据结构和高级功能(发布订阅模型、Lua 脚本、事务等),
可以更好地适应不同的应用场景,支持更多的编程语言。并且支持可拔插的Redis Module,咱们上一篇有介绍到。
更多内容参考笔者这几篇:
Redis系列12:Redis 的事务机制
Redis系列:使用 Redis Module 扩展功能

2.1.8 过期删除策略

Redis 支持惰性删除和定期删除,如下图定期删除:
默认每 1 秒运行 10 次,也就是每 100 ms 执行一次,每次随机抽取一些设置了过期时间的 key(这边注意不是检查所有设置过期时间的key,而是随机抽取部分),检查是否过期,如果发现过期了就直接删除。
image
更多内容参考笔者这篇:
Redis系列18:过期数据的删除策略

2.2 共同点

  • 两者都是内存数据存储系统,将数据存储在内存中,从而提供快速的读取和写入速度。所以都用做高速缓存使用。性能都很优秀。
  • 都有过期策略,都具备惰性删除能力。
  • 两者都可以通过集群扩展来提高可扩展性和可靠性。

3 技术选型讨论

从上面的那些梳理可以看出,Redis的性能、功能丰富程度、能力扩展性都有一些优势。但我们还是需要从业务特征来进行选型参考。
比如 ,以下场景中,我们更趋向选择Redis:

3.1 更复杂数据结构

如果需要存储复杂的数据,建议使用Redis,因为它支持哈希(HASH),列表(LIST),集合(SET),有序集合(ZSET)这类复杂的数据结构Memcache是无法满足的。
典型场景如下:计数、分布式锁、消息队列、购物车信息、分析、专注、点赞等

3.2 持久化需求

如果你的缓存数据比较重要,需要有持久化兜底,避免故障时完全覆灭。
那你只能选择Redis,因为Memcache无法满足持久化的需求。
Redis的数据持久化包含:

  • RDB内存快照:把某一刻的数据都定格下来,持久化到磁盘上。
  • AOF缓存日志:缓存修改指令记录,可以通过指令对数据进行恢复。

3.3 高可用需求

如果有更高的稳定性需求,建议使用Redis,Redis支持主从、哨兵、集群三种高可用模式。特别是Cluster模式:

  • 数据复制
  • 故障检测
  • 主从故障转移
  • 读写分离

集群模式的故障转移等能力对业务是透明的,保障业务服务的稳定需求。

3.4 大Key存储需求

Memcache 的value存储,最大为1M。但在实际业务场景中,经常会遇到存储Value很大的对象,我们称之为大Key,这种情况只能使用Redis。

3.5 需要较强的扩展能力

Redis的扩展性更好,因为它支持多种数据结构和高级功能(发布订阅模型、Lua 脚本、事务等),并且支持可拔插的Redis Module。
Redis系列:使用 Redis Module 扩展功能

3.6 Memcache 选型参考

如果你的应用只需要简单的键值对存储,数据量很大,并发量也大。
并且对数据的持久性和可靠性要求不高,那么Memcache可能是更合适的选择,因为它更加轻量级且并发性能也很高。

4 总结

本文做了比较详细的介绍和比较,总的来说Redis基本覆盖Memcache的能力。这也是为什么现在的互联网缓存组件优先选择Redis的原因。