| --- |
| type: docs |
| title: "Dubbo 一站式微服务开发" |
| linkTitle: "微服务开发" |
| weight: 1 |
| description: "" |
| feature: |
| title: 定义服务 |
| description: > |
| 定义服务 |
| --- |
| |
| 本文可帮助开发者了解 Dubbo 微服务项目构建、开发、部署、观测、治理的全生命周期基本流程,这篇文档更多的是展示 Dubbo 的开发流程与开发模式。 |
| |
| 如果您需要可实际动手实践的示例,期望能跟随示例讲解一步步的完成开发,请参考以下每个语言的快速开始: |
| * [Java 快速开始]() |
| * [Go 快速开始]() |
| * [Rust 快速开始]() |
| |
| ## 第一步,准备微服务环境 |
| 在开发微服务之前,您需要安装相关的微服务基础设施如注册中心、服务治理控制台等。 |
| |
| 以下文档可以引导您快速的安装 Nacos、Zookeeper、Dubbo Admin 等注册中心与控制台组件。 |
| * [Kubernetes 环境安装]() |
| * [传统虚拟机环境安装]() |
| |
| 安装注册中心、服务治理中心等组件 |
| |
| ## 第一步,初始化项目 |
| 如果您正在使用 Java 或 Go 开发微服务,则可以使用 Dubbo 提供的脚手架快速创建项目骨架,骨架项目包含开发 Dubbo 必须的依赖和配置,同时还包含一些对应的微服务开发的常用模式: |
| * Java 项目脚手架 |
| * Go 项目脚手架 |
| |
| 以下是一个脚手架示例项目结构: |
| |
| ![骨架项目截图]() |
| |
| 可以直接导入 IDE 开始微服务业务开发。对于除 Java 和 Go 之外的其他语言,您也可以基于 Dubbo 提供的指引快速的创建项目。 |
| |
| ## 第二步,定义服务 |
| 服务是 Dubbo 开发、通信和治理的基本单位,通常我们讲的 Dubbo 服务是一个类似编程语言接口的概念,它是一系列可以被调用的方法的集合。通常来说,服务提供者 (Server) 负责提供服务定义的实现,而服务消费者 (Client) 基于服务定义对服务提供者发起 RPC 调用。 |
| |
| Dubbo 提供多种语言实现,开发者既可以选择以语言特有的方式定义服务,也可以选择使用语言中立的 IDL (Proto Buffers) 定义服务。 |
| |
| {{< tabpane langEqualsHeader=true >}} |
| {{< tab header="Java" >}} |
| public interface DemoService { |
| String sayHello(String name); |
| } |
| {{< /tab >}} |
| {{< tab header="Go" >}} |
| type DemoService struct { |
| SayHello func(req []string) (string, error) |
| } |
| {{< /tab >}} |
| {{< tab header="IDL" >}} |
| syntax = "proto3"; |
| |
| option java_multiple_files = true; |
| option java_package = "org.apache.dubbo.demo.hello"; |
| option java_outer_classname = "HelloWorldProto"; |
| option objc_class_prefix = "HLW"; |
| |
| package helloworld; |
| |
| service Greeter{ |
| // unary |
| rpc greet(HelloRequest) returns (HelloReply); |
| } |
| |
| // The request message containing the user's name. |
| message HelloRequest { |
| string name = 1; |
| } |
| |
| // The response message containing the greetings |
| message HelloReply { |
| string message = 1; |
| } |
| {{< /tab >}} |
| {{< /tabpane >}} |
| |
| ## 第三步,开发服务提供者 |
| 服务提供者(Server)需要完成两件事情: |
| 1. 基于上一步的服务定义给出业务逻辑实现 |
| 2. 启动一个 Dubbo Server 监听来自客户端的请求并返回服务响应 |
| <br/><br/> |
| |
| 首先,开发者遵循服务定义规范编写业务逻辑实现,如业务类需要实现特定接口或者抽象某个抽象类等。 |
| <br/><br/> |
| |
| {{< tabpane langEqualsHeader=true >}} |
| {{< tab header="Java" >}} |
| public class DemoServiceImpl implements DemoService { |
| @Override |
| public String sayHello(String name) { |
| return "Hello " + name + ", response from provider: " + RpcContext.getServiceContext().getLocalAddress(); |
| } |
| } |
| {{< /tab >}} |
| {{< tab header="Go" >}} |
| type DemoServiceImpl struct { |
| } |
| |
| func (u *DemoServiceImpl) SayHello(msg string) (string, error) { |
| return "Response message!", nil |
| } |
| {{< /tab >}} |
| {{< tab header="IDL Java" lang="Java" >}} |
| // Code generated by Dubbo plugin of protoc compiler |
| // Greeter.java |
| public interface Greeter { |
| String JAVA_SERVICE_NAME = "org.apache.dubbo.demo.hello.Greeter"; |
| String SERVICE_NAME = "helloworld.Greeter"; |
| |
| org.apache.dubbo.demo.hello.HelloReply greet(org.apache.dubbo.demo.hello.HelloRequest request); |
| |
| default CompletableFuture<org.apache.dubbo.demo.hello.HelloReply> greetAsync(org.apache.dubbo.demo.hello.HelloRequest request){ |
| return CompletableFuture.completedFuture(greet(request)); |
| } |
| // ...... |
| } |
| |
| // Code generated by Dubbo plugin of protoc compiler |
| // DubboGreeterTriple.java |
| public static abstract class GreeterImplBase implements Greeter, ServerService<Greeter> { |
| @Override |
| public org.apache.dubbo.demo.hello.HelloReply greet(org.apache.dubbo.demo.hello.HelloRequest request){ |
| throw unimplementedMethodException(greetMethod); |
| } |
| // ...... |
| } |
| |
| // The actual business logic that is written and provided by Dubbo user |
| public class GreeterServiceImpl extends DubboGreeterTriple.GreeterImplBase { |
| @Override |
| public HelloReply greet(HelloRequest request) { |
| return HelloReply.newBuilder() |
| .setMessage("Hello " + request.getName()) |
| .build(); |
| } |
| } |
| {{< /tab >}} |
| {{< tab header="IDL Go" lang="Go" >}} |
| // Paste the protoc generated and user provided code snippet here. |
| {{< /tab >}} |
| {{< tab header="IDL Rust" lang="Rust" >}} |
| use ... |
| |
| #[allow(dead_code)] |
| #[derive(Default, Clone)] |
| struct GreeterServerImpl { |
| name: String, |
| } |
| |
| // #[async_trait] |
| #[async_trait] |
| impl Greeter for GreeterServerImpl { |
| async fn greet( |
| &self, |
| request: Request<GreeterRequest>, |
| ) -> Result<Response<GreeterReply>, dubbo::status::Status> { |
| println!("GreeterServer::greet {:?}", request.metadata); |
| |
| Ok(Response::new(GreeterReply { |
| message: "hello, dubbo-rust".to_string(), |
| })) |
| } |
| } |
| {{< /tab >}} |
| {{< tab header="IDL Node.js" >}} |
| // Paste the protoc generated and user provided code snippet here. |
| {{< /tab >}} |
| {{< /tabpane >}} |
| |
| <br/> |
| 配置并注册以上服务实现类,同时,还可以指定服务参数、注册中心地址、协议与端口等配置,以下是支持几种配置格式示例: |
| <br/><br/> |
| |
| {{< tabpane langEqualsHeader=true >}} |
| {{< tab header="YAML" lang="yaml" >}} |
| dubbo: |
| application: |
| name: demo-provider |
| protocol: |
| name: dubbo |
| port: -1 |
| registry: |
| address: zookeeper://127.0.0.1:2181 |
| {{< /tab >}} |
| {{< tab header="API" lang="java" >}} |
| public static void main(String[] args) throws Exception { |
| ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>(); |
| service.setInterface(DemoService.class); |
| service.setRef(new DemoServiceImpl()); |
| |
| DubboBootstrap bootstrap = DubboBootstrap.getInstance(); |
| bootstrap.application(new ApplicationConfig("dubbo-demo-api-provider")) |
| .registry(new RegistryConfig("zookeeper://127.0.0.1:2181")) |
| .protocol(new ProtocolConfig(CommonConstants.DUBBO, -1)) |
| .service(service) |
| .start() |
| .await(); |
| } |
| {{< /tab >}} |
| {{< tab header="Spring XML" >}} |
| <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" |
| xmlns="http://www.springframework.org/schema/beans" |
| xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd |
| http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> |
| |
| <!-- Process related configurations --> |
| <dubbo:application name="demo-provider"/> |
| |
| <!-- Registry address for service discovery--> |
| <dubbo:registry address="zookeeper://127.0.0.1:2181"/> |
| |
| <!-- Specifies the RPC protocol to use and the TCP port to listen on --> |
| <dubbo:protocol name="dubbo" port="-1"/> |
| |
| <!-- Put all services need to be exported here --> |
| <dubbo:service interface="org.apache.dubbo.demo.DemoService" timeout="3000" ref="demoService"/> |
| <bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/> |
| </beans> |
| {{< /tab >}} |
| {{< tab header="Java Annotation" lang="Java" >}} |
| @DubboService |
| public class DemoServiceImpl implements DemoService { |
| // business implementation |
| } |
| |
| public class DemoServiceComponent implements DemoService { |
| @DubboReference |
| private DemoService demoService; |
| } |
| {{< /tab >}} |
| {{< tab header="dubbo.properties" lang="properties" >}} |
| dubbo.application.name=dubbo-demo-annotation-provider |
| dubbo.protocol.name=dubbo |
| dubbo.protocol.port=-1 |
| dubbo.registry.address=zookeeper://127.0.0.1:2181 |
| {{< /tab >}} |
| {{< /tabpane >}} |
| |
| <br/> |
| 启动 Server 监听服务 |
| <br/><br/> |
| |
| {{< tabpane langEqualsHeader=true >}} |
| {{< tab header="Java" >}} |
| public static void main(String[] args) throws Exception { |
| ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>(); |
| service.setInterface(DemoService.class); |
| service.setRef(new DemoServiceImpl()); |
| |
| DubboBootstrap bootstrap = DubboBootstrap.getInstance(); |
| bootstrap.application(new ApplicationConfig("dubbo-demo-api-provider")) |
| .registry(new RegistryConfig("zookeeper://127.0.0.1:2181")) |
| .protocol(new ProtocolConfig(CommonConstants.DUBBO, -1)) |
| .service(service) |
| .start() |
| .await(); |
| } |
| {{< /tab >}} |
| {{< tab header="Go" >}} |
| func main() { |
| config.SetProviderService(&DemoServiceImpl{}) |
| if err := config.Load(); err != nil { |
| panic(err) |
| } |
| select {} |
| } |
| {{< /tab >}} |
| {{< tab header="Rust" >}} |
| #[tokio::main] |
| async fn main() { |
| register_server(GreeterServerImpl { |
| name: "greeter".to_string(), |
| }); |
| |
| // Dubbo::new().start().await; |
| Dubbo::new() |
| .with_config({ |
| let r = RootConfig::new(); |
| match r.load() { |
| Ok(config) => config, |
| Err(_err) => panic!("err: {:?}", _err), // response was droped |
| } |
| }) |
| .start() |
| .await; |
| } |
| {{< /tab >}} |
| {{< tab header="Node.js" >}} |
| // Put node.js server bootstrapping snippet here |
| {{< /tab >}} |
| {{< /tabpane >}} |
| |
| <br/> |
| 至此,能监听请求并提供特定服务的 Dubbo Server 就开发和启动完成了。 |
| |
| ## 第四步,开发服务消费者 |
| |
| 接下来就是开发一个调用服务的 Client 了 |
| |
| > 在 Client 调用服务的前提是需要有服务定义的依赖,这可以通过语言特定的依赖分发系统或 IDL 管理系统实现。 |
| |
| 通过配置/API声明服务调用,告诉 Dubbo 要生成 Proxy 的服务,同时可以指定服务发现的注册中心等配置: |
| |
| {{< tabpane langEqualsHeader=true >}} |
| {{< tab header="YAML" lang="yaml" >}} |
| dubbo: |
| application: |
| name: demo-consumer |
| registry: |
| address: zookeeper://127.0.0.1:2181 |
| {{< /tab >}} |
| {{< tab header="API" lang="java" >}} |
| private static void main(String[] args) { |
| ReferenceConfig<DemoService> reference = new ReferenceConfig<>(); |
| reference.setInterface(DemoService.class); |
| reference.setGeneric("true"); |
| |
| DubboBootstrap bootstrap = DubboBootstrap.getInstance(); |
| bootstrap.application(new ApplicationConfig("demo-consumer")) |
| .registry(new RegistryConfig("zookeeper://127.0.0.1:2181")) |
| .reference(reference) |
| .start(); |
| } |
| {{< /tab >}} |
| {{< tab header="Spring XML" >}} |
| <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" |
| xmlns="http://www.springframework.org/schema/beans" |
| xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd |
| http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> |
| |
| <dubbo:application name="demo-consumer"/> |
| |
| <dubbo:registry id="demo1" address="zookeeper://127.0.0.1:2181"/> |
| |
| <dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.demo.DemoService"/> |
| </beans> |
| {{< /tab >}} |
| {{< tab header="Java Annotation" lang="Java" >}} |
| public class SpringApplication { |
| @DubboReference |
| private DemoService demoService; |
| } |
| {{< /tab >}} |
| {{< tab header="dubbo.properties" lang="properties" >}} |
| dubbo.application.name=dubbo-demo-annotation-provider |
| dubbo.registry.address=zookeeper://127.0.0.1:2181 |
| {{< /tab >}} |
| {{< /tabpane >}} |
| |
| <br/> |
| 启动 Client |
| <br/><br/> |
| |
| {{< tabpane langEqualsHeader=true >}} |
| {{< tab header="Java" >}} |
| public static void main(String[] args) throws Exception { |
| // ... |
| DemoService demoService = bootstrap.getCache().get(reference); |
| String message = demoService.sayHello("dubbo"); |
| System.out.println("Result: " + message); |
| } |
| {{< /tab >}} |
| {{< tab header="Go" >}} |
| func main() { |
| config.SetProviderService(&DemoServiceImpl{}) |
| if err := config.Load(); err != nil { |
| panic(err) |
| } |
| select {} |
| } |
| {{< /tab >}} |
| {{< tab header="Rust" >}} |
| #[tokio::main] |
| async fn main() { |
| let mut cli = GreeterClient::new().with_uri("http://127.0.0.1:8888".to_string()); |
| |
| println!("# unary call"); |
| let resp = cli |
| .greet(Request::new(GreeterRequest { |
| name: "message from client".to_string(), |
| })) |
| .await; |
| let resp = match resp { |
| Ok(resp) => resp, |
| Err(err) => return println!("{:?}", err), |
| }; |
| let (_parts, body) = resp.into_parts(); |
| println!("Response: {:?}", body); |
| } |
| {{< /tab >}} |
| {{< tab header="Node.js" >}} |
| // Put node.js client snippet here |
| {{< /tab >}} |
| {{< /tabpane >}} |
| |
| 服务调用 |
| |
| ## 第五步,打包部署 |
| |
| #### 语言特定形式分发包 |
| |
| 您可以选择以语言特定方式打包 Dubbo 开发的服务 (如 Java Jar、Go Module 等),并以语言提供的发机制将二进制包分发出去。一般来讲,要注意一以下几点: |
| * 服务定义最好作为单独的二进制包由 Server 端定义并打包分发,以便所有 Client 都能依赖并基于服务定义编码; |
| * Dubbo Server 和 Dubbo Client 的打包与分发与普通应用完全一样,如 Java 应用就可以用 Maven 或 Gradle 直接打包分发; |
| * 如果您是以 IDL 方式定义服务,还需要考虑 IDL 的分发与管理方式; |
| |
| #### Docker 镜像 |
| |
| 在当今容器时代,打包为 Docker 镜像已变为更通用的分发形式 |
| |
| ```sh |
| docker build -t ${your-organization}/${project-name}:${tag-or-version} . |
| ``` |
| |
| > 通常,在脚手架生成的根目录下 `/deploy` 有预先生成的镜像打包 Dockerfile 模版,可以按需修改后直接用来打包。 |
| |
| |
| ## 第六步,部署 |
| Dubbo 微服务支持多种部署架构,与云原生基础设施做了很好的适配: |
| * 传统的自建服务治理体系模式,需自行维护微服务需要的注册中心集群、配置中心集群等 |
| * 基于 Kubernetes Native Service 微服务体系,此时 Kubernetes 集群承担服务抽象、注册中心、配置中心等角色 |
| * 服务网格架构,服务治理角色由控制面承担,Dubbo 作为数据面组件与 Sidecar 部署在一起,或者采用无 Sidecar 的 Proxyless 架构 |
| |
| #### 传统自建注册、配置中心模式 |
| |
| Dubbo 微服务需要依赖一些中心化集群协调状态,以下是一个抽象的 Dubbo 部署架构图: |
| |
| ![三中心部署架构图]() |
| |
| 图:部署在虚拟机或 Kubernetes 集群的传统 Dubbo 微服务架构 |
| |
| * 注册中心。协调 Consumer 与 Provider 之间的地址注册与发现 |
| * 配置中心 (可选) |
| * 存储 Dubbo 启动阶段的全局配置,保证配置的跨环境共享与全局一致性 |
| * 负责服务治理规则(路由规则、动态配置等)的存储与推送。 |
| * 元数据中心 (可选) |
| * 接收 Provider 上报的服务接口元数据,为 Admin 等控制台提供运维能力(如服务测试、接口文档等) |
| * 作为服务发现机制的补充,提供额外的接口/方法级别配置信息的同步能力,相当于注册中心的额外扩展 |
| |
| 以上三个中心集群并不是运行 Dubbo 的必要条件,用户完全可以根据自身业务情况决定只启用其中一个或多个,以达到简化部署的目的。通常情况下,所有用户都会从独立的注册中心开始 Dubbo 服务开发,而配置中心、元数据中心则会在微服务演进的过程中逐步的按需被引入进来。注册中心、配置中心和元数据中心都是逻辑概念,它们完全可以是同一个物理集群,如部署一个 Zookeeper 集群同时作为注册中心、配置中心和元数据中心。 |
| |
| 如您是在 Kubernetes 搭建 Dubbo 微服务集群,请参考 [如何在 Kubernetes 集群部署 Dubbo 服务]() 了解更多。 |
| |
| #### Kubernetes 原生服务 |
| |
| ![参考下 Spring Kubernetes 等的架构图]() |
| |
| 在这种模式下,Dubbo 服务将与 Kubernetes 原生服务实现概念对齐,同时,开发者也不再需要部署独立的注册、配置中心集群,这部分职责由 Kubernetes 及相应组件如 Service、ConfigMap、Deployment 等承担。具体是原理上,是由 Dubbo 节点直接与 Kubernete api-server 或 DNS 通信实现。 |
| |
| 请参考 [Dubbo Kubernetes 原生服务任务]() 了解更多 |
| |
| #### 服务网格 |
| |
| Dubbo 服务可以无缝接入 Istio 体系,并且,Dubbo 支持更灵活的数据面部署形式 |
| * Sidecar 模式,Dubbo 可以与 Envoy 等代理部署在一起,实现流量拦截和治理 |
| * Proxyless 模式,Dubbo 通过直接与 Istio 等控制面通信,实现与 Sidecar 模式对等能力的同时,减少了部署成本和性能损耗。 |
| |
| ![服务网格部署架构图](/imgs/v3/mesh/thinsdk-envoy.png) |
| |
| 具体可参见 [Dubbo 服务网格]() 部分说明。 |
| |
| ## 第七步,观测服务状态 |
| |
| 可以通过 Dubbo 官方提供的 Admin 控制台非常方便的观测服务运行状态。 |
| |
| ![Admin 截图]() |
| |
| 请参考 [如何部署 Admin]() 了解如何将 Admin 部署您的开发或生产环境。 |
| |
| > 注意:部署过程中必须要配置 Admin 连接到您正在使用的注册中心或配置中心集群,保证 Admin 和 Dubbo 微服务集群共享相同数据源。 |
| |
| 如果您将 Dubbo 部署在服务网格架构,则还可以使用对应控制面产品支持的控制台来观测 Dubbo 服务状态,如 Istio、Kiali。更多可观测能力如 Accessing Log、Tracing 等,请参考[可观测性]()文档。 |
| |
| ## 第八步,服务治理 |
| 为了使 Dubbo 服务稳定、可控的运行,我们需要在运行态对 Dubbo 服务进行治理。 |
| |
| 首先,可以通过 Dubbo Admin 完成绝大多数的治理需求,如查看服务状态、下发流量规则等 |
| |
| ![Dubbo Admin 治理效果图]() |
| |
| 对于更高阶的治理诉求,可通过以下内容了解: |
| |
| * [网关流量接入](),通过网关实现前端 HTTP 流量接入 Dubbo 服务 |
| * [限流降级](),通过 Sentinel 等突发流量,就要用到限流降级能力, |
| * [数据一致性](),了解 Dubbo 服务的分布式事物解决方案 |
| * [全链路追踪](),了解 Dubbo 服务如何接入 Zipkin、Skywalking、OpenTracing 等全链路监控组件 |
| * [服务发现](),了解 Dubbo 的服务发现机制和扩展实现 |
| * [流量管控](),了解 Dubbo 丰富的流量管控规则定义及使用方式 |
| |