metadata/options.go (217 lines of code) (raw):
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package metadata
import (
"strconv"
"strings"
"sync"
"time"
)
import (
"github.com/dubbogo/gost/log/logger"
perrors "github.com/pkg/errors"
)
import (
"dubbo.apache.org/dubbo-go/v3/common"
"dubbo.apache.org/dubbo-go/v3/common/constant"
"dubbo.apache.org/dubbo-go/v3/global"
)
var (
metadataOptions *Options
exportOnce sync.Once
)
type Options struct {
appName string
metadataType string
port int
protocol string
}
func defaultOptions() *Options {
return &Options{metadataType: constant.DefaultMetadataStorageType, protocol: constant.DefaultProtocol}
}
func NewOptions(opts ...Option) *Options {
metaOptions := defaultOptions()
for _, opt := range opts {
opt(metaOptions)
}
return metaOptions
}
func (opts *Options) Init() error {
metadataOptions = opts
var err error
exportOnce.Do(func() {
if opts.metadataType != constant.RemoteMetadataStorageType {
exporter := &serviceExporter{service: metadataService, opts: opts}
defer func() {
// TODO remove this recover func,this just to avoid some unit test failed,this will not happen in user side mostly
// config test -> metadata exporter -> dubbo protocol/remoting -> config,cycle import will occur
// some day we fix the cycle import then can remove this recover
if err := recover(); err != nil {
logger.Errorf("metadata export failed,please check if dubbo protocol is imported, error: %v", err)
}
}()
err = exporter.Export()
}
})
return err
}
type Option func(*Options)
func WithAppName(app string) Option {
return func(options *Options) {
options.appName = app
}
}
func WithMetadataType(typ string) Option {
return func(options *Options) {
options.metadataType = typ
}
}
func WithPort(port int) Option {
return func(options *Options) {
options.port = port
}
}
func WithMetadataProtocol(protocol string) Option {
return func(options *Options) {
options.protocol = protocol
}
}
type ReportOptions struct {
registryId string
*global.MetadataReportConfig
}
func InitRegistryMetadataReport(registries map[string]*global.RegistryConfig) error {
if len(registries) > 0 {
for id, reg := range registries {
ok, err := reg.UseAsMetadataReport()
if err != nil {
return err
}
if ok {
opts := fromRegistry(id, reg)
if err := opts.Init(); err != nil {
return err
}
}
}
}
return nil
}
func fromRegistry(id string, rc *global.RegistryConfig) *ReportOptions {
opts := NewReportOptions(
WithRegistryId(id),
WithProtocol(rc.Protocol),
WithAddress(rc.Address),
WithUsername(rc.Username),
WithPassword(rc.Password),
WithGroup(rc.Group),
WithNamespace(rc.Namespace),
WithParams(rc.Params),
)
if rc.Timeout != "" {
timeout, err := time.ParseDuration(rc.Timeout)
if err != nil {
logger.Errorf("parse registry timeout config error %v", rc.Timeout)
} else {
WithTimeout(timeout)(opts)
}
}
return opts
}
func (opts *ReportOptions) Init() error {
url, err := opts.toUrl()
if err != nil {
logger.Errorf("metadata report create error %v", err)
return err
}
return addMetadataReport(opts.registryId, url)
}
func (opts *ReportOptions) toUrl() (*common.URL, error) {
res, err := common.NewURL(opts.Address,
common.WithUsername(opts.Username),
common.WithPassword(opts.Password),
common.WithLocation(opts.Address),
common.WithProtocol(opts.Protocol),
common.WithParamsValue(constant.TimeoutKey, opts.Timeout),
common.WithParamsValue(constant.MetadataReportGroupKey, opts.Group),
common.WithParamsValue(constant.MetadataReportNamespaceKey, opts.Namespace),
common.WithParamsValue(constant.ClientNameKey, strings.Join([]string{constant.MetadataReportPrefix, opts.Protocol, opts.Address}, "-")),
)
if err != nil || len(res.Protocol) == 0 {
return nil, perrors.New("Invalid MetadataReport Config.")
}
res.SetParam("metadata", res.Protocol)
for key, val := range opts.Params {
res.SetParam(key, val)
}
return res, nil
}
func defaultReportOptions() *ReportOptions {
return &ReportOptions{MetadataReportConfig: global.DefaultMetadataReportConfig()}
}
func NewReportOptions(opts ...ReportOption) *ReportOptions {
reportOptions := defaultReportOptions()
for _, opt := range opts {
opt(reportOptions)
}
return reportOptions
}
type ReportOption func(*ReportOptions)
func WithZookeeper() ReportOption {
return func(opts *ReportOptions) {
opts.Protocol = constant.ZookeeperKey
}
}
func WithNacos() ReportOption {
return func(opts *ReportOptions) {
opts.Protocol = constant.NacosKey
}
}
func WithEtcdV3() ReportOption {
return func(opts *ReportOptions) {
opts.Protocol = constant.EtcdV3Key
}
}
func WithProtocol(meta string) ReportOption {
return func(opts *ReportOptions) {
opts.Protocol = meta
}
}
// WithAddress address metadata report will to use, if a URL schema is set,this will also set the protocol,
// such as WithAddress("zookeeper://127.0.0.1") will set address to "127.0.0.1" and protocol to "zookeeper"
func WithAddress(address string) ReportOption {
return func(opts *ReportOptions) {
if i := strings.Index(address, "://"); i > 0 {
opts.Protocol = address[0:i]
}
opts.Address = address
}
}
func WithUsername(username string) ReportOption {
return func(opts *ReportOptions) {
opts.Username = username
}
}
func WithPassword(password string) ReportOption {
return func(opts *ReportOptions) {
opts.Password = password
}
}
func WithTimeout(timeout time.Duration) ReportOption {
return func(opts *ReportOptions) {
opts.Timeout = strconv.Itoa(int(timeout.Milliseconds()))
}
}
func WithGroup(group string) ReportOption {
return func(opts *ReportOptions) {
opts.Group = group
}
}
func WithNamespace(namespace string) ReportOption {
return func(opts *ReportOptions) {
opts.Namespace = namespace
}
}
func WithParams(params map[string]string) ReportOption {
return func(opts *ReportOptions) {
opts.Params = params
}
}
func WithRegistryId(id string) ReportOption {
return func(opts *ReportOptions) {
opts.registryId = id
}
}