You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

339 lines
10 KiB

  1. /*
  2. * Copyright 2019 gRPC authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package edsbalancer
  17. import (
  18. "context"
  19. "sync"
  20. "google.golang.org/grpc/balancer"
  21. "google.golang.org/grpc/balancer/base"
  22. "google.golang.org/grpc/connectivity"
  23. "google.golang.org/grpc/grpclog"
  24. "google.golang.org/grpc/resolver"
  25. )
  26. type pickerState struct {
  27. weight uint32
  28. picker balancer.Picker
  29. state connectivity.State
  30. }
  31. // balancerGroup takes a list of balancers, and make then into one balancer.
  32. //
  33. // Note that this struct doesn't implement balancer.Balancer, because it's not
  34. // intended to be used directly as a balancer. It's expected to be used as a
  35. // sub-balancer manager by a high level balancer.
  36. //
  37. // Updates from ClientConn are forwarded to sub-balancers
  38. // - service config update
  39. // - Not implemented
  40. // - address update
  41. // - subConn state change
  42. // - find the corresponding balancer and forward
  43. //
  44. // Actions from sub-balances are forwarded to parent ClientConn
  45. // - new/remove SubConn
  46. // - picker update and health states change
  47. // - sub-pickers are grouped into a group-picker
  48. // - aggregated connectivity state is the overall state of all pickers.
  49. // - resolveNow
  50. type balancerGroup struct {
  51. cc balancer.ClientConn
  52. mu sync.Mutex
  53. idToBalancer map[string]balancer.Balancer
  54. scToID map[balancer.SubConn]string
  55. pickerMu sync.Mutex
  56. // All balancer IDs exist as keys in this map. If an ID is not in map, it's
  57. // either removed or never added.
  58. idToPickerState map[string]*pickerState
  59. }
  60. func newBalancerGroup(cc balancer.ClientConn) *balancerGroup {
  61. return &balancerGroup{
  62. cc: cc,
  63. scToID: make(map[balancer.SubConn]string),
  64. idToBalancer: make(map[string]balancer.Balancer),
  65. idToPickerState: make(map[string]*pickerState),
  66. }
  67. }
  68. // add adds a balancer built by builder to the group, with given id and weight.
  69. func (bg *balancerGroup) add(id string, weight uint32, builder balancer.Builder) {
  70. bg.mu.Lock()
  71. if _, ok := bg.idToBalancer[id]; ok {
  72. bg.mu.Unlock()
  73. grpclog.Warningf("balancer group: adding a balancer with existing ID: %s", id)
  74. return
  75. }
  76. bg.mu.Unlock()
  77. bgcc := &balancerGroupCC{
  78. id: id,
  79. group: bg,
  80. }
  81. b := builder.Build(bgcc, balancer.BuildOptions{})
  82. bg.mu.Lock()
  83. bg.idToBalancer[id] = b
  84. bg.mu.Unlock()
  85. bg.pickerMu.Lock()
  86. bg.idToPickerState[id] = &pickerState{
  87. weight: weight,
  88. // Start everything in IDLE. It's doesn't affect the overall state
  89. // because we don't count IDLE when aggregating (as opposite to e.g.
  90. // READY, 1 READY results in overall READY).
  91. state: connectivity.Idle,
  92. }
  93. bg.pickerMu.Unlock()
  94. }
  95. // remove removes the balancer with id from the group, and closes the balancer.
  96. //
  97. // It also removes the picker generated from this balancer from the picker
  98. // group. It always results in a picker update.
  99. func (bg *balancerGroup) remove(id string) {
  100. bg.mu.Lock()
  101. // Close balancer.
  102. if b, ok := bg.idToBalancer[id]; ok {
  103. b.Close()
  104. delete(bg.idToBalancer, id)
  105. }
  106. // Remove SubConns.
  107. for sc, bid := range bg.scToID {
  108. if bid == id {
  109. bg.cc.RemoveSubConn(sc)
  110. delete(bg.scToID, sc)
  111. }
  112. }
  113. bg.mu.Unlock()
  114. bg.pickerMu.Lock()
  115. // Remove id and picker from picker map. This also results in future updates
  116. // for this ID to be ignored.
  117. delete(bg.idToPickerState, id)
  118. // Update state and picker to reflect the changes.
  119. bg.cc.UpdateBalancerState(buildPickerAndState(bg.idToPickerState))
  120. bg.pickerMu.Unlock()
  121. }
  122. // changeWeight changes the weight of the balancer.
  123. //
  124. // NOTE: It always results in a picker update now. This probably isn't
  125. // necessary. But it seems better to do the update because it's a change in the
  126. // picker (which is balancer's snapshot).
  127. func (bg *balancerGroup) changeWeight(id string, newWeight uint32) {
  128. bg.pickerMu.Lock()
  129. defer bg.pickerMu.Unlock()
  130. pState, ok := bg.idToPickerState[id]
  131. if !ok {
  132. return
  133. }
  134. if pState.weight == newWeight {
  135. return
  136. }
  137. pState.weight = newWeight
  138. // Update state and picker to reflect the changes.
  139. bg.cc.UpdateBalancerState(buildPickerAndState(bg.idToPickerState))
  140. }
  141. // Following are actions from the parent grpc.ClientConn, forward to sub-balancers.
  142. // SubConn state change: find the corresponding balancer and then forward.
  143. func (bg *balancerGroup) handleSubConnStateChange(sc balancer.SubConn, state connectivity.State) {
  144. grpclog.Infof("balancer group: handle subconn state change: %p, %v", sc, state)
  145. bg.mu.Lock()
  146. var b balancer.Balancer
  147. if id, ok := bg.scToID[sc]; ok {
  148. if state == connectivity.Shutdown {
  149. // Only delete sc from the map when state changed to Shutdown.
  150. delete(bg.scToID, sc)
  151. }
  152. b = bg.idToBalancer[id]
  153. }
  154. bg.mu.Unlock()
  155. if b == nil {
  156. grpclog.Infof("balancer group: balancer not found for sc state change")
  157. return
  158. }
  159. b.HandleSubConnStateChange(sc, state)
  160. }
  161. // Address change: forward to balancer.
  162. func (bg *balancerGroup) handleResolvedAddrs(id string, addrs []resolver.Address) {
  163. bg.mu.Lock()
  164. b, ok := bg.idToBalancer[id]
  165. bg.mu.Unlock()
  166. if !ok {
  167. grpclog.Infof("balancer group: balancer with id %q not found", id)
  168. return
  169. }
  170. b.HandleResolvedAddrs(addrs, nil)
  171. }
  172. // TODO: handleServiceConfig()
  173. //
  174. // For BNS address for slicer, comes from endpoint.Metadata. It will be sent
  175. // from parent to sub-balancers as service config.
  176. // Following are actions from sub-balancers, forward to ClientConn.
  177. // newSubConn: forward to ClientConn, and also create a map from sc to balancer,
  178. // so state update will find the right balancer.
  179. //
  180. // One note about removing SubConn: only forward to ClientConn, but not delete
  181. // from map. Delete sc from the map only when state changes to Shutdown. Since
  182. // it's just forwarding the action, there's no need for a removeSubConn()
  183. // wrapper function.
  184. func (bg *balancerGroup) newSubConn(id string, addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
  185. sc, err := bg.cc.NewSubConn(addrs, opts)
  186. if err != nil {
  187. return nil, err
  188. }
  189. bg.mu.Lock()
  190. bg.scToID[sc] = id
  191. bg.mu.Unlock()
  192. return sc, nil
  193. }
  194. // updateBalancerState: create an aggregated picker and an aggregated
  195. // connectivity state, then forward to ClientConn.
  196. func (bg *balancerGroup) updateBalancerState(id string, state connectivity.State, picker balancer.Picker) {
  197. grpclog.Infof("balancer group: update balancer state: %v, %v, %p", id, state, picker)
  198. bg.pickerMu.Lock()
  199. defer bg.pickerMu.Unlock()
  200. pickerSt, ok := bg.idToPickerState[id]
  201. if !ok {
  202. // All state starts in IDLE. If ID is not in map, it's either removed,
  203. // or never existed.
  204. grpclog.Infof("balancer group: pickerState not found when update picker/state")
  205. return
  206. }
  207. pickerSt.picker = picker
  208. pickerSt.state = state
  209. bg.cc.UpdateBalancerState(buildPickerAndState(bg.idToPickerState))
  210. }
  211. func (bg *balancerGroup) close() {
  212. bg.mu.Lock()
  213. for _, b := range bg.idToBalancer {
  214. b.Close()
  215. }
  216. // Also remove all SubConns.
  217. for sc := range bg.scToID {
  218. bg.cc.RemoveSubConn(sc)
  219. }
  220. bg.mu.Unlock()
  221. }
  222. func buildPickerAndState(m map[string]*pickerState) (connectivity.State, balancer.Picker) {
  223. var readyN, connectingN int
  224. readyPickerWithWeights := make([]pickerState, 0, len(m))
  225. for _, ps := range m {
  226. switch ps.state {
  227. case connectivity.Ready:
  228. readyN++
  229. readyPickerWithWeights = append(readyPickerWithWeights, *ps)
  230. case connectivity.Connecting:
  231. connectingN++
  232. }
  233. }
  234. var aggregatedState connectivity.State
  235. switch {
  236. case readyN > 0:
  237. aggregatedState = connectivity.Ready
  238. case connectingN > 0:
  239. aggregatedState = connectivity.Connecting
  240. default:
  241. aggregatedState = connectivity.TransientFailure
  242. }
  243. if aggregatedState == connectivity.TransientFailure {
  244. return aggregatedState, base.NewErrPicker(balancer.ErrTransientFailure)
  245. }
  246. return aggregatedState, newPickerGroup(readyPickerWithWeights)
  247. }
  248. type pickerGroup struct {
  249. readyPickerWithWeights []pickerState
  250. length int
  251. mu sync.Mutex
  252. idx int // The index of the picker that will be picked
  253. count uint32 // The number of times the current picker has been picked.
  254. }
  255. // newPickerGroup takes pickers with weights, and group them into one picker.
  256. //
  257. // Note it only takes ready pickers. The map shouldn't contain non-ready
  258. // pickers.
  259. //
  260. // TODO: (bg) confirm this is the expected behavior: non-ready balancers should
  261. // be ignored when picking. Only ready balancers are picked.
  262. func newPickerGroup(readyPickerWithWeights []pickerState) *pickerGroup {
  263. return &pickerGroup{
  264. readyPickerWithWeights: readyPickerWithWeights,
  265. length: len(readyPickerWithWeights),
  266. }
  267. }
  268. func (pg *pickerGroup) Pick(ctx context.Context, opts balancer.PickOptions) (conn balancer.SubConn, done func(balancer.DoneInfo), err error) {
  269. if pg.length <= 0 {
  270. return nil, nil, balancer.ErrNoSubConnAvailable
  271. }
  272. // TODO: the WRR algorithm needs a design.
  273. // MAYBE: move WRR implmentation to util.go as a separate struct.
  274. pg.mu.Lock()
  275. pickerSt := pg.readyPickerWithWeights[pg.idx]
  276. p := pickerSt.picker
  277. pg.count++
  278. if pg.count >= pickerSt.weight {
  279. pg.idx = (pg.idx + 1) % pg.length
  280. pg.count = 0
  281. }
  282. pg.mu.Unlock()
  283. return p.Pick(ctx, opts)
  284. }
  285. // balancerGroupCC implements the balancer.ClientConn API and get passed to each
  286. // sub-balancer. It contains the sub-balancer ID, so the parent balancer can
  287. // keep track of SubConn/pickers and the sub-balancers they belong to.
  288. //
  289. // Some of the actions are forwarded to the parent ClientConn with no change.
  290. // Some are forward to balancer group with the sub-balancer ID.
  291. type balancerGroupCC struct {
  292. id string
  293. group *balancerGroup
  294. }
  295. func (bgcc *balancerGroupCC) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
  296. return bgcc.group.newSubConn(bgcc.id, addrs, opts)
  297. }
  298. func (bgcc *balancerGroupCC) RemoveSubConn(sc balancer.SubConn) {
  299. bgcc.group.cc.RemoveSubConn(sc)
  300. }
  301. func (bgcc *balancerGroupCC) UpdateBalancerState(state connectivity.State, picker balancer.Picker) {
  302. bgcc.group.updateBalancerState(bgcc.id, state, picker)
  303. }
  304. func (bgcc *balancerGroupCC) ResolveNow(opt resolver.ResolveNowOption) {
  305. bgcc.group.cc.ResolveNow(opt)
  306. }
  307. func (bgcc *balancerGroupCC) Target() string {
  308. return bgcc.group.cc.Target()
  309. }