Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 

221 linhas
5.8 KiB

  1. // Copyright 2017 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Package loadtest implements load testing for pubsub,
  15. // following the interface defined in https://github.com/GoogleCloudPlatform/pubsub/tree/master/load-test-framework/ .
  16. //
  17. // This package is experimental.
  18. package loadtest
  19. import (
  20. "bytes"
  21. "context"
  22. "errors"
  23. "log"
  24. "runtime"
  25. "strconv"
  26. "sync"
  27. "sync/atomic"
  28. "time"
  29. "cloud.google.com/go/pubsub"
  30. pb "cloud.google.com/go/pubsub/loadtest/pb"
  31. "github.com/golang/protobuf/ptypes"
  32. "golang.org/x/time/rate"
  33. )
  34. type pubServerConfig struct {
  35. topic *pubsub.Topic
  36. msgData []byte
  37. batchSize int32
  38. }
  39. // PubServer is a dummy Pub/Sub server for load testing.
  40. type PubServer struct {
  41. ID string
  42. cfg atomic.Value
  43. seqNum int32
  44. }
  45. // Start starts the server.
  46. func (l *PubServer) Start(ctx context.Context, req *pb.StartRequest) (*pb.StartResponse, error) {
  47. log.Println("received start")
  48. c, err := pubsub.NewClient(ctx, req.Project)
  49. if err != nil {
  50. return nil, err
  51. }
  52. dur, err := ptypes.Duration(req.PublishBatchDuration)
  53. if err != nil {
  54. return nil, err
  55. }
  56. l.init(c, req.Topic, req.MessageSize, req.PublishBatchSize, dur)
  57. log.Println("started")
  58. return &pb.StartResponse{}, nil
  59. }
  60. func (l *PubServer) init(c *pubsub.Client, topicName string, msgSize, batchSize int32, batchDur time.Duration) {
  61. topic := c.Topic(topicName)
  62. topic.PublishSettings = pubsub.PublishSettings{
  63. DelayThreshold: batchDur,
  64. CountThreshold: 950,
  65. ByteThreshold: 9500000,
  66. }
  67. l.cfg.Store(pubServerConfig{
  68. topic: topic,
  69. msgData: bytes.Repeat([]byte{'A'}, int(msgSize)),
  70. batchSize: batchSize,
  71. })
  72. }
  73. // Execute executes a request.
  74. func (l *PubServer) Execute(ctx context.Context, _ *pb.ExecuteRequest) (*pb.ExecuteResponse, error) {
  75. latencies, err := l.publishBatch()
  76. if err != nil {
  77. log.Printf("error: %v", err)
  78. return nil, err
  79. }
  80. return &pb.ExecuteResponse{Latencies: latencies}, nil
  81. }
  82. func (l *PubServer) publishBatch() ([]int64, error) {
  83. var cfg pubServerConfig
  84. if c, ok := l.cfg.Load().(pubServerConfig); ok {
  85. cfg = c
  86. } else {
  87. return nil, errors.New("config not loaded")
  88. }
  89. start := time.Now()
  90. latencies := make([]int64, cfg.batchSize)
  91. startStr := strconv.FormatInt(start.UnixNano()/1e6, 10)
  92. seqNum := atomic.AddInt32(&l.seqNum, cfg.batchSize) - cfg.batchSize
  93. rs := make([]*pubsub.PublishResult, cfg.batchSize)
  94. for i := int32(0); i < cfg.batchSize; i++ {
  95. rs[i] = cfg.topic.Publish(context.TODO(), &pubsub.Message{
  96. Data: cfg.msgData,
  97. Attributes: map[string]string{
  98. "sendTime": startStr,
  99. "clientId": l.ID,
  100. "sequenceNumber": strconv.Itoa(int(seqNum + i)),
  101. },
  102. })
  103. }
  104. for i, r := range rs {
  105. _, err := r.Get(context.Background())
  106. if err != nil {
  107. return nil, err
  108. }
  109. // TODO(jba,pongad): fix latencies
  110. // Later values will be skewed by earlier ones, since we wait for the
  111. // results in order. (On the other hand, it may not matter much, since
  112. // messages are added to bundles in order and bundles get sent more or
  113. // less in order.) If we want more accurate values, we can either start
  114. // a goroutine for each result (similar to the original code using a
  115. // callback), or call reflect.Select with the Ready channels of the
  116. // results.
  117. latencies[i] = time.Since(start).Nanoseconds() / 1e6
  118. }
  119. return latencies, nil
  120. }
  121. // SubServer is a dummy Pub/Sub server for load testing.
  122. type SubServer struct {
  123. // TODO(deklerk) what is this actually for?
  124. lim *rate.Limiter
  125. mu sync.Mutex
  126. idents []*pb.MessageIdentifier
  127. latencies []int64
  128. }
  129. // Start starts the server.
  130. func (s *SubServer) Start(ctx context.Context, req *pb.StartRequest) (*pb.StartResponse, error) {
  131. log.Println("received start")
  132. s.lim = rate.NewLimiter(rate.Every(time.Second), 1)
  133. c, err := pubsub.NewClient(ctx, req.Project)
  134. if err != nil {
  135. return nil, err
  136. }
  137. // Load test API doesn't define any way to stop right now.
  138. go func() {
  139. sub := c.Subscription(req.GetPubsubOptions().Subscription)
  140. sub.ReceiveSettings.NumGoroutines = 10 * runtime.GOMAXPROCS(0)
  141. err := sub.Receive(context.Background(), s.callback)
  142. log.Fatal(err)
  143. }()
  144. log.Println("started")
  145. return &pb.StartResponse{}, nil
  146. }
  147. func (s *SubServer) callback(_ context.Context, m *pubsub.Message) {
  148. id, err := strconv.ParseInt(m.Attributes["clientId"], 10, 64)
  149. if err != nil {
  150. log.Println(err)
  151. m.Nack()
  152. return
  153. }
  154. seqNum, err := strconv.ParseInt(m.Attributes["sequenceNumber"], 10, 32)
  155. if err != nil {
  156. log.Println(err)
  157. m.Nack()
  158. return
  159. }
  160. sendTimeMillis, err := strconv.ParseInt(m.Attributes["sendTime"], 10, 64)
  161. if err != nil {
  162. log.Println(err)
  163. m.Nack()
  164. return
  165. }
  166. latency := time.Now().UnixNano()/1e6 - sendTimeMillis
  167. ident := &pb.MessageIdentifier{
  168. PublisherClientId: id,
  169. SequenceNumber: int32(seqNum),
  170. }
  171. s.mu.Lock()
  172. s.idents = append(s.idents, ident)
  173. s.latencies = append(s.latencies, latency)
  174. s.mu.Unlock()
  175. m.Ack()
  176. }
  177. // Execute executes the request.
  178. func (s *SubServer) Execute(ctx context.Context, _ *pb.ExecuteRequest) (*pb.ExecuteResponse, error) {
  179. // Throttle so the load tester doesn't spam us and consume all our CPU.
  180. if err := s.lim.Wait(ctx); err != nil {
  181. return nil, err
  182. }
  183. s.mu.Lock()
  184. idents := s.idents
  185. s.idents = nil
  186. latencies := s.latencies
  187. s.latencies = nil
  188. s.mu.Unlock()
  189. return &pb.ExecuteResponse{
  190. Latencies: latencies,
  191. ReceivedMessages: idents,
  192. }, nil
  193. }