项目结构:

Go
/*
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Push & Pull Pattern(推 - 拉)模式
# Author : geovindu,Geovin Du 涂聚文.
# IDE : goLang 2024.3.6 go 26.2
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/6/25 21:25
# User : geovindu
# Product : GoLand
# Project : godesginpattern
# File : config.go
*/
package config
import "time"
// ZMQ 端口 go get github.com/zeromq/goczmq
const (
PortRawMaterial = 5555
PortProcess = 5556
PortQuality = 5557
PortSale = 5558
)
// 超时毫秒
const ZmqTimeoutMs = 3000
// 消息分隔符
const (
MsgSep = "|"
KvSep = ":"
)
// 连接地址
const (
LocalTcpAddr = "tcp://localhost"
BindAddr = "tcp://*"
)
// 珠宝品类常量
var JewelryCategory = []string{
"钻石戒指", "黄金项链", "翡翠手镯", "铂金耳钉", "彩宝吊坠",
}
// 质检等级
var QualityGrade = []string{
"S级(收藏)", "A级(精品)", "B级(常规)", "C级(特价)",
}
// 休眠间隔
const (
SleepRaw = 2 * time.Second
SleepProcess = 1 * time.Second
SleepQuality = 500 * time.Millisecond
SleepSale = 1 * time.Second
IdleSleep = 100 * time.Millisecond // 无消息空转休眠
)
/*
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Push & Pull Pattern(推 - 拉)模式
# Author : geovindu,Geovin Du 涂聚文.
# IDE : goLang 2024.3.6 go 26.2
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/6/25 21:26
# User : geovindu
# Product : GoLand
# Project : godesginpattern
# File : logger.go
*/
package utils
import (
"fmt"
"log"
"os"
"time"
)
func GetLogger(module string) *log.Logger {
prefix := fmt.Sprintf("[%s] ", module)
logger := log.New(os.Stdout, prefix, log.LstdFlags)
return logger
}
// 格式化时间输出辅助
func NowStr() string {
return time.Now().Format("2006-01-02 15:04:05")
}
/*
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Push & Pull Pattern(推 - 拉)模式
# Author : geovindu,Geovin Du 涂聚文.
# IDE : goLang 2024.3.6 go 26.2
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/6/25 21:26
# User : geovindu
# Product : GoLand
# Project : godesginpattern
# File : message.go
*/
package model
import (
"godesginpattern/pushpull/config"
"strings"
)
// BaseMessage 统一消息打包解包
type BaseMessage struct{}
// Pack map 转消息字符串
// 首字母大写,跨包可访问
var MsgHelper = &BaseMessage{}
func (b *BaseMessage) Pack(data map[string]string) string {
var parts []string
for k, v := range data {
parts = append(parts, k+config.KvSep+v)
}
return strings.Join(parts, config.MsgSep)
}
func (b *BaseMessage) Unpack(raw string) map[string]string {
res := make(map[string]string)
items := strings.Split(raw, config.MsgSep)
for _, item := range items {
kv := strings.SplitN(item, config.KvSep, 2)
if len(kv) == 2 {
res[kv[0]] = kv[1]
}
}
return res
}
/*
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Push & Pull Pattern(推 - 拉)模式
# Author : geovindu,Geovin Du 涂聚文.
# IDE : goLang 2024.3.6 go 26.2
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/6/25 21:25
# User : geovindu
# Product : GoLand
# Project : godesginpattern
# File : socket_factory.go
*/
package core
import (
"bufio"
"fmt"
"godesginpattern/pushpull/config"
"godesginpattern/pushpull/utils"
"io"
"net"
"sync"
"time"
)
var logger = utils.GetLogger("SocketFactory")
type TcpPushSocket struct {
listener net.Listener
clients []net.Conn
mu sync.Mutex
}
func NewTcpPushSocket(port int) *TcpPushSocket {
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {
logger.Fatalf("创建Push监听失败: %v", err)
}
sock := &TcpPushSocket{listener: listener}
go sock.acceptLoop()
logger.Printf("Push 绑定成功: tcp://localhost:%d", port)
return sock
}
func (s *TcpPushSocket) acceptLoop() {
for {
conn, err := s.listener.Accept()
if err != nil {
logger.Printf("Accept错误: %v", err)
return
}
s.mu.Lock()
s.clients = append(s.clients, conn)
s.mu.Unlock()
logger.Printf("新客户端连接: %s", conn.RemoteAddr())
}
}
func (s *TcpPushSocket) Send(msg string) (int, error) {
s.mu.Lock()
defer s.mu.Unlock()
for _, conn := range s.clients {
conn.SetWriteDeadline(time.Now().Add(time.Duration(config.ZmqTimeoutMs) * time.Millisecond))
if _, err := conn.Write([]byte(msg + "\n")); err != nil {
logger.Printf("发送失败: %v", err)
}
}
return len(msg), nil
}
func (s *TcpPushSocket) Close() {
s.mu.Lock()
defer s.mu.Unlock()
for _, conn := range s.clients {
conn.Close()
}
s.listener.Close()
}
type TcpPullSocket struct {
conn net.Conn
reader *bufio.Reader
}
func NewTcpPullSocket(port int) *TcpPullSocket {
conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port))
if err != nil {
logger.Fatalf("连接Push失败: %v", err)
}
logger.Printf("Pull 连接成功: tcp://localhost:%d", port)
return &TcpPullSocket{
conn: conn,
reader: bufio.NewReader(conn),
}
}
var ErrConnectionClosed = fmt.Errorf("connection closed")
func (s *TcpPullSocket) Recv(nonBlocking bool) (string, error) {
if nonBlocking {
s.conn.SetReadDeadline(time.Now().Add(50 * time.Millisecond))
} else {
s.conn.SetReadDeadline(time.Now().Add(time.Duration(config.ZmqTimeoutMs) * time.Millisecond))
}
msg, err := s.reader.ReadString('\n')
if err != nil {
if nonBlocking && (err == bufio.ErrBufferFull || isTimeoutError(err)) {
return "", fmt.Errorf("resource temporarily unavailable")
}
if err == io.EOF {
return "", ErrConnectionClosed
}
return "", err
}
return msg[:len(msg)-1], nil
}
func (s *TcpPullSocket) Close() {
s.conn.Close()
}
func isTimeoutError(err error) bool {
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
return true
}
return false
}
Go
/*
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Push & Pull Pattern(推 - 拉)模式
# Author : geovindu,Geovin Du 涂聚文.
# IDE : goLang 2024.3.6 go 26.2
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/6/25 21:26
# User : geovindu
# Product : GoLand
# Project : godesginpattern
# File : rawmaterial.go
*/
package service
import (
"godesginpattern/pushpull/config"
"godesginpattern/pushpull/core"
"godesginpattern/pushpull/model"
"godesginpattern/pushpull/utils"
"math/rand"
"strconv"
"time"
)
var rawLog = utils.GetLogger("RawMaterialService")
type RawMaterialService struct {
pushSock *core.TcpPushSocket
}
func NewRawMaterialService() *RawMaterialService {
return &RawMaterialService{
pushSock: core.NewTcpPushSocket(config.PortRawMaterial),
}
}
func (r *RawMaterialService) RunProduce(total int) {
rawLog.Println("原料采购服务启动")
rand.Seed(time.Now().UnixNano())
for i := 1; i <= total; i++ {
cat := config.JewelryCategory[rand.Intn(len(config.JewelryCategory))]
msg := model.MsgHelper.Pack(map[string]string{
"type": "原料订单",
"order_id": "RAW_" + strconv.Itoa(i),
"category": cat,
"status": "已采购待加工",
})
_, _ = r.pushSock.Send(msg)
rawLog.Printf("推送: %s", msg)
time.Sleep(config.SleepRaw)
}
rawLog.Println("原料订单全部推送完成")
time.Sleep(time.Second)
r.pushSock.Close()
}
/*
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Push & Pull Pattern(推 - 拉)模式
# Author : geovindu,Geovin Du 涂聚文.
# IDE : goLang 2024.3.6 go 26.2
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/6/25 21:26
# User : geovindu
# Product : GoLand
# Project : godesginpattern
# File : process.go
*/
package service
import (
"godesginpattern/pushpull/config"
"godesginpattern/pushpull/core"
"godesginpattern/pushpull/model"
"godesginpattern/pushpull/utils"
"math/rand"
"strings"
"time"
)
var procLog = utils.GetLogger("Process")
type ProcessService struct {
pullSock *core.TcpPullSocket
pushSock *core.TcpPushSocket
done chan struct{}
}
func NewProcessService() *ProcessService {
return &ProcessService{
pullSock: core.NewTcpPullSocket(config.PortRawMaterial),
pushSock: core.NewTcpPushSocket(config.PortProcess),
done: make(chan struct{}),
}
}
func (p *ProcessService) RunPipeline() {
procLog.Println("加工车间启动")
for {
select {
case <-p.done:
procLog.Println("加工车间停止")
return
default:
msg, err := p.pullSock.Recv(true)
if err != nil {
if err == core.ErrConnectionClosed {
procLog.Println("加工车间连接关闭")
return
}
if err.Error() == "resource temporarily unavailable" {
time.Sleep(config.IdleSleep)
continue
}
procLog.Printf("接收错误: %v", err)
time.Sleep(config.IdleSleep)
continue
}
data := model.MsgHelper.Unpack(msg)
procLog.Printf("收到: %s", msg)
time.Sleep(config.SleepProcess + time.Duration(rand.Intn(2000))*time.Millisecond)
sendMsg := model.MsgHelper.Pack(map[string]string{
"type": "成品珠宝",
"order_id": strings.Replace(data["order_id"], "RAW", "FIN", 1),
"category": data["category"],
"status": "已加工待质检",
})
_, _ = p.pushSock.Send(sendMsg)
procLog.Printf("加工完成: %s", sendMsg)
}
}
}
func (p *ProcessService) Stop() {
close(p.done)
p.pullSock.Close()
p.pushSock.Close()
}
/*
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Push & Pull Pattern(推 - 拉)模式
# Author : geovindu,Geovin Du 涂聚文.
# IDE : goLang 2024.3.6 go 26.2
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/6/25 21:26
# User : geovindu
# Product : GoLand
# Project : godesginpattern
# File : quality.go
*/
package service
import (
"godesginpattern/pushpull/config"
"godesginpattern/pushpull/core"
"godesginpattern/pushpull/model"
"godesginpattern/pushpull/utils"
"math/rand"
"time"
)
var qualityLog = utils.GetLogger("Quality")
type QualityService struct {
pullSock *core.TcpPullSocket
pushSock *core.TcpPushSocket
done chan struct{}
}
func NewQualityService() *QualityService {
return &QualityService{
pullSock: core.NewTcpPullSocket(config.PortProcess),
pushSock: core.NewTcpPushSocket(config.PortQuality),
done: make(chan struct{}),
}
}
func (q *QualityService) RunPipeline() {
qualityLog.Println("质检中心启动")
for {
select {
case <-q.done:
qualityLog.Println("质检中心停止")
return
default:
msg, err := q.pullSock.Recv(true)
if err != nil {
if err == core.ErrConnectionClosed {
qualityLog.Println("质检中心连接关闭")
return
}
if err.Error() == "resource temporarily unavailable" {
time.Sleep(config.IdleSleep)
continue
}
qualityLog.Printf("接收错误: %v", err)
time.Sleep(config.IdleSleep)
continue
}
data := model.MsgHelper.Unpack(msg)
qualityLog.Printf("收到: %s", msg)
time.Sleep(config.SleepQuality + time.Duration(rand.Intn(1500))*time.Millisecond)
sendMsg := model.MsgHelper.Pack(map[string]string{
"type": "质检成品",
"order_id": data["order_id"],
"category": data["category"],
"grade": config.QualityGrade[rand.Intn(len(config.QualityGrade))],
"status": "可销售",
})
_, _ = q.pushSock.Send(sendMsg)
qualityLog.Printf("质检完成: %s", sendMsg)
}
}
}
func (q *QualityService) Stop() {
close(q.done)
q.pullSock.Close()
q.pushSock.Close()
}
/*
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Push & Pull Pattern(推 - 拉)模式
# Author : geovindu,Geovin Du 涂聚文.
# IDE : goLang 2024.3.6 go 26.2
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/6/25 21:26
# User : geovindu
# Product : GoLand
# Project : godesginpattern
# File : sale.go
*/
package service
import (
"godesginpattern/pushpull/config"
"godesginpattern/pushpull/core"
"godesginpattern/pushpull/model"
"godesginpattern/pushpull/utils"
"math/rand"
"strconv"
"strings"
"time"
)
var saleLog = utils.GetLogger("Sale")
type SaleService struct {
pullSock *core.TcpPullSocket
pushSock *core.TcpPushSocket
done chan struct{}
}
func NewSaleService() *SaleService {
return &SaleService{
pullSock: core.NewTcpPullSocket(config.PortQuality),
pushSock: core.NewTcpPushSocket(config.PortSale),
done: make(chan struct{}),
}
}
func (s *SaleService) RunPipeline() {
saleLog.Println("销售部启动")
for {
select {
case <-s.done:
saleLog.Println("销售部停止")
return
default:
msg, err := s.pullSock.Recv(true)
if err != nil {
if err == core.ErrConnectionClosed {
saleLog.Println("销售部连接关闭")
return
}
if err.Error() == "resource temporarily unavailable" {
time.Sleep(config.IdleSleep)
continue
}
saleLog.Printf("接收错误: %v", err)
time.Sleep(config.IdleSleep)
continue
}
data := model.MsgHelper.Unpack(msg)
saleLog.Printf("收到: %s", msg)
time.Sleep(config.SleepSale + time.Duration(rand.Intn(1500))*time.Millisecond)
sendMsg := model.MsgHelper.Pack(map[string]string{
"type": "销售完成",
"order_id": strings.Replace(data["order_id"], "FIN", "SALE", 1),
"category": data["category"],
"price": strconv.Itoa(rand.Intn(49000)+1000) + "元",
"status": "已售出",
})
_, _ = s.pushSock.Send(sendMsg)
saleLog.Printf("销售完成: %s", sendMsg)
}
}
}
func (s *SaleService) Stop() {
close(s.done)
s.pullSock.Close()
s.pushSock.Close()
}
/*
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Push & Pull Pattern(推 - 拉)模式
# Author : geovindu,Geovin Du 涂聚文.
# IDE : goLang 2024.3.6 go 26.2
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/6/25 21:26
# User : geovindu
# Product : GoLand
# Project : godesginpattern
# File : aftersale.go
*/
package service
import (
"godesginpattern/pushpull/config"
"godesginpattern/pushpull/core"
"godesginpattern/pushpull/model"
"godesginpattern/pushpull/utils"
"time"
)
var afterLog = utils.GetLogger("AfterSale")
type AfterSaleService struct {
pullSock *core.TcpPullSocket
done chan struct{}
}
func NewAfterSaleService() *AfterSaleService {
return &AfterSaleService{
pullSock: core.NewTcpPullSocket(config.PortSale),
done: make(chan struct{}),
}
}
func (a *AfterSaleService) RunConsumer() {
afterLog.Println("售后维保启动")
for {
select {
case <-a.done:
afterLog.Println("售后维保停止")
return
default:
msg, err := a.pullSock.Recv(true)
if err != nil {
if err == core.ErrConnectionClosed {
afterLog.Println("售后维保连接关闭")
return
}
if err.Error() == "resource temporarily unavailable" {
time.Sleep(config.IdleSleep)
continue
}
afterLog.Printf("接收错误: %v", err)
time.Sleep(config.IdleSleep)
continue
}
data := model.MsgHelper.Unpack(msg)
afterLog.Printf("收到单据: %s", msg)
afterLog.Printf("✅ 维保生效 | 品类:%s | 订单:%s",
data["category"], data["order_id"])
}
}
}
func (a *AfterSaleService) Stop() {
close(a.done)
a.pullSock.Close()
}
调用:
Go
/*
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Push & Pull Pattern(推 - 拉)模式
# Author : geovindu,Geovin Du 涂聚文.
# IDE : goLang 2024.3.6 go 26.2
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/6/25 21:26
# User : geovindu
# Product : GoLand
# Project : godesginpattern
# File : pushpullbll.go
*/
package bll
import (
"godesginpattern/pushpull/service"
"godesginpattern/pushpull/utils"
"os"
"os/signal"
"syscall"
"time"
)
var ppLog = utils.GetLogger("pushpullbll")
func PushPullMain() {
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
raw := service.NewRawMaterialService()
proc := service.NewProcessService()
quality := service.NewQualityService()
sale := service.NewSaleService()
after := service.NewAfterSaleService()
go func() {
<-sig
ppLog.Println("退出中...")
proc.Stop()
quality.Stop()
sale.Stop()
after.Stop()
os.Exit(0)
}()
go proc.RunPipeline()
time.Sleep(200 * time.Millisecond)
go quality.RunPipeline()
time.Sleep(200 * time.Millisecond)
go sale.RunPipeline()
time.Sleep(200 * time.Millisecond)
go after.RunConsumer()
time.Sleep(200 * time.Millisecond)
ppLog.Println("✅ 珠宝 Push-Pull 流水线启动完成")
raw.RunProduce(5)
time.Sleep(5 * time.Second)
proc.Stop()
quality.Stop()
sale.Stop()
after.Stop()
ppLog.Println("✅ 珠宝 Push-Pull 流水线运行结束")
}
输出:
