ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Watermill 贡献指南:从 Issue 认领到自研 Pub/Sub 的完整开发流程

Watermill 贡献指南:从 Issue 认领到自研 Pub/Sub 的完整开发流程 Watermill 贡献指南从 Issue 认领到自研 Pub/Sub 的完整开发流程【免费下载链接】watermillBuilding event-driven applications the easy way in Go.项目地址: https://gitcode.com/GitHub_Trending/wa/watermillWatermillmessage/pubsub.go 中定义的message.Publisher/message.Subscriber接口是一个以用最简单的方式在 Go 中构建事件驱动应用为目标的库。本文面向有意向该项目贡献代码的开发者无论你想从good first issue起步还是计划提交一个全新的 Pub/Sub 实现本文都会带你走完从选题、本地开发、代码规范到通过官方通用测试套件的完整路径。读完本文你将掌握 Watermill 的贡献工作流、Makefile提供的常用开发命令以及一套经过全部 Pub/Sub 实现验证的接口契约与测试标准。参与方式总览Watermill 团队欢迎任何形式的贡献。在 docs/content/development/contributing.md仓库根目录另有内容基本一致的 CONTRIBUTING.md中官方列出了几条明确的参与路径认领现有 Issue绝大多数 Issue 带有工作量预估标签S - small 小、M - medium 中、L - large 大可根据自己的时间选择实现新的 Pub/Sub基于某个技术栈编写全新的 Pub/Sub 实现可以在你的私有仓库先行开发提交新想法Issue 列表中没有覆盖的想法可以新建 Issue 描述并建议在动手实现生产级代码之前先到 Discord 上交流对齐。无论选择哪条路径都建议先在社区里沟通想法、产出 Proof of ConceptPoC后再大规模实现——文档明确提醒在实现某些可以被简化或更轻松完成的功能之前先讨论也许能帮你省下大量时间。从现有 Issue 入手Issue 列表是贡献者最直接的入口官方维护了两类过滤好的列表Good first issues标注了good first issue的简单任务适合初次接触项目、想先熟悉代码库的开发者Help wanted issues标注了help wanted的任务通常需求描述已经比较清晰可以较快开始实现。在 CONTRIBUTING.md 中还有一条重要提醒在项目组织内你无法直接向 master 分支推送改动所有改动都应通过 Pull Request 提交。新增 Pub/Sub 实现正确的打开方式独立仓库起步官方辅助迁移如果你有一个基于某项技术甚至是一些疯狂的想法比如基于实体邮件的 Pub/Sub的新实现文档给出的建议是先在你的私有仓库里完成实现如果后续希望官方托管可以迁移到ThreeDotsLabs/watermill-[name]组织仓库迁移后你将保留该仓库的 maintainer 权限并且会被邀请加入仅限维护者的 Discord 频道。实现前必读接口契约动手前请先阅读 docs/content/development/pub-sub-implementing.md。核心要求是任何自定义 Pub/Sub 都必须实现message.Publisher和message.Subscriber两个接口可选实现message.SubscribeInitializer用于订阅前的初始化。完整接口定义见 message/pubsub.go// Publisher is the emitting part of a Pub/Sub. type Publisher interface { // Publish publishes provided messages to the given topic. // // Publish can be synchronous or asynchronous - it depends on the implementation. // // Most publisher implementations dont support atomic publishing of messages. // This means that if publishing one of the messages fails, the next messages will not be published. // // Publish does not work with a single Context. // Use the Context() method of each message instead. // // Publish must be thread safe. Publish(topic string, messages ...*Message) error // Close should flush unsent messages if publisher is async. Close() error } // Subscriber is the consuming part of the Pub/Sub. type Subscriber interface { // Subscribe returns an output channel with messages from the provided topic. // The channel is closed after Close() is called on the subscriber. // // To receive the next message, Ack() must be called on the received message. // If message processing fails and the message should be redelivered Nack() should be called instead. // // When the provided ctx is canceled, the subscriber closes the subscription and the output channel. // The provided ctx is passed to all produced messages. // When Nack or Ack is called on the message, the context of the message is canceled. Subscribe(ctx context.Context, topic string) (-chan *Message, error) // Close closes all subscriptions with their output channels and flushes offsets etc. when needed. Close() error }接口注释里的隐藏规范message/pubsub.go中逐条注释本身就是一份接口契约从源码结构可以提炼出以下关键约束Publish必须线程安全且发布是否阻塞、是否原子取决于实现消息使用各自的Context()见 message/message.go 的NewMessage/NewMessageWithContext而不是统一传入单个 ContextSubscribe返回的输出 channel 在Close()后被关闭ctx 取消时订阅随之关闭Ack()/Nack()语义收到消息后必须调用Ack()才能继续消费下一条处理失败需调用Nack()以触发重投递。两者的实现都是非阻塞、幂等的详见 message/message.go 中基于关闭 channel 的ack/noAck机制。可选的SubscribeInitializermessage.SubscribeInitializer接口message/pubsub.go用于在消费前初始化订阅例如某些需要先建立 subscription 的云服务。它是可选的在先 Subscribe 后 Publish的使用场景下可以不需要主要服务于性能优化等特定目的。官方通用测试套件通过它才算生产就绪Watermill 为所有 Pub/Sub 提供了一套通用测试套件任何实现都应通过它才能被视为生产就绪production ready。套件位于 pubsub/tests/test_pubsub.go入口函数为TestPubSubpubsub/tests/test_pubsub.go#L34-L91func TestPubSub( t *testing.T, features Features, pubSubConstructor PubSubConstructor, consumerGroupPubSubConstructor ConsumerGroupPubSubConstructor, )该函数会串起一组完整的基础场景测试从源码可见其覆盖范围包括测试函数验证内容TestPublishSubscribe最基本的发布/订阅含 Payload 与 Metadata 校验默认发布 100 条消息TestConcurrentSubscribe多并发订阅者消费默认 50 个订阅者 × 5000 条消息TestConcurrentSubscribeMultipleTopics并发订阅多个 topicTestResendOnErrorNack()后消息被重新投递TestNoAck未 Ack 前下一条消息被阻塞需GuaranteedOrderTestContinueAfterSubscribeClose关闭订阅后消息不丢失需PersistentTestConcurrentClose并发关闭的正确性TestContinueAfterErrors失败后继续处理TestPublishSubscribeInOrder消息顺序保证需GuaranteedOrderTestPublisherClose大量发布时关闭不丢消息TestTopic多 topic 隔离TestMessageCtx/TestSubscribeCtx消息与订阅 Context 生命周期TestNewSubscriberReceivesOldMessages新订阅者收到历史消息需NewSubscriberReceivesOldMessagesTestReconnect通过RestartServiceCommand重启 broker 后自动重连不并行运行TestConsumerGroups消费组行为需ConsumerGroups通过ConsumerGroupPubSubConstructor运行另有压力测试入口TestPubSubStressTestpubsub/tests/test_pubsub.go#L215-L233默认循环执行 10 轮TestPubSub可通过环境变量STRESS_TEST_COUNT调整轮数。Features声明你的 Pub/Sub 能力边界套件不要求每个实现支持全部特性。tests.Featurespubsub/tests/test_pubsub.go#L93-L137让实现方声明自己支持的能力测试据此跳过不适用的用例字段含义ConsumerGroups是否支持消费组ExactlyOnceDelivery是否支持精确一次投递GuaranteedOrder是否保证消息顺序GuaranteedOrderWithSingleSubscriber是否仅在单订阅者时保证顺序Persistent消息是否跨实例持久化实践中仅 GoChannel 不支持RestartServiceCommand用于重连测试的 broker 重启命令如[]string{docker, restart, rabbitmq}RequireSingleInstance是否需要单实例才能正常工作如 GoChannelNewSubscriberReceivesOldMessages新订阅者是否可读到已消费的历史消息如 KafkaGenerateTopicFunc/GenerateIDFunc自定义 topic 名与测试 ID 生成ForceShort强制以短模式运行适合较慢或有局限的 Pub/SubContextPreserved消息发布与消费时 Context 是否被保留一个真实的接入范例GoChannel仓库自带的进程内 Pub/Sub——GoChannel 就是接入这套测试的样板。在 pubsub/gochannel/pubsub_test.go 中可以看到它如何声明自己的特性并接入套件func TestPublishSubscribe_persistent(t *testing.T) { tests.TestPubSub( t, tests.Features{ ConsumerGroups: false, ExactlyOnceDelivery: true, GuaranteedOrder: false, Persistent: false, RequireSingleInstance: true, }, createPersistentPubSub, nil, ) }而 pubsub/gochannel/pubsub_stress_test.go 则通过tests.TestPubSubStressTest(...)接入压力测试。同时pubsub/gochannel/pubsub.go 的Config结构也提供了OutputChannelBuffer、Persistent、BlockPublishUntilSubscriberAck、PreserveContext等选项可作为新实现设计配置项时的参考。其余内置实现的接入方式如 Kafka、NATS、AMQP 等可在 pubsubs 文档与各示例目录中查看。调试与 FAQ如果测试失败官方推荐查阅 docs/content/docs/troubleshooting.md 中的Debugging Pub/Sub tests一节。遇到实现细节不清楚时也可以随时通过文档中的支持渠道求助。新想法与 PoC 讨论对于未被 Issue 覆盖的想法官方建议先发 Issue 描述想法实现前先在 Discord/GitHub 上讨论——有些想法其实可以被简化或换种更简单的方式实现先沟通可以避免在错误方向上投入大量时间先产出 Proof of Concept 与社区对齐再写生产级代码。本地开发环境Makefile 与 docker-composeWatermill 为本地开发提供了完备的工具链。贡献指南指出Makefile 和 docker-compose用于 Pub/Sub是你的好朋友本地运行的测试与 CI 完全一致。常用命令见 Makefile命令作用make updocker-compose up拉起依赖的 Pub/Sub 中间件如 Kafka、NATS、RabbitMQ 等make test运行全部测试等价于go test ./...make test_short运行短测试go test ./... -short适合改动后做快速检查make fmt执行go fmt与goimports格式化代码Makefile 中还提供了更多开发辅助命令从源码可以看到make test_v— 带 verbose 输出的测试make test_race— 短测试 -race竞态检测go test ./... -short -racemake test_stress— 压力测试go test -tagsstress -timeout30m ./...make test_codecov— 生成覆盖率报告make test_reconnect— 带reconnectbuild tag 的重连测试make build— 编译全部包make update_examples_deps/make validate_examples— 分别通过 dev/update-examples-deps 与 dev/validate-examples 更新和校验示例依赖。测试超时与并行说明在 pubsub/tests/test_pubsub.go 中默认超时设为 15 秒defaultTimeoutTestReconnect被标记为NotParallel其余用例默认并行执行t.Parallel()因此重启 broker 的重连测试不会与其他用例并发。测试文件顶部注释也明确RunOnlyFastTests()会在-short且未开-race时返回 true用于跳过较慢的测试。代码规范Code standardsWatermill 对代码质量有明确要求贡献代码前请对照以下清单运行make fmt统一go fmtgoimports格式遵循 CodeReviewCommentsGo 官方代码评审常见意见汇总遵循 Effective GoGo 官方最佳实践符合 SOLID 原则开放配置、不绑定序列化方式代码应当对配置开放并且不耦合于任何特定的序列化方法。官方给出的范例是 AMQP Pub/Sub 将 marshaler 与 config 拆分为独立组件——这一marshaler 可替换的设计理念在 pub-sub-implementing.md 的 TODO 清单中被再次强调可替换且可配置的消息 marshaler。新增 Pub/Sub 的 TODO 清单pub-sub-implementing.md 中列出了一些实现时容易遗漏的要点逐一核对可避免返工日志良好的日志消息与合适的日志级别可参考 GoChannel 在Publish/Subscribe/Close中通过watermill.LoggerAdapter输出的 Trace/Debug/Info 日志见 pubsub/gochannel/pubsub.go可替换且可配置的消息 marshalerPublisher 与 Subscriber 的Close()实现需满足三个条件幂等在 Publisher/Subscriber 被阻塞例如等待 Ack时也能正确关闭在订阅者输出 channel 被阻塞没有消费者监听时也能正确关闭消费到的消息必须支持Ack()和Nack()Nack()后消息必须重新投递接入通用测试套件即上文tests.TestPubSub调试时参考 troubleshooting 指南性能优化编写 GoDoc、Markdown 文档与 Getting Started 示例对应 pubsubs 文档 与 learn/getting-started。完成以上工作后即可提交 Pull Request任何不清楚的地方都可以通过文档列出的支持渠道与维护者沟通。小结Watermill 的贡献路径非常清晰从good first issue起步熟悉代码库用Makefile命令保持本地环境与 CI 一致以官方通用测试套件pubsub/tests/test_pubsub.go作为实现质量的硬性门槛最后按代码规范与 TODO 清单打磨细节后提交 PR。其中接口契约 Features能力声明 通用测试的组合让新 Pub/Sub 可以在不重复造测试轮子的前提下获得与内置实现同等的质量保证——这也是项目能够围绕 message/pubsub.go 这一组小而稳定的接口快速扩展生态的关键所在。【免费下载链接】watermillBuilding event-driven applications the easy way in Go.项目地址: https://gitcode.com/GitHub_Trending/wa/watermill创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表