Oxidaner commented on code in PR #3711: URL: https://github.com/apache/dubbo-go/pull/3711#discussion_r3895361665
########## metadata/service_definition.go: ########## @@ -0,0 +1,219 @@ +/* + * 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 ( + "encoding/json" + "sort" +) + +import ( + "github.com/dubbogo/gost/log/logger" +) + +import ( + "dubbo.apache.org/dubbo-go/v3/common" + "dubbo.apache.org/dubbo-go/v3/common/constant" + "dubbo.apache.org/dubbo-go/v3/metadata/definition" + "dubbo.apache.org/dubbo-go/v3/metadata/report" +) + +// PublishServiceDefinitions builds and publishes an interface-level service +// definition for each exported URL that carries a describable contract, and +// returns the URLs whose publish failed for a reason worth retrying. +// +// No error is returned and nothing here blocks. A provider whose definition did +// not reach the metadata center is still a working provider — it is only +// invisible to Admin's console — so a metadata-center outage must not keep +// instances out of traffic. Java reaches the same outcome by a different route: +// AbstractMetadataReport defaults sync-report to false and hands the write to an +// executor, so export never observes the result at all. +// +// Only write failures come back. A service that has no describable contract — +// unregistered, unbuildable, or with no publishable methods — is reported in the +// log and dropped, because retrying cannot change any of those. +// +// Publishing is idempotent and keyed only by service identity, so calling this +// on every start, on each cycle-report pass, and on every retry simply +// overwrites the previous document. +func PublishServiceDefinitions(urls []*common.URL) []*common.URL { + publishers := serviceDefinitionPublishers() + if len(publishers) == 0 { + return nil + } + + var failed []*common.URL + for _, u := range dedupeByService(urls) { + if !publishServiceDefinition(u, publishers) { + failed = append(failed, u) + } + } + return failed +} + +// publishServiceDefinition reports whether the caller should retry. +// +// A partial failure across several reports counts as a failure: the retry +// republishes to all of them, which is harmless because each write overwrites. +func publishServiceDefinition(u *common.URL, publishers []report.ServiceDefinitionPublisher) bool { + svc := common.ServiceMap.GetServiceByServiceKey(u.Protocol, u.ServiceKey()) + if svc == nil { + logger.Warnf("[Metadata][Definition] no registered service for %s/%s, skipping definition", + u.Protocol, u.ServiceKey()) + return true + } + + def, skips, err := definition.BuildFromURL(u, svc.ServiceType()) + if err != nil { + logger.Errorf("[Metadata][Definition] could not build definition for %s: %v", u.ServiceKey(), err) + return true + } + for _, skip := range skips { + logger.Warnf("[Metadata][Definition] method %s.%s is not published: %s", + def.CanonicalName, skip.Name, skip.Reason) + } + if len(def.Methods) == 0 { + logger.Warnf("[Metadata][Definition] %s has no publishable methods, skipping definition", + def.CanonicalName) + return true + } + + payload, err := json.Marshal(def) + if err != nil { + logger.Errorf("[Metadata][Definition] could not serialize definition for %s: %v", + def.CanonicalName, err) + return true + } + + application := u.GetParam(constant.ApplicationKey, "") + published := true + for _, publisher := range publishers { + if err := publisher.PublishServiceDefinition( + def.CanonicalName, u.Version(), u.Group(), application, string(payload), + ); err != nil { + logger.Errorf("[Metadata][Definition] could not publish definition for %s: %v", + def.CanonicalName, err) + published = false + continue + } + logger.Infof("[Metadata][Definition] published definition for %s, methods=%d types=%d", + def.CanonicalName, len(def.Methods), len(def.Types)) + } + return published +} + +// ServiceDefinitionsEnabled reports whether any configured metadata report will +// accept interface-level service definitions. +// +// Callers use this to decide whether work that only exists to serve definitions +// — the daily re-publish, for one — is worth scheduling at all. +func ServiceDefinitionsEnabled() bool { + return len(serviceDefinitionPublishers()) > 0 +} + +// serviceDefinitionPublishers returns the configured reports that both support +// the capability and have it switched on. +// +// The instance table stores *DelegateMetadataReport, so the capability has to be +// queried through the wrapper rather than type-asserted off the interface value. +func serviceDefinitionPublishers() []report.ServiceDefinitionPublisher { + var publishers []report.ServiceDefinitionPublisher + for _, r := range GetMetadataReports() { Review Comment: done -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
