手把手教你搭建SpringCloudAlibaba微服务架构——集成Dubbo、Feign实现服务注册、远程服务调用(RPC)
目录
@
前言
说实话我一直在纠结要怎么写这篇文章,一个从来没有在生产环境用过微服务的人,竟然在教别人怎样用微服务,说起来不觉得有点搞笑吗?但是最终还是决定了要写,因为我觉得学习本身就是一个不断探索的过程,不能一直停留在自己的认知中。
这个系列的文章都是建立在我个人对微服务的一个认知上的,每个人对微服务的理解应该都是不一样的,如果觉得写的还行可以点个赞表扬一下,如果觉得哪里写的有问题也欢迎大家在线diss我。
环境准备
Nacos肯定是要有的,上一篇已经介绍了Nacos的搭建以及配置中心的基本使用,而在这篇文章中Nacos扮演的是注册中心的角色,通过集成Dubbo与Feign完成RPC的调用,还没有搭建Nacos的小伙伴可以参考我的上一篇文章哦。
还不知道啥是注册中心、啥是RPC的同学可以出门左拐了:www.baidu.com
为什么需要RPC?
架构的演进这里就不介绍了,当我们的应用演进到分布式架构时,进程与进程之间的通信是无法避免的,于是我们会通过一些类似于HttpClient的工具来发起Http请求,完成进程与进程之间的通信。这个其实也算是RPC(Remote Procedure Call)的一种实现。但Http协议其实是为浏览器量身打造的一种应用层协议,Http头部包含了浏览器需要的一些额外控制信息,我们系统大多数时候并不怎么关注这部分信息,于是我们就想能不能为分布式系统调用也量身定做一套应用层协议呢?于是RPC框架就此诞生了。
tip:这是我目前的理解,感觉还是有些不太牢靠,大家如果有不一样的见解记得call我一下,万分感谢。
基于Nacos的服务注册与发现
项目结构
这个项目结构大致是分了3个层级
dubboDemo是顶层的父级项目,主要是通过pom文件统一管理该项目下依赖的版本号。
第二层级是order-service及order-service-consumer这两个子模块,前者用于提供订单服务,后者用于调用订单服务并转换为http接口暴露给外部系统。
第三层级是order-service模块下的order-service-api及order-service-provider这两个子模块,api用于维护所提供的dubbo接口,provider是对api接口的一种实现
dubboDemo工程pom文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
</parent>
<groupId>com.example</groupId>
<artifactId>dubboDemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>dubboDemo</name>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR3</spring-cloud.version>
<spring-boot.version>2.2.5.RELEASE</spring-boot.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
<spring-cloud-sentinel.version>2.1.1.RELEASE</spring-cloud-sentinel.version>
<sentinel-datasource-nacos.version>1.7.1</sentinel-datasource-nacos.version>
<lombok.version>1.18.16</lombok.version>
</properties>
<modules>
<module>order-service</module>
<module>order-service-consumer</module>
</modules>
<dependencyManagement>
<dependencies>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!--Dubbo-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
<version>${spring-cloud-alibaba.version}</version>
</dependency>
<!--Nacos Discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${spring-cloud-alibaba.version}</version>
</dependency>
<!--Nacos Config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>${spring-cloud-alibaba.version}</version>
</dependency>
<!--Openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>${spring-cloud-alibaba.version}</version>
</dependency>
<!--SpringCloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
pay-service工程pom文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>dubboDemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>order-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>order-service</name>
<packaging>pom</packaging>
<description>This is order Service</description>
<modules>
<module>order-service-api</module>
<module>order-service-provider</module>
</modules>
<dependencies>
<!--Spring Web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--SpringBoot Test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--Dubbo-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<!--Nacos Discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
order-service-consumer工程pom文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>dubboDemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>order-service-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>order-service-consumer</name>
<description>Demo project for Spring Boot</description>
<dependencies>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--Order-Service-Api-->
<dependency>
<groupId>com.example</groupId>
<artifactId>order-service-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!--Spring Web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--SpringBoot Test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--Dubbo-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
order-service-api工程pom文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>order-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>order-service-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>order-service-api</name>
<packaging>jar</packaging>
<description>This is Order Service Api</description>
</project>
order-service-provider工程pom文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>order-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>order-service-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>order-service-provider</name>
<description>This is Order Service Provider</description>
<dependencies>
<!--api-->
<dependency>
<groupId>com.example</groupId>
<artifactId>order-service-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
Dubbo
Dubbo是阿里巴巴公司开源的一个高性能的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和Spring框架无缝集成,目前已捐献给Apache软件基金会成为Apache顶级孵化项目。
官网地址:http://dubbo.apache.org/zh-cn/docs/2.7/user/preface/background/
架构图:
Dubbo服务注册
在order-service-api工程下创建一个OrderService的Java接口,用于对外暴露dubbo服务
package com.example.orderserviceapi.order;
/**
* @author hcq
* @date 2020/11/16 11:29
*/
public interface OrderService {
/**
* 下单
* @param shopName 商品名称
* @return orderId
*/
String placeAnOrder(String shopName);
}
在order-service-provider工程下实现api中的orderService接口,并配置dubbo协议、注册中心地址等信息
注意:provider是要依赖api工程的,否则provider会提示无法找到OrderService接口
package com.example.orderserviceprovider.order;
import com.example.orderserviceapi.order.OrderService;
import org.apache.dubbo.config.annotation.Service;
/**
* @author hcq
* @date 2020/11/16 11:32
* @Service是Dubbo中的注解,不要与Spring中的@Service搞混
*/
@Service
public class OrderServiceImpl implements OrderService {
@Override
public String placeAnOrder(String shopName) {
return String.format("下单成功,商品名称:%s,订单号:%s", shopName, System.currentTimeMillis());
}
}
order-service-provider: application.yml
server:
port: 8000
spring:
application:
name: order-privoder # 服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 # 服务注册中心地址
dubbo:
protocol: # name:采用的协议名称 port:-1代表自动选择端口
port: -1
name: dubbo
scan: # dubbo服务路径
base-packages: com.example.orderserviceprovider
application:
qos-enable: false #关闭dubbo在线运维工具
registry:
address: spring-cloud://localhost #挂载到SpringCloud注册中心
cloud:
subscribed-services: / #订阅的服务名称,默认订阅所有服务,provider无须订阅服务所以配置为/
启动provider项目后,登录Nacos查看服务列表,可以看到order-provider服务已经成功注册到Nacos中了
Dubbo服务消费
首先在order-service-consumer工程下引入order-service-api依赖(consumer需要依赖哪个服务就引入相关服务的api依赖)。
<!--Order-Service-Api-->
<dependency>
<groupId>com.example</groupId>
<artifactId>order-service-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
order-service-consumer:application.yml 配置注册中心地址、订阅的服务列表等信息
server:
port: 8001
spring:
application:
name: order-comsumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
dubbo:
application:
qos-enable: false #关闭dubbo在线运维工具
cloud:
subscribed-services: order-privoder #订阅的服务名称,多个服务之间通过,分割
提供Http接口给外部系统。@Reference用于引用注册中心的dubbo服务
@RestController
@RequestMapping("order")
public class OrderController {
@Reference
OrderService orderService;
@GetMapping("/placeAnOrder")
public String placeAnOrder(String shopName) {
return orderService.placeAnOrder(shopName);
}
}
浏览器访问:http://localhost:8001/order/placeAnOrder?shopName=MacBookPro
总结
- dubbo通过接口名称对注册中心暴露所提供的服务列表,consumer从注册中心订阅所需要的服务完成RPC调用
OpenFeign
Feign是一个声明式的Web Service客户端。它的出现使开发Web Service客户端变得很简单。使用Feign只需要创建一个接口加上对应的注解。
OpenFeign服务注册
由于OpenFeign是基于Http协议做的服务暴露,所以服务提供方无须改动,只需要按照SpringMVC模式对外提供一个Http接口并注册到注册中心即可,比如这里可以直接在provider模块中新增一个Http接口
/**
* @author hcq
* @date 2020/11/16 12:42
*/
@RequestMapping("order")
@RestController
public class OrderController {
@RequestMapping("/now")
public Long now() {
return System.currentTimeMillis();
}
}
重启provider模块后,通过浏览器访问http://localhost:8080/order/now,成功返回当前系统时间
OpenFeign服务消费
OpenFeign消费端需要引入Openfeign依赖
<!--Openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<exclusions>
<exclusion>
<artifactId>jsr305</artifactId>
<groupId>com.google.code.findbugs</groupId>
</exclusion>
</exclusions>
</dependency>
启动类添加@EnableFeignClients注解,启动Feign注解
@SpringBootApplication
@EnableFeignClients
public class OrderServiceConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceConsumerApplication.class, args);
}
}
新增一个接口,然后通过@FeignClient引用order-privoder提供的服务。
/**
* @author hcq
* @date 2020/11/16 14:00
*/
@FeignClient("order-privoder")
public interface OrderServiceFeign {
@RequestMapping(value = "/order/now", method = RequestMethod.GET)
String now();
}
启动consumer,并访问http://localhost:8001/order/now
总结
- OpenFeign通过订阅注册中心的服务从而获取到服务节点的地址信息(ip:port),并通过HTTP协议完成RPC调用
Dubbo与OpenFeign对比
-
OpenFeign基于Http1.1实现RPC调用,服务提供者需要对外暴露Http接口供消费者调用,服务粒度是http接口级的
-
Dubbo默认基于dubbo协议且支持多种协议配置,注册中心、服务提供者、服务消费者三者通过TCP长连接的方式进行通信,服务粒度是方法级的。
-
两者都是RPC框架的一种实现,实际项目中任选一种即可。
踩坑记录
在准备演示OpenFeign时出现了一点小插曲,启动consumer时,出现了如下报错。
根据这个提示信息可以看出来似乎是版本问题,于是我打开了Idea依赖检查的一个工具Diagrams->Show Dependencies…
通过这个依赖图可以很清晰的看到我们项目所依赖的包,图中红色的部分就是冲突的地方。最后经过一番排查,原来是父项目中没有引入spring-cloud-dependencies这个依赖导致的,加上此依赖后问题解决
其实最后这部分主是想介绍idea的这个图形化工具,我觉得还挺实用的,可以通过这个工具检查一下项目中的依赖,不需要的依赖建议尽量移除。
代码已上传至github:https://github.com/hechaoqi123/SpringCloudAlibabaDemo_2
针对Dubbo作为RPC产生的疑问?
按照目前我理解的逻辑,当采用Dubbo作为RPC框架时,服务应该分为api、provider、consumer这三种角色。
api模块的职责是管理provider与consumer之间约定的接口。
provider模块是api模块的一种实现,提供基础的数据访问。(这里我感觉有点类似于垂直架构中的持久层)
consumer模块的职责是调用所依赖的服务,完成业务逻辑的组装,并对外提供接口。(类似于垂直架构中的业务逻辑处理层+控制层)
所以如果严格按照这个规范划分的话,那么我会得出如下的结论
- consumer只需要做逻辑组装,所以是不需要数据库的。(可以加缓存层,提高RT)
- provider只需要提供基础的数据访问,不需要对外暴露http接口。
- consumer之间应该不允许横向调用。(避免系统耦合)
- provider可以依赖provider提高代码复用性(但是不应该双向依赖)
但是据我了解,许多Dubbo项目中,通常会存在一个服务即是provider又是comsumer,我有些不明白为什么要这样做?