百万并发抽奖系统细节讨论
今天是第二天和 AI 聊抽奖系统。
主要集中下面几个话题:
- 具体的概率算法实现方式(权重法 vs 区间法)
- 前端转盘的动画实现方案(CSS动画 vs Canvas)
- 管理后台免登录实现
- 错误码和异常处理规范
- 是否需要连接池监控和慢查询日志
编码只应该占据项目时间的20%,也就是有了ai, 你也应该和ai讨论各种细节。最后再让ai写代码,这样写出来的才是符合你预期的代码。
今天是第二天和 AI 聊抽奖系统。
主要集中下面几个话题:
编码只应该占据项目时间的20%,也就是有了ai, 你也应该和ai讨论各种细节。最后再让ai写代码,这样写出来的才是符合你预期的代码。
Raft 是一种用于分布式系统的共识算法,它的设计目标是让分布式一致性更容易理解和实现。
核心思想
Raft 通过选举一个领导者(Leader)来简化共识过程。所有的数据修改都必须先提交到 Leader,然后由 Leader 负责将日志复制到其他节点(Followers)。这种方式比 Paxos 等算法更直观。
三种角色
系统中的每个节点都处于以下三种状态之一:Leader 负责处理所有客户端请求并管理日志复制,Follower 是被动的,只响应 Leader 和 Candidate 的请求,Candidate 是在选举新 Leader 时的临时状态。
工作流程
系统启动时,所有节点都是 Follower。如果 Follower 在一定时间内没有收到 Leader 的心跳,它会转变为 Candidate 并发起选举。Candidate 向其他节点请求投票,如果获得多数票就成为 Leader。Leader 会定期向所有 Follower 发送心跳来维持其地位。
日志复制
当客户端向 Leader 发送请求时,Leader 将操作追加到自己的日志中,然后并行地将这个日志条目复制到所有 Follower。一旦多数节点确认收到并存储了该日志,Leader 就会提交这个条目并返回结果给客户端。
安全保证
Raft 保证已经提交的日志不会丢失,并且所有节点最终会按相同的顺序执行相同的命令。即使发生网络分区或节点故障,系统也能保持一致性。选举时会确保拥有最新日志的节点才能当选为 Leader。
Raft 被广泛应用于 etcd、Consul 等分布式系统中,它的可理解性使得工程师更容易正确实现和调试分布式一致性系统。
今天开始做开源项目。
一方面因为自己做的事情我感觉没啥挑战,锻炼下技术能力,基于场景深挖下。
另一方面是近些年的项目经历实在过于单薄,感觉拿不出手。除了全栈,多面手,就没亮点了。
考虑到我近些年的经验主要是 web开发。
所以我预计做 4 个项目,慢慢做。会写下自己的思考过程,以及实现过程中遇到的坑,还有一些反思。
因为是验证想法和达到目的,所以主要是vibe coding.
我有github copliot, claude code, droid, iflow cli.
主要编程工具是 zed, vscode.
考虑到主要是验证自己的想法,所以就白嫖iflow cli,也行,国内的模型。vscode review代码。最终就是vscode+ iflow cli extention.
| 项目 | 关键主题 | 技术类型 |
|---|---|---|
| 1. 高并发抽奖系统 | 缓存 + 异步 + 压测 | 极限性能架构 |
| 2. AI内容平台 | 调度 + 自动化 + AI能力 | 智能系统架构 |
| 3. WebSocket系统 | 实时通信 + 分布式消息 | 实时体验架构 |
| 4. 🛒 电商核心系统 | 数据一致性 + 事务保真 | 企业级业务架构 |
day1: 建仓库:高并发抽奖系统
技术选型:
我个人会的技术栈是php/python/golang/rust
rust一直没用起来,所以我打算玩下rust后端, vue 做前端。
基于我之前阅读过极客课程,《如何做秒杀系统》。印象中的思想就是 层层过滤。
抽奖也是一样的, 你能不能抽中其实是个概率问题。
那么从性能优化的角度思考,我可以把概率计算放到每个环节。
继续解读下面的相关代码:
//
// AppState.swift
// Ice
//
import Combine
import SwiftUI
/// The model for app-wide state.
@MainActor
final class AppState: ObservableObject {
/// A Boolean value that indicates whether the active space is fullscreen.
@Published private(set) var isActiveSpaceFullscreen = Bridging.isSpaceFullscreen(Bridging.activeSpaceID)
/// Manager for the menu bar's appearance.
private(set) lazy var appearanceManager = MenuBarAppearanceManager(appState: self)
/// Manager for events received by the app.
private(set) lazy var eventManager = EventManager(appState: self)
/// Manager for menu bar items.
private(set) lazy var itemManager = MenuBarItemManager(appState: self)
/// Manager for the state of the menu bar.
private(set) lazy var menuBarManager = MenuBarManager(appState: self)
/// Manager for app permissions.
private(set) lazy var permissionsManager = PermissionsManager(appState: self)
/// Manager for the app's settings.
private(set) lazy var settingsManager = SettingsManager(appState: self)
/// Manager for app updates.
private(set) lazy var updatesManager = UpdatesManager(appState: self)
/// Manager for user notifications.
private(set) lazy var userNotificationManager = UserNotificationManager(appState: self)
/// Global cache for menu bar item images.
private(set) lazy var imageCache = MenuBarItemImageCache(appState: self)
/// Manager for menu bar item spacing.
let spacingManager = MenuBarItemSpacingManager()
/// Model for app-wide navigation.
let navigationState = AppNavigationState()
/// The app's hotkey registry.
nonisolated let hotkeyRegistry = HotkeyRegistry()
/// The app's delegate.
private(set) weak var appDelegate: AppDelegate?
/// The window that contains the settings interface.
private(set) weak var settingsWindow: NSWindow?
/// The window that contains the permissions interface.
private(set) weak var permissionsWindow: NSWindow?
/// A Boolean value that indicates whether the "ShowOnHover" feature is prevented.
private(set) var isShowOnHoverPrevented = false
/// Storage for internal observers.
private var cancellables = Set<AnyCancellable>()
/// A Boolean value that indicates whether the app is running as a SwiftUI preview.
let isPreview: Bool = {
#if DEBUG
let environment = ProcessInfo.processInfo.environment
let key = "XCODE_RUNNING_FOR_PREVIEWS"
return environment[key] != nil
#else
return false
#endif
}()
/// A Boolean value that indicates whether the application can set the cursor
/// in the background.
var setsCursorInBackground: Bool {
get { Bridging.getConnectionProperty(forKey: "SetsCursorInBackground") as? Bool ?? false }
set { Bridging.setConnectionProperty(newValue, forKey: "SetsCursorInBackground") }
}
/// Configures the internal observers for the app state.
private func configureCancellables() {
var c = Set<AnyCancellable>()
Publishers.Merge3(
NSWorkspace.shared.notificationCenter
.publisher(for: NSWorkspace.activeSpaceDidChangeNotification)
.mapToVoid(),
// Frontmost application change can indicate a space change from one display to
// another, which gets ignored by NSWorkspace.activeSpaceDidChangeNotification.
NSWorkspace.shared
.publisher(for: \.frontmostApplication)
.mapToVoid(),
// Clicking into a fullscreen space from another space is also ignored.
UniversalEventMonitor
.publisher(for: .leftMouseDown)
.delay(for: 0.1, scheduler: DispatchQueue.main)
.mapToVoid()
)
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
guard let self else {
return
}
isActiveSpaceFullscreen = Bridging.isSpaceFullscreen(Bridging.activeSpaceID)
}
.store(in: &c)
NSWorkspace.shared.publisher(for: \.frontmostApplication)
.receive(on: DispatchQueue.main)
.sink { [weak self] frontmostApplication in
guard let self else {
return
}
navigationState.isAppFrontmost = frontmostApplication == .current
}
.store(in: &c)
if let settingsWindow {
settingsWindow.publisher(for: \.isVisible)
.debounce(for: 0.05, scheduler: DispatchQueue.main)
.sink { [weak self] isVisible in
guard let self else {
return
}
navigationState.isSettingsPresented = isVisible
}
.store(in: &c)
} else {
Logger.appState.warning("No settings window!")
}
Publishers.Merge(
navigationState.$isAppFrontmost,
navigationState.$isSettingsPresented
)
.debounce(for: 0.1, scheduler: DispatchQueue.main)
.sink { [weak self] shouldUpdate in
guard
let self,
shouldUpdate
else {
return
}
Task.detached {
if ScreenCapture.cachedCheckPermissions(reset: true) {
await self.imageCache.updateCacheWithoutChecks(sections: MenuBarSection.Name.allCases)
}
}
}
.store(in: &c)
menuBarManager.objectWillChange
.sink { [weak self] in
self?.objectWillChange.send()
}
.store(in: &c)
permissionsManager.objectWillChange
.sink { [weak self] in
self?.objectWillChange.send()
}
.store(in: &c)
settingsManager.objectWillChange
.sink { [weak self] in
self?.objectWillChange.send()
}
.store(in: &c)
updatesManager.objectWillChange
.sink { [weak self] in
self?.objectWillChange.send()
}
.store(in: &c)
cancellables = c
}
/// Sets up the app state.
func performSetup() {
configureCancellables()
permissionsManager.stopAllChecks()
menuBarManager.performSetup()
appearanceManager.performSetup()
eventManager.performSetup()
settingsManager.performSetup()
itemManager.performSetup()
imageCache.performSetup()
updatesManager.performSetup()
userNotificationManager.performSetup()
}
/// Assigns the app delegate to the app state.
func assignAppDelegate(_ appDelegate: AppDelegate) {
guard self.appDelegate == nil else {
Logger.appState.warning("Multiple attempts made to assign app delegate")
return
}
self.appDelegate = appDelegate
}
/// Assigns the settings window to the app state.
func assignSettingsWindow(_ window: NSWindow) {
guard window.identifier?.rawValue == Constants.settingsWindowID else {
Logger.appState.warning("Window \(window.identifier?.rawValue ?? "<NIL>") is not the settings window!")
return
}
settingsWindow = window
configureCancellables()
}
/// Assigns the permissions window to the app state.
func assignPermissionsWindow(_ window: NSWindow) {
guard window.identifier?.rawValue == Constants.permissionsWindowID else {
Logger.appState.warning("Window \(window.identifier?.rawValue ?? "<NIL>") is not the permissions window!")
return
}
permissionsWindow = window
configureCancellables()
}
/// Opens the settings window.
func openSettingsWindow() {
with(EnvironmentValues()) { environment in
environment.openWindow(id: Constants.settingsWindowID)
}
}
/// Dismisses the settings window.
func dismissSettingsWindow() {
with(EnvironmentValues()) { environment in
environment.dismissWindow(id: Constants.settingsWindowID)
}
}
/// Opens the permissions window.
func openPermissionsWindow() {
with(EnvironmentValues()) { environment in
environment.openWindow(id: Constants.permissionsWindowID)
}
}
/// Dismisses the permissions window.
func dismissPermissionsWindow() {
with(EnvironmentValues()) { environment in
environment.dismissWindow(id: Constants.permissionsWindowID)
}
}
/// Activates the app and sets its activation policy to the given value.
func activate(withPolicy policy: NSApplication.ActivationPolicy) {
// Store whether the app has previously activated inside an internal
// context to keep it isolated.
enum Context {
static let hasActivated = ObjectStorage<Bool>()
}
func activate() {
if let frontApp = NSWorkspace.shared.frontmostApplication {
NSRunningApplication.current.activate(from: frontApp)
} else {
NSApp.activate()
}
NSApp.setActivationPolicy(policy)
}
if Context.hasActivated.value(for: self) == true {
activate()
} else {
Context.hasActivated.set(true, for: self)
Logger.appState.debug("First time activating app, so going through Dock")
// Hack to make sure the app properly activates for the first time.
NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.dock").first?.activate()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
activate()
}
}
}
/// Deactivates the app and sets its activation policy to the given value.
func deactivate(withPolicy policy: NSApplication.ActivationPolicy) {
if let nextApp = NSWorkspace.shared.runningApplications.first(where: { $0 != .current }) {
NSApp.yieldActivation(to: nextApp)
} else {
NSApp.deactivate()
}
NSApp.setActivationPolicy(policy)
}
/// Prevents the "ShowOnHover" feature.
func preventShowOnHover() {
isShowOnHoverPrevented = true
}
/// Allows the "ShowOnHover" feature.
func allowShowOnHover() {
isShowOnHoverPrevented = false
}
}
// MARK: AppState: BindingExposable
extension AppState: BindingExposable { }
// MARK: - Logger
private extension Logger {
/// The logger to use for the app state.
static let appState = Logger(category: "AppState")
}
好的,我们继续解读 AppState.swift 文件。
如果说 AppDelegate 是应用程序的“事件总管”,那么 AppState 就是整个应用程序的**“大脑”和“中央数据中心”**。它持有并管理着 app 运行所需的所有状态、数据和核心逻辑。AppDelegate 负责响应系统事件,然后通知 AppState 来进行实际的状态更新和处理。
//
// AppDelegate.swift
// Ice
//
import SwiftUI
@MainActor
final class AppDelegate: NSObject, NSApplicationDelegate {
private weak var appState: AppState?
// MARK: NSApplicationDelegate Methods
func applicationWillFinishLaunching(_ notification: Notification) {
guard let appState else {
Logger.appDelegate.warning("Missing app state in applicationWillFinishLaunching")
return
}
// Assign the delegate to the shared app state.
appState.assignAppDelegate(self)
// Allow the app to set the cursor in the background.
appState.setsCursorInBackground = true
}
func applicationDidFinishLaunching(_ notification: Notification) {
guard let appState else {
Logger.appDelegate.warning("Missing app state in applicationDidFinishLaunching")
return
}
// Dismiss the windows.
appState.dismissSettingsWindow()
appState.dismissPermissionsWindow()
// Hide the main menu to make more space in the menu bar.
if let mainMenu = NSApp.mainMenu {
for item in mainMenu.items {
item.isHidden = true
}
}
// Perform setup after a small delay to ensure that the settings window
// has been assigned.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
guard !appState.isPreview else {
return
}
// If we have the required permissions, set up the shared app state.
// Otherwise, open the permissions window.
switch appState.permissionsManager.permissionsState {
case .hasAllPermissions, .hasRequiredPermissions:
appState.performSetup()
case .missingPermissions:
appState.activate(withPolicy: .regular)
appState.openPermissionsWindow()
}
}
}
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
// Deactivate and set the policy to accessory when all windows are closed.
appState?.deactivate(withPolicy: .accessory)
return false
}
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
// MARK: Other Methods
/// Assigns the app state to the delegate.
func assignAppState(_ appState: AppState) {
guard self.appState == nil else {
Logger.appDelegate.warning("Multiple attempts made to assign app state")
return
}
self.appState = appState
}
/// Opens the settings window and activates the app.
@objc func openSettingsWindow() {
guard let appState else {
Logger.appDelegate.error("Failed to open settings window")
return
}
// Small delay makes this more reliable.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
appState.activate(withPolicy: .regular)
appState.openSettingsWindow()
}
}
}
// MARK: - Logger
private extension Logger {
static let appDelegate = Logger(category: "AppDelegate")
}
我没有swift开发经验。请尽可能详细地解释上述代码。
好的,我们来详细解释一下这段 Swift 代码。假设你没有任何 Swift 开发经验,我会用最通俗易懂的方式来讲解。
这段代码定义了一个名为 “Ice” 的 macOS 应用程序的 AppDelegate。你可以把 AppDelegate 想象成这个应用程序的**“总管”或“事件经理”**。当 app 的生命周期中发生关键事件时,比如 app 启动、关闭窗口、或者即将退出时,操作系统就会通知这个“总管”,由它来负责处理。
简单来说,这段代码为 “Ice” 这个 app 定义了启动时的核心行为。它的主要工作包括: