| name | Scaffolding dubbo-go services |
| description | Generates dubbo-go v3 provider or consumer skeletons using the code-API style (dubbo.NewInstance / server.NewServer / client.NewClient). Use when user asks to create, scaffold, or bootstrap a new dubbo-go service, provider, or consumer. |
Scaffolding dubbo-go Services
dubbo-go v3 exposes two entry styles. Use the code API by default — it is the style the samples repo has converged on and the one dubbo-go continues to invest in. Only fall back to YAML-driven dubbo.Load() when the user explicitly asks for it or is migrating an existing YAML project.
Before generating code, confirm three things with the user:
- Protocol — Triple (default, HTTP/2, gRPC-compatible) / Dubbo (Java interop with Hessian2) / gRPC. Pick Triple unless they have a reason.
- Registry — Nacos / ZooKeeper / etcd / direct (no registry). Pick direct for local demos, Nacos for production Apache stacks.
- Service definition — do they already have a
.proto file?
Project Layout
Mirror the samples repo layout so the user can cross-reference:
myservice/
├── go.mod
├── proto/
│ ├── greet.proto
│ ├── greet.pb.go # generated by protoc-gen-go
│ └── greet.triple.go # generated by protoc-gen-go-triple
├── go-server/
│ └── cmd/main.go
└── go-client/
└── cmd/main.go
Step 1: Proto Definition
syntax = "proto3";
package greet;
option go_package = "github.com/yourorg/myservice/proto;greet";
message GreetRequest {
string name = 1;
}
message GreetResponse {
string greeting = 1;
}
service GreetService {
rpc Greet(GreetRequest) returns (GreetResponse);
}
Install codegen plugins once:
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install github.com/dubbogo/protoc-gen-go-triple@latest
Generate:
protoc --go_out=. --go_opt=paths=source_relative \
--go-triple_out=. --go-triple_opt=paths=source_relative \
proto/greet.proto
Step 2: Provider (go-server/cmd/main.go)
Direct mode (no registry — simplest form for local demos):
package main
import (
"context"
)
import (
_ "dubbo.apache.org/dubbo-go/v3/imports"
"dubbo.apache.org/dubbo-go/v3/protocol"
"dubbo.apache.org/dubbo-go/v3/server"
"github.com/dubbogo/gost/log/logger"
)
import (
greet "github.com/yourorg/myservice/proto"
)
type GreetTripleServer struct{}
func (s *GreetTripleServer) Greet(ctx context.Context, req *greet.GreetRequest) (*greet.GreetResponse, error) {
return &greet.GreetResponse{Greeting: "hello " + req.Name}, nil
}
func main() {
srv, err := server.NewServer(
server.WithServerProtocol(
protocol.WithPort(20000),
protocol.WithTriple(),
),
)
if err != nil {
logger.Fatalf("new server: %v", err)
}
if err := greet.RegisterGreetServiceHandler(srv, &GreetTripleServer{}); err != nil {
logger.Fatalf("register handler: %v", err)
}
if err := srv.Serve(); err != nil {
logger.Fatalf("serve: %v", err)
}
}
With a registry (Nacos example) — use dubbo.NewInstance so registry and protocol are applied globally:
import (
"dubbo.apache.org/dubbo-go/v3"
"dubbo.apache.org/dubbo-go/v3/registry"
)
func main() {
ins, err := dubbo.NewInstance(
dubbo.WithName("myservice-provider"),
dubbo.WithRegistry(
registry.WithNacos(),
registry.WithAddress("127.0.0.1:8848"),
),
dubbo.WithProtocol(
protocol.WithTriple(),
protocol.WithPort(20000),
),
)
if err != nil { panic(err) }
srv, err := ins.NewServer()
if err != nil { panic(err) }
if err := greet.RegisterGreetServiceHandler(srv, &GreetTripleServer{}); err != nil {
panic(err)
}
if err := srv.Serve(); err != nil { panic(err) }
}
Swap registries by changing two lines:
- Nacos:
registry.WithNacos() + registry.WithAddress("127.0.0.1:8848")
- ZooKeeper:
registry.WithZookeeper() + registry.WithAddress("127.0.0.1:2181")
- etcd:
registry.WithEtcdV3() + registry.WithAddress("127.0.0.1:2379")
Step 3: Consumer (go-client/cmd/main.go)
Direct mode:
package main
import (
"context"
"time"
)
import (
"dubbo.apache.org/dubbo-go/v3/client"
_ "dubbo.apache.org/dubbo-go/v3/imports"
"github.com/dubbogo/gost/log/logger"
)
import (
greet "github.com/yourorg/myservice/proto"
)
func main() {
cli, err := client.NewClient(
client.WithClientURL("127.0.0.1:20000"),
)
if err != nil { logger.Fatalf("new client: %v", err) }
svc, err := greet.NewGreetService(cli)
if err != nil { logger.Fatalf("new service: %v", err) }
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
resp, err := svc.Greet(ctx, &greet.GreetRequest{Name: "world"})
if err != nil { logger.Fatalf("call: %v", err) }
logger.Infof("resp: %s", resp.Greeting)
}
With registry — omit WithClientURL and attach the same registry to an Instance:
ins, _ := dubbo.NewInstance(
dubbo.WithName("myservice-consumer"),
dubbo.WithRegistry(registry.WithNacos(), registry.WithAddress("127.0.0.1:8848")),
)
cli, _ := ins.NewClient()
svc, _ := greet.NewGreetService(cli)
Conventions
These are non-obvious rules the samples repo follows; keep them when scaffolding:
- Three import blocks: stdlib, then third-party, then same-module — separated by blank lines. Run
imports-formatter (from dubbogo/tools) if the user wants enforcement.
- Blank-import
imports during development: _ "dubbo.apache.org/dubbo-go/v3/imports" pulls in every built-in filter, protocol, registry, serializer. For production builds, switch to selective imports to cut binary size.
- Fail fast on startup errors — the samples
panic or logger.Fatalf on NewInstance / NewServer / NewClient / Register*Handler failures. Don't wrap these in silent error returns.
- Application name matters — v3 defaults to application-level service discovery, so
dubbo.WithName("...") is what appears in the registry, not the interface FQN.
Quick decision table
| User says | Use |
|---|
| "just want to try it" / "local demo" | direct mode, no registry |
| "production", "microservices", "service discovery" | dubbo.NewInstance + Nacos or ZK |
| "talk to Java Dubbo" | Triple + Protobuf (see java-interop skill) |
| "existing YAML config" | dubbo.Load() (legacy) — see config_yaml sample |
Reference samples