golang-im聊天
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.
 
 
 
 

206 linhas
4.9 KiB

  1. package k8s
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "gim/pkg/logger"
  7. "go.uber.org/zap"
  8. "k8s.io/client-go/kubernetes"
  9. "k8s.io/client-go/rest"
  10. "sort"
  11. "strings"
  12. "time"
  13. "google.golang.org/grpc/resolver"
  14. v1 "k8s.io/api/core/v1"
  15. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  16. corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
  17. )
  18. var k8sClientSet *kubernetes.Clientset
  19. func GetK8sClient() (*kubernetes.Clientset, error) {
  20. if k8sClientSet == nil {
  21. config, err := rest.InClusterConfig()
  22. if err != nil {
  23. return nil, err
  24. }
  25. k8sClientSet, err = kubernetes.NewForConfig(config)
  26. if err != nil {
  27. return nil, err
  28. }
  29. }
  30. return k8sClientSet, nil
  31. }
  32. // 实现k8s地址解析,根据k8s的service的endpoints解析 比如,k8s:///namespace.server:port
  33. func init() {
  34. resolver.Register(&k8sBuilder{})
  35. }
  36. func GetK8STarget(namespace, server, port string) string {
  37. return fmt.Sprintf("k8s:///%s.%s:%s", namespace, server, port)
  38. }
  39. type k8sBuilder struct{}
  40. func (b *k8sBuilder) Build(target resolver.Target, clientConn resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
  41. return newK8sResolver(target, clientConn)
  42. }
  43. func (b *k8sBuilder) Scheme() string {
  44. return "k8s"
  45. }
  46. // k8sResolver k8s地址解析器
  47. type k8sResolver struct {
  48. log *zap.Logger
  49. clientConn resolver.ClientConn
  50. endpointsClient corev1.EndpointsInterface
  51. service string
  52. cancel context.CancelFunc
  53. ips []string
  54. port string
  55. }
  56. func newK8sResolver(target resolver.Target, clientConn resolver.ClientConn) (*k8sResolver, error) {
  57. log := logger.Logger.With(zap.String("target", target.Endpoint))
  58. log.Info("k8s resolver build")
  59. namespace, service, port, err := parseTarget(target)
  60. if err != nil {
  61. log.Error("k8s resolver error", zap.Error(err))
  62. return nil, err
  63. }
  64. k8sClient, err := GetK8sClient()
  65. if err != nil {
  66. log.Error("k8s resolver error", zap.Error(err))
  67. return nil, err
  68. }
  69. ctx, cancel := context.WithCancel(context.Background())
  70. client := k8sClient.CoreV1().Endpoints(namespace)
  71. k8sResolver := &k8sResolver{
  72. log: log,
  73. clientConn: clientConn,
  74. endpointsClient: client,
  75. service: service,
  76. cancel: cancel,
  77. port: port,
  78. }
  79. err = k8sResolver.updateState(true)
  80. if err != nil {
  81. log.Error("k8s resolver error", zap.Error(err))
  82. return nil, err
  83. }
  84. ticker := time.NewTicker(time.Second)
  85. // 监听变化
  86. go func() {
  87. for {
  88. select {
  89. case <-ctx.Done():
  90. return
  91. case <-ticker.C:
  92. _ = k8sResolver.updateState(false)
  93. }
  94. }
  95. }()
  96. return k8sResolver, nil
  97. }
  98. // ResolveNow grpc感知到连接异常,会做通知,观察日志得知
  99. func (r *k8sResolver) ResolveNow(opt resolver.ResolveNowOptions) {
  100. r.log.Info("k8s resolver resolveNow")
  101. }
  102. func (r *k8sResolver) Close() {
  103. r.log.Info("k8s resolver close")
  104. r.cancel()
  105. }
  106. // updateState 更新地址列表
  107. func (r *k8sResolver) updateState(isFromNew bool) error {
  108. endpoints, err := r.endpointsClient.Get(context.TODO(), r.service, metav1.GetOptions{})
  109. if err != nil {
  110. r.log.Error("k8s resolver error", zap.Error(err))
  111. return err
  112. }
  113. newIPs := getIPs(endpoints)
  114. if len(newIPs) == 0 {
  115. return nil
  116. }
  117. if !isFromNew && isEqualIPs(r.ips, newIPs) {
  118. return nil
  119. }
  120. r.ips = newIPs
  121. addresses := make([]resolver.Address, 0, len(r.ips))
  122. for _, ip := range r.ips {
  123. addresses = append(addresses, resolver.Address{
  124. Addr: ip + ":" + r.port,
  125. })
  126. }
  127. state := resolver.State{
  128. Addresses: addresses,
  129. }
  130. r.log.Info("k8s resolver updateState", zap.Bool("is_from_new", isFromNew), zap.Any("service", r.service), zap.Any("addresses", addresses))
  131. // 这里地址数量不能为0,为0会返回错误
  132. err = r.clientConn.UpdateState(state)
  133. if err != nil {
  134. r.log.Error("k8s resolver error", zap.Error(err))
  135. return err
  136. }
  137. return nil
  138. }
  139. // parseTarget 对grpc的Endpoint进行解析,格式必须是:k8s:///namespace.server:port
  140. func parseTarget(target resolver.Target) (namespace string, service string, port string, err error) {
  141. namespaceAndServerPort := strings.Split(target.Endpoint, ".")
  142. if len(namespaceAndServerPort) != 2 {
  143. err = errors.New("endpoint must is namespace.server:port")
  144. return
  145. }
  146. namespace = namespaceAndServerPort[0]
  147. serverAndPort := strings.Split(namespaceAndServerPort[1], ":")
  148. if len(serverAndPort) != 2 {
  149. err = errors.New("endpoint must is namespace.server:port")
  150. return
  151. }
  152. service = serverAndPort[0]
  153. port = serverAndPort[1]
  154. return
  155. }
  156. // isEqualIPs 判断两个地址列表是否相等
  157. func isEqualIPs(s1, s2 []string) bool {
  158. if len(s1) != len(s2) {
  159. return false
  160. }
  161. sort.Strings(s1)
  162. sort.Strings(s2)
  163. for i := range s1 {
  164. if s1[i] != s2[i] {
  165. return false
  166. }
  167. }
  168. return true
  169. }
  170. // getIPs 获取EndpointSlice里面的IP列表
  171. func getIPs(endpoints *v1.Endpoints) []string {
  172. ips := make([]string, 0, 10)
  173. if len(endpoints.Subsets) <= 0 {
  174. return ips
  175. }
  176. for _, address := range endpoints.Subsets[0].Addresses {
  177. ips = append(ips, address.IP)
  178. }
  179. return ips
  180. }