-
Notifications
You must be signed in to change notification settings - Fork 6
/
shop.go
122 lines (100 loc) · 3.1 KB
/
shop.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package shop
import (
"context"
"fmt"
"github.com/mroth/weightedrand"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.uber.org/zap"
"net/http"
"time"
)
type Shop struct {
cfg Config
logger *zap.Logger
chooser *weightedrand.Chooser
// Services
customerSvc *CustomerService
}
func New(cfg Config, logger *zap.Logger) (*Shop, error) {
customerSvc, err := NewCustomerService(cfg, logger)
if err != nil {
return nil, fmt.Errorf("failed to create customer service: %w", err)
}
addressSvc, err := NewAddressService(cfg, logger)
if err != nil {
return nil, fmt.Errorf("failed to create address service: %w", err)
}
frontendSvc, err := NewFrontendService(cfg, logger)
if err != nil {
return nil, fmt.Errorf("failed to create frontend service: %w", err)
}
orderSvc, err := NewOrderService(cfg, logger)
if err != nil {
return nil, fmt.Errorf("failed to create order service: %w", err)
}
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
err = customerSvc.Initialize(ctx)
if err != nil {
return nil, fmt.Errorf("failed to initialize customer service: %w", err)
}
err = addressSvc.Initialize(ctx)
if err != nil {
return nil, fmt.Errorf("failed to initialize customer service: %w", err)
}
err = frontendSvc.Initialize(ctx)
if err != nil {
return nil, fmt.Errorf("failed to initialize frontend service: %w", err)
}
err = orderSvc.Initialize(ctx)
if err != nil {
return nil, fmt.Errorf("failed to initialize order service: %w", err)
}
go addressSvc.Start()
go orderSvc.Start()
// Random chooser
wr, err := weightedrand.NewChooser(
weightedrand.Choice{Item: frontendSvc.CreateFrontendEvent, Weight: 1000},
weightedrand.Choice{Item: customerSvc.CreateCustomer, Weight: 50},
weightedrand.Choice{Item: addressSvc.CreateAddress, Weight: 30},
weightedrand.Choice{Item: customerSvc.DeleteCustomer, Weight: 8},
weightedrand.Choice{Item: customerSvc.ModifyCustomer, Weight: 6},
weightedrand.Choice{Item: orderSvc.CreateOrder, Weight: 5},
)
if err != nil {
return nil, fmt.Errorf("failed to create random chooser: %w", err)
}
return &Shop{
cfg: cfg,
logger: logger,
chooser: wr,
customerSvc: customerSvc,
}, nil
}
// Start starts all shop components and triggers events (e.g. customer registration) in accordance with the
// config for traffic simulation.
func (s *Shop) Start() error {
http.Handle("/metrics", promhttp.Handler())
go func() {
err := http.ListenAndServe(":8080", nil)
s.logger.Info("prometheus http handler quit", zap.Error(err))
}()
for {
for i := 0; i < s.cfg.Traffic.Interval.Rate; i++ {
pageImpressionsSimulated.Inc()
s.SimulatePageImpression()
}
time.Sleep(s.cfg.Traffic.Interval.Duration)
}
}
// SimulatePageImpression simulates a user visiting a page in our imaginary owl shop. This page impression can be a
// user registration, oder, viewing articles or doing anything else a common user would do in a shop.
func (s *Shop) SimulatePageImpression() {
go func() {
fn, isOk := s.chooser.Pick().(func())
if !isOk {
s.logger.Fatal("randomly picked method is not a func")
}
fn()
}()
}