Spring Cloud Alibaba Dubbo
为什么?是什么?怎么做?
为什么?
-
单体应用
单体服务经过长期的迭代更新,逐渐走向代码臃肿、高耦合,这显然与我们软件开发设计理念高内聚低耦合
背道而驰,从而难以维护~
市场需求也在逐渐要求服务高并发、高性能、高可用,在这样的场景下,单体服务宕机重启时间长、访问过慢的问题一一暴露! -
分布式应用系统
分布式开发,将服务拆分,是建立在网络之上的软件系统。正是因为软件的特性,所以分布式系统具有高度的内聚性和透明性。
这么多服务,遇到四个问题:
- 客户端如何访问?
- 服务与服务间如何通信?
- 如何管理这么多服务?
- 挂了怎么办?
是什么?
微服务架构是一种架构概念,实际的开发时分布式应用的开发,旨在解决分布式开发下遇到的四个问题
怎么做?
1. 目标
Spring Cloud Alibaba Dubbo 项目的目标是将 Dubbo 融入到 Spring Cloud Alibaba 生态中,使微服务之间的调用同时具备 RESTful 和 Dubbo 调用的能力。
RPC 即远程过程调用(Remote Procedure Call Protocol,简称RPC),像调用本地服务(方法)一样调用服务器的服务(方法)
REST直接使用HTTP做应用层协议
- RPC,支持多种语言(但不是所有语言),四层通讯协议,性能高,节省带宽。相对Restful协议,使用RPC,在同等硬件条件下,带宽使用率仅为前者的20%,性能却提升一个数量级。但是这种协议最大的问题在于,无法穿透防火墙。
- 以Spring Cloud为代表所支持的Restful 协议,优势在于能够穿透防火墙,使用方便,语言无关,基本上可以使用各种开发语言实现的系统,都可以接受Restful 的请求。但性能和带宽占用上有劣势。
所以,对微服务的实现,基本是确定一个组织边界,在该边界内,使用RPC; 边界外,使用Restful。这个边界,可以是业务、部门,甚至是全公司。
做到对业务代码无侵入,无感知;引入 JAR 包则微服务间调用使用 Dubbo,去掉 JAR 包则使用默认的 RESTful;
实现参考的是 Spring Cloud 中文社区的 Spring Cloud Dubbo 项目。
2. 部署Nacos
既然已经将 Dubbo 集成进了 Spring Cloud Alibaba,那么理所当然的我们的注册中心也不再采用 Zookeeper 方案而是转为 Nacos 方案了
部署 Nacos
下载并安装
# 下载源码
git clone https://github.com/alibaba/nacos.git
# 安装到本地仓库
cd nacos/
mvn -Prelease-nacos clean install -U
# 启动服务
cd distribution/target/nacos-server-0.7.0/nacos/bin
startup.cmd
实现基于 Feign 的注册方案
Dubbo
API
Provider
@Service
Comsumer
@Reference
Spring Cloud Alibaba
Feign(HttpClient)
Comsumer
@FeignClient()
由于 Nacos、Dubbo、Spring Cloud Alibaba 都是阿里系产品,所以我们可以很容易的将 Dubbo 和 Http 服务注册到 Nacos 中。
此时服务提供者即注册了 Dubbo 又注册了 Http 服务,服务消费者根据配置方式可以在 Dubbo 与 Http 调用中随意切换
服务提供者
服务提供者在使用 Dubbo 注册服务时是需要使用 @Service
注解将服务注册到注册中心的,现在改用 @FeignClient
注解来注册
以 spring-cloud-alibaba-dubbo-provider-api
项目中定义的 API 接口为例
package com.funtl.alibaba.dubbo.provider.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient("provider")
public interface ProviderService {
@GetMapping("/hi")
String hi();
}
以 spring-cloud-alibaba-dubbo-provider-service
项目中实现接口为例
package com.funtl.alibaba.dubbo.provider.service.impl;
import com.funtl.alibaba.dubbo.provider.service.ProviderService;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProviderServiceImpl implements ProviderService {
@Override
public String hi() {
return "Hi Spring Cloud Alibaba Dubbo";
}
}
以上代码是一个典型的 Spring Cloud RESTFul API,服务提供者需要做的就是引入 Dubbo 相关依赖
扫描包含 @FeignClient
注解的类并注册到 Nacos 即可,关键代码在 spring-cloud-alibaba-dubbo-core
项目的 FeignClientToDubboProviderBeanPostProcessor
类中
/**
* Registers Beans whose classes was annotated {@link FeignClient}
*
* @param packagesToScan The base packages to scan
* @param registry {@link BeanDefinitionRegistry}
*/
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class, true, true));
for (String packageToScan : packagesToScan) {
// Registers @Service Bean first
scanner.scan(packageToScan);
// Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
registerServiceBean(beanDefinitionHolder, registry, scanner);
}
if (logger.isInfoEnabled()) {
logger.info(beanDefinitionHolders.size() + " annotated Dubbo's @Service Components { " +
beanDefinitionHolders +
" } were scanned under package[" + packageToScan + "]");
}
} else {
if (logger.isWarnEnabled()) {
logger.warn("No Spring Bean annotating Dubbo's @Service was found under package["
+ packageToScan + "]");
}
}
}
}
服务消费者
服务消费者需要依赖 spring-cloud-alibaba-dubbo-provider-api
,并直接使用 @Autowired
注解即可实现注入,可以不使用 Dubbo 提供的 @Reference
注解
package com.funtl.alibaba.dubbo.consumer.service;
import com.funtl.alibaba.dubbo.provider.service.ProviderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestService {
@Autowired
private ProviderService providerService;
@GetMapping("/test")
public String test() {
return providerService.hi();
}
}
以上代码是一个典型的 Spring Cloud Feign 调用。我们只需要替换 Feign 的实现。产生 ProviderService
接口的 ProxyBean
时,使用 Dubbo 产生的 Bean 替换默认的 Feign 产生的 RESTFul 调用的 Bean 即可,关键代码在 spring-cloud-alibaba-dubbo-core
项目的 DubboFeignBuilder
类中
@Override
public <T> T target(Target<T> target) {
ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
.create(defaultReference, target.getClass().getClassLoader(), applicationContext)
.interfaceClass(target.type());
try {
T object = (T) beanBuilder.build().getObject();
return object;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
消费者使用 RESTFul 调用
只需将 Dubbo 相关依赖排除即可
<dependency>
<groupId>com.funtl</groupId>
<artifactId>spring-cloud-alibaba-dubbo-starter</artifactId>
<exclusions>
<exclusion>
<groupId>com.funtl</groupId>
<artifactId>spring-cloud-alibaba-dubbo-core</artifactId>
</exclusion>
<exclusion>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>