blob: 27fd038a8e5ddc8a53592ecfa8dc4bfce9be5325 [file] [log] [blame]
---
type: docs
title: "Dubbo 一站式微服务开发"
linkTitle: "微服务开发"
weight: 1
description: ""
feature:
title: 定义服务
description: >
定义服务
---
本文可帮助开发者了解 Dubbo 微服务项目构建、开发、部署、观测、治理的全生命周期基本流程,这篇文档更多的是展示 Dubbo 的开发流程与开发模式。
如果您需要可实际动手实践的示例,期望能跟随示例讲解一步步的完成开发,请参考以下每个语言的快速开始:
* [Java 快速开始]()
* [Go 快速开始]()
* [Rust 快速开始]()
## 第一步,准备微服务环境
在开发微服务之前,您需要安装相关的微服务基础设施如注册中心、服务治理控制台等。
以下文档可以引导您快速的安装 NacosZookeeperDubbo 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 JarGo 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 及相应组件如 ServiceConfigMapDeployment 等承担。具体是原理上,是由 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 服务状态,如 IstioKiali。更多可观测能力如 Accessing LogTracing 等,请参考[可观测性]()文档。
## 第八步,服务治理
为了使 Dubbo 服务稳定、可控的运行,我们需要在运行态对 Dubbo 服务进行治理。
首先,可以通过 Dubbo Admin 完成绝大多数的治理需求,如查看服务状态、下发流量规则等
![Dubbo Admin 治理效果图]()
对于更高阶的治理诉求,可通过以下内容了解:
* [网关流量接入](),通过网关实现前端 HTTP 流量接入 Dubbo 服务
* [限流降级](),通过 Sentinel 等突发流量,就要用到限流降级能力,
* [数据一致性](),了解 Dubbo 服务的分布式事物解决方案
* [全链路追踪](),了解 Dubbo 服务如何接入 ZipkinSkywalkingOpenTracing 等全链路监控组件
* [服务发现](),了解 Dubbo 的服务发现机制和扩展实现
* [流量管控](),了解 Dubbo 丰富的流量管控规则定义及使用方式