Dubbo:高性能 RPC 分布式服务框架
Dubbo是阿里巴巴开源的基于 Java 的高性能 RPC(一种远程调用) 分布式服务框架(SOA),致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。Macula 架构使用 Dubbo Spring Cloud 为组件模块,项目中引入 macula-cloud-dubbo 便可直接用于分布式服务的使用。
Dubbo Spring Cloud 主要特性
- 面向接口代理的高性能RPC调用:提供高性能的基于代理的远程调用能力,服务以接口为粒度,屏蔽了远程调用底层细节。
- 智能负载均衡:内置多种负载均衡策略,智能感知下游节点健康状况,显著减少调用延迟,提高系统吞吐量。
- 服务自动注册与发现:支持多种注册中心服务,服务实例上下线实时感知。
- 高度可扩展能力:遵循微内核+插件的设计原则,所有核心能力如Protocol、Transport、Serialization被设计为扩展点,平等对待内置实现和第三方实现。
- 运行期流量调度:内置条件、脚本等路由策略,通过配置不同的路由规则,轻松实现灰度发布,同机房优先等功能。
- 可视化的服务治理与运维:提供丰富服务治理、运维工具:随时查询服务元数据、服务健康状态及调用统计,实时下发路由策略、调整配置参数。
Quick Start.
- 创建父工程demo-member
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.macula.base</groupId>
<artifactId>demo-member</artifactId>
<version>4.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>demo-member-infra</module>
<module>demo-member-admin</module>
<module>demo-member-esb-api</module>
<module>demo-member-esb-api-impl</module>
</modules>
</project>
总项目中 modules 说明: demo-member-infra :基础对象公共模块,存放对象实体类, Repostory, 公共服务类等。
demo-member-admin :管理端接口,本地服务接口调用。
demo-member-esb-api :Dubbo Api 定义。
demo-member-esb-api-impl :dubbo与rest服务供应商, Dubbo Api 的实现。
- 子模块demo-member-esb-api
API模块,存放Dubbo服务接口和模型定义,非必要,这里创建仅为更好的代码重用以及接口、模型规格控制管理。
定义抽象接口UserApi.java:
package org.macula.base.api;
import org.macula.base.vo.UserVo;
public interface UserApi {
/**
* 根据userName 获取用户
*/
UserVo get(String userName);
/**
* 保存用户信息
*/
boolean save(UserVo user);
}
- 子模块 demo-member-esb-api-impl Dubbo服务提供方
pom.xml 依赖:
<parent>
<groupId>org.maculaframework</groupId>
<artifactId>macula-parent</artifactId>
<version>4.0.0-SNAPSHOT</version>
<relativePath/>
</parent>
<!-- Dubbo 使用相关依赖 -->
<dependency>
<groupId>org.maculaframework</groupId>
<artifactId>macula-cloud-dubbo</artifactId>
</dependency>
<!-- nacos 注册中心相关依赖 -->
<dependency>
<groupId>org.maculaframework</groupId>
<artifactId>macula-cloud-nacos</artifactId>
</dependency>
<!--实现 demo-api 模块 -->
<dependency>
<groupId>org.macula.base</groupId>
<artifactId>demo-member-esb-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- 公共模块依赖 -->
<dependency>
<groupId>org.macula.base</groupId>
<artifactId>demo-member-infra</artifactId>
<version>${revision}</version>
<scope>compile</scope>
</dependency>
此处引入公共模块,API模块, 架构dubbo模块 等依赖。 实现Dubbo接口,UserApiImpl.java如下:
package org.macula.base.api;
import org.apache.dubbo.config.annotation.DubboService;
import org.macula.base.member.domain.UserDemoEntity;
import org.macula.base.member.repository.UserDemoRepository;
import org.macula.base.vo.UserVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
@DubboService(protocol = {"dubbo", "rest"})
@Path("/user")
public class UserApiImpl implements UserApi {
@Autowired
private UserDemoRepository userDemoRepository;
@Override
@Path("get")
@GET
@Produces(APPLICATION_JSON_VALUE)
@Consumes(MediaType.APPLICATION_JSON)
public UserVo get(@QueryParam("userName") String userName) {
Assert.hasLength(userName, "macula.get.user.userName.notBlank");
UserDemoEntity user = userDemoRepository.findByUserName(userName);
if (user == null) {
return null;
}
return convert(user);
}
@Override
@Path("save")
@POST
@Produces(APPLICATION_JSON_VALUE)
public boolean save(UserVo user) {
Assert.notNull(user, "macula.sync.user.notNull");
UserDemoEntity currentUser = userDemoRepository.findByUserName(user.getUserName());
UserDemoEntity newUser = convert(user);
if (currentUser != null) {
BeanUtils.copyProperties(newUser, currentUser, "id");
return userDemoRepository.save(currentUser).getId() != null;
} else {
return userDemoRepository.save(newUser).getId() != null;
}
}
private UserVo convert(UserDemoEntity userEntity) {
UserVo userVO = new UserVo();
BeanUtils.copyProperties(userEntity, userVO);
return userVO;
}
private UserDemoEntity convert(UserVo userVO) {
UserDemoEntity userEntity = new UserDemoEntity();
BeanUtils.copyProperties(userVO, userEntity);
return userEntity;
}
}
@DubboService 声明了Dubbo请求 与Rest请求 两种模式, @Path("/user") 为 rest 请求路径地址,结合方法路径上的 @Path(“get”) 发送 http 使用。 配置文件 application.yml 需要将Java服务(本地)配置为 Dubbo 服务(远程)如下:
## 服务发现与注册配置
spring:
cloud:
nacos:
discovery:
server-addr: ${macula.nacos.url}
namespace: ${macula.nacos.namespace}
username: ${macula.nacos.username}
password: ${macula.nacos.password}
###########dubbo配置###############
dubbo:
application:
name: ${spring.application.name}
scan:
# dubbo 服务扫描基准包
base-packages: org.macula.base.api
protocols:
dubbo:
# dubbo 协议
name: dubbo
# dubbo 协议端口( -1 表示自增端口,从 20880 开始)
port: -1
threads: 500
rest:
# http rest 请求
name: rest
port: 9090
server: netty
registries:
macula:
default: true
timeout: 60000
# 挂载到 Spring Cloud 注册中心
address: nacos://${macula.nacos.registry.url}?namespace=${macula.nacos.namespace}&username=${macula.nacos.username}&password=${macula.nacos.password}
dubbo.scan.base-packages
:指定 Dubbo 服务实现类的扫描基准包dubbo.protocols
:- name: dubbo 为 Dubbo服务暴露的协议配置,其中子属性name为协议名称,port为协议端口(-1 表示自增端口,从 20880 开始)
- name: rest 为http协议,server:netty 为应用启动容器服务
dubbo.registries
:- macula: default true 为 Dubbo 服务默认注册中心配置,其中子属性address 的值 “nacos://${macula.nacos.registry.url}",说明挂载到 Nacos 注册中心。
dubbo.application.name
:dubbo服务名称spring.cloud.nacos.discovery
:Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口。
创建服务启动类
@DubboComponentScan
@SpringBootApplication
@EnableDiscoveryClient
public class MemberEsbDubboApplication {
public static void main(String[] args) {
SpringApplication.run(MemberEsbDubboApplication.class, args);
}
}
- 子模块 demo-member-admin 服务调用方
工程依赖 pom.xml
<dependency>
<groupId>org.macula.base</groupId>
<artifactId>demo-member-esb-api</artifactId>
<version>${revision}</version>
</dependency>
配置文件 application.yml
## 服务发现与注册配置
spring:
cloud:
nacos:
discovery:
server-addr: ${macula.nacos.url}
namespace: ${macula.nacos.namespace}
username: ${macula.nacos.username}
password: ${macula.nacos.password}
dubbo:
scan:
# dubbo 服务扫描基准包
base-packages: org.macula.base.api
cloud:
# The subscribed services in consumer side
subscribed-services: ${provider.application.name}
protocols:
dubbo:
port: -1
consumer:
timeout: 60000
registries:
macula:
default: true
address: nacos://${macula.nacos.registry.url}?namespace=${macula.nacos.namespace}&username=${macula.nacos.username}&password=${macula.nacos.password}
timeout: 60000
测试接口HelloController.java
@RestController
public class HelloController {
@DubboReference
private UserApi userApi;
private static final String BASE_URL = "http://localhost:9090";
// 通过Dubbo协议请求
@GetMapping("/testDubbo")
public String testDubbo() {
return userApi.get("admin");
}
// 通过Http Rest 请求
@GetMapping("/testRest")
private static void testRest() {
String uri = "/user/get?userName=admin";
// 发起请求
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(BASE_URL + uri)
.get()
.build();
try (Response response = client.newCall(request).execute()) {
System.out.println("请求结果: " + response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}