dubbo.apache.org/dubbo-go/v3@v3.1.1/protocol/dubbo/dubbo_protocol.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package dubbo
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"sync"
    24  	"time"
    25  )
    26  
    27  import (
    28  	"github.com/dubbogo/gost/log/logger"
    29  
    30  	"github.com/opentracing/opentracing-go"
    31  )
    32  
    33  import (
    34  	"dubbo.apache.org/dubbo-go/v3/common"
    35  	"dubbo.apache.org/dubbo-go/v3/common/constant"
    36  	"dubbo.apache.org/dubbo-go/v3/common/extension"
    37  	"dubbo.apache.org/dubbo-go/v3/protocol"
    38  	"dubbo.apache.org/dubbo-go/v3/protocol/invocation"
    39  	"dubbo.apache.org/dubbo-go/v3/remoting"
    40  	"dubbo.apache.org/dubbo-go/v3/remoting/getty"
    41  )
    42  
    43  const (
    44  	// DUBBO is dubbo protocol name
    45  	DUBBO = "dubbo"
    46  )
    47  
    48  var (
    49  	// Make the connection can be shared.
    50  	// It will create one connection for one address (ip+port)
    51  	exchangeClientMap = new(sync.Map)
    52  	exchangeLock      = new(sync.Map)
    53  )
    54  
    55  func init() {
    56  	extension.SetProtocol(DUBBO, GetProtocol)
    57  }
    58  
    59  var dubboProtocol *DubboProtocol
    60  
    61  // DubboProtocol supports dubbo protocol. It implements Protocol interface for dubbo protocol.
    62  type DubboProtocol struct {
    63  	protocol.BaseProtocol
    64  	// It is store relationship about serviceKey(group/interface:version) and ExchangeServer
    65  	// The ExchangeServer is introduced to replace of Server. Because Server is depend on getty directly.
    66  	serverMap  map[string]*remoting.ExchangeServer
    67  	serverLock sync.Mutex
    68  }
    69  
    70  // NewDubboProtocol create a dubbo protocol.
    71  func NewDubboProtocol() *DubboProtocol {
    72  	return &DubboProtocol{
    73  		BaseProtocol: protocol.NewBaseProtocol(),
    74  		serverMap:    make(map[string]*remoting.ExchangeServer),
    75  	}
    76  }
    77  
    78  // Export export dubbo service.
    79  func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
    80  	url := invoker.GetURL()
    81  	serviceKey := url.ServiceKey()
    82  	exporter := NewDubboExporter(serviceKey, invoker, dp.ExporterMap())
    83  	dp.SetExporterMap(serviceKey, exporter)
    84  	logger.Infof("[DUBBO Protocol] Export service: %s", url.String())
    85  	// start server
    86  	dp.openServer(url)
    87  	return exporter
    88  }
    89  
    90  // Refer create dubbo service reference.
    91  func (dp *DubboProtocol) Refer(url *common.URL) protocol.Invoker {
    92  	exchangeClient := getExchangeClient(url)
    93  	if exchangeClient == nil {
    94  		logger.Warnf("can't dial the server: %+v", url.Location)
    95  		return nil
    96  	}
    97  	invoker := NewDubboInvoker(url, exchangeClient)
    98  	dp.SetInvokers(invoker)
    99  	logger.Infof("[DUBBO Protocol] Refer service: %s", url.String())
   100  	return invoker
   101  }
   102  
   103  // Destroy destroy dubbo service.
   104  func (dp *DubboProtocol) Destroy() {
   105  	logger.Infof("DubboProtocol destroy.")
   106  
   107  	dp.BaseProtocol.Destroy()
   108  
   109  	// stop server
   110  	for key, server := range dp.serverMap {
   111  		delete(dp.serverMap, key)
   112  		server.Stop()
   113  	}
   114  }
   115  
   116  func (dp *DubboProtocol) openServer(url *common.URL) {
   117  	_, ok := dp.serverMap[url.Location]
   118  	if !ok {
   119  		_, ok := dp.ExporterMap().Load(url.ServiceKey())
   120  		if !ok {
   121  			panic("[DubboProtocol]" + url.Key() + "is not existing")
   122  		}
   123  
   124  		dp.serverLock.Lock()
   125  		_, ok = dp.serverMap[url.Location]
   126  		if !ok {
   127  			handler := func(invocation *invocation.RPCInvocation) protocol.RPCResult {
   128  				return doHandleRequest(invocation)
   129  			}
   130  			srv := remoting.NewExchangeServer(url, getty.NewServer(url, handler))
   131  			dp.serverMap[url.Location] = srv
   132  			srv.Start()
   133  		}
   134  		dp.serverLock.Unlock()
   135  	}
   136  }
   137  
   138  // GetProtocol get a single dubbo protocol.
   139  func GetProtocol() protocol.Protocol {
   140  	if dubboProtocol == nil {
   141  		dubboProtocol = NewDubboProtocol()
   142  	}
   143  	return dubboProtocol
   144  }
   145  
   146  func doHandleRequest(rpcInvocation *invocation.RPCInvocation) protocol.RPCResult {
   147  	exporter, _ := dubboProtocol.ExporterMap().Load(rpcInvocation.ServiceKey())
   148  	result := protocol.RPCResult{}
   149  	if exporter == nil {
   150  		err := fmt.Errorf("don't have this exporter, key: %s", rpcInvocation.ServiceKey())
   151  		logger.Errorf(err.Error())
   152  		result.Err = err
   153  		// reply(session, p, hessian.PackageResponse)
   154  		return result
   155  	}
   156  	invoker := exporter.(protocol.Exporter).GetInvoker()
   157  	if invoker != nil {
   158  		// FIXME
   159  		ctx := rebuildCtx(rpcInvocation)
   160  
   161  		invokeResult := invoker.Invoke(ctx, rpcInvocation)
   162  		if err := invokeResult.Error(); err != nil {
   163  			result.Err = invokeResult.Error()
   164  			// p.Header.ResponseStatus = hessian.Response_OK
   165  			// p.Body = hessian.NewResponse(nil, err, result.Attachments())
   166  		} else {
   167  			result.Rest = invokeResult.Result()
   168  			// p.Header.ResponseStatus = hessian.Response_OK
   169  			// p.Body = hessian.NewResponse(res, nil, result.Attachments())
   170  		}
   171  		result.Attrs = invokeResult.Attachments()
   172  	} else {
   173  		result.Err = fmt.Errorf("don't have the invoker, key: %s", rpcInvocation.ServiceKey())
   174  	}
   175  	return result
   176  }
   177  
   178  func getExchangeClient(url *common.URL) *remoting.ExchangeClient {
   179  	clientTmp, ok := exchangeClientMap.Load(url.Location)
   180  	if !ok {
   181  		var exchangeClientTmp *remoting.ExchangeClient
   182  		func() {
   183  			// lock for NewExchangeClient and store into map.
   184  			_, loaded := exchangeLock.LoadOrStore(url.Location, 0x00)
   185  			// unlock
   186  			defer exchangeLock.Delete(url.Location)
   187  			if loaded {
   188  				// retry for 5 times.
   189  				for i := 0; i < 5; i++ {
   190  					if clientTmp, ok = exchangeClientMap.Load(url.Location); ok {
   191  						break
   192  					} else {
   193  						// if cannot get, sleep a while.
   194  						time.Sleep(time.Duration(i*100) * time.Millisecond)
   195  					}
   196  				}
   197  				return
   198  			}
   199  
   200  			// todo set by config
   201  			exchangeClientTmp = remoting.NewExchangeClient(url, getty.NewClient(getty.Options{
   202  				ConnectTimeout: 3 * time.Second,
   203  				RequestTimeout: 3 * time.Second,
   204  			}), 3*time.Second, false)
   205  			// input store
   206  			if exchangeClientTmp != nil {
   207  				exchangeClientMap.Store(url.Location, exchangeClientTmp)
   208  			}
   209  		}()
   210  		if exchangeClientTmp != nil {
   211  			return exchangeClientTmp
   212  		}
   213  	}
   214  	// cannot dial the server
   215  	if clientTmp == nil {
   216  		return nil
   217  	}
   218  	exchangeClient := clientTmp.(*remoting.ExchangeClient)
   219  	exchangeClient.IncreaseActiveNumber()
   220  	return exchangeClient
   221  }
   222  
   223  // rebuildCtx rebuild the context by attachment.
   224  // Once we decided to transfer more context's key-value, we should change this.
   225  // now we only support rebuild the tracing context
   226  func rebuildCtx(inv *invocation.RPCInvocation) context.Context {
   227  	ctx := context.WithValue(context.Background(), constant.DubboCtxKey("attachment"), inv.Attachments())
   228  
   229  	// actually, if user do not use any opentracing framework, the err will not be nil.
   230  	spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap,
   231  		opentracing.TextMapCarrier(filterContext(inv.Attachments())))
   232  	if err == nil {
   233  		ctx = context.WithValue(ctx, constant.DubboCtxKey(constant.TracingRemoteSpanCtx), spanCtx)
   234  	}
   235  	return ctx
   236  }