dubo和springcloud可以单独用作生产中的微服务治理框架,但使用springcloud的学生可能知道,springcloud生态的相关组件近年来逐渐停止,导致服务架构演变过程中的迭代断层,一些新的技术组件难以引入,所以在国内市场上有升级版的springcloud-alibaba。
二、springcloud-与dubbo相比,aliba与dubo2.1 springcloud-alibaba 简述Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。该项目包括开发分布式应用微服务的必要组件,方便开发者通过 Spring Cloud 用于开发分布式应用服务的编程模型。
2.1.1 介绍主要技术组件springcloud-alibaba提供了丰富的服务治理技术组件,列出如下:
- Sentinel:从流量控制、熔断降级、系统负载保护等维度保护服务的稳定性来看,流量控制组件以流量为切入点;
- Nacos:集动态服务发现、分布式配置管理和服务管理平台,更容易构建云原生应用;
- RocketMQ:基于高可用分布式集群技术,开源分布式新闻系统提供低延迟、高可靠的新闻发布和订阅服务;
- Dubbo:Apache Dubbo™ 是高性能的 Java RPC 可单独作为服务治理框架的框架;
- Seata:阿里巴巴开源产品,易于使用的高性能微服务分布式事务解决方案;
- Alibaba Cloud ACM(收费等于Nacos):在分布式架构环境中集中管理和推送应用配置的应用配置中心产品;
- Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里巴巴云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用程序、任何时间、任何地点存储和访问任何类型的数据;
- Alibaba Cloud SchedulerX: (收费) 阿里中间件团队开发的分布式任务调度产品,基于秒级、精度、高可靠性、高可用性( Cron 表达式)任务调度服务;
- Alibaba Cloud SMS: 覆盖全球短信服务,友好、高效、智能的互联通信能力,帮助企业快速建立客户接触渠道;
利用这些组件的组合,可以更好地完成生产级项目的微服务改造。从技术演进的角度来看,这套技术组件也在逐渐适应云原生的步伐,不用担心未来集成云原生过程中无法解决的问题。
2.1.2 springcloud-alibaba使用场景Springcloud架构升级改造
由于springcloud生态下的一些组件很长一段时间没有更新,对于以前使用本地springcloud但需要进一步优化、调整和扩展架构的场景。
流控治理更加完善
springcloud-alibaba中的sentinel在流控方面更为出色。虽然springcloud中也有hystrix用于服务降级和流控的功能,但功能仍然相对单一。
集中管理服务与配置
springcloud-alibaba中的nacos集服务和配置管理于一体,提供更友好、更方便的可视化界面,便于开发、运维和使用,可以降低部署和运维成本,而原始springcloud则需要同时使用eureka和引入其他配置管理组件。
容器化部署和云原生支持
强大的技术团队背书,阿里云对云原生技术的支持与融合,Spring Cloud Alibaba 支持 Kubernetes、Docker 等容器化技术,部署方便。
调用跨应用接口
2.2 dubbo 简述springcloud主要通过htp接口实现微服务之间的相互调用,可以考虑不同应用跨机器甚至跨网络部署的场景。
Dubo是阿里巴巴开源的分布式服务治理框架,其使用简单、高效、方便,受到多架构师的青睐,底层采用RPC呼叫远程服务。
dubo提供了三个核心能力:
- 调用面向接口的远程方法;
- 智能容错和负载平衡;
- 以及自动注册和发现服务;
下图为dubbo模型架构图,不难看出,该架构主要包括四个部分:
- Registry,服务注册中心;
- Provider,服务提供商、外部或其他应用程序提供服务界面;
- Consumer,服务消费者,调用Provider提供的服务,如本地调用;
- Monitor,一般很少使用服务监控;
虽然dubo也可以作为微服务治理的框架单独拿出来,但在很多公司中也是广泛实用的。与springcloud-alibaba相比,大多数场景可以相互替代。以下是dubo使用场景的参考总结;
微服务架构改造
在微服务架构下,服务的拆分非常详细,服务需要相互呼叫。使用Dubo可以很容易地实现微服务之间的通信。对于服务消费者来说,只需要引入供应商的SDK,就像当地呼叫一样方便;
分布式系统
在分布式系统中,每个微服务模块都需要相互合作来处理任务。此时,可以考虑使用Dubo作为服务调用的中间部件
内部系统架构
在平台级内部应用之间相互调用服务时,使用dubo比springcloud传输效率更高
并发性高,流量场景大
nettty用于dubbo的底层。与http相比,传输效率在一定程度上更好。在高并发性下,dubo的优势更加明显
更加注重接口级API治理
三、服务改造难题3.1 技术选型难题dubbo提供了完整的服务治理技术,提供了一套完整的服务发现、负载平衡、容错机制、线程调整、通信协议等解决方案,对API层面的治理更加友好和精细
对于一个稍微大一点的公司级产品,技术架构当然不是一成不变的,但有一点我相信很多合作伙伴还是可以达成共识的,那就是对于大多数中小企业的产品来说,其技术架构很难在很长一段时间内做出很大的调整,而技术架构的选择是在一开始之后基本定型了。为什么这么说?
3.1.1 技术负责人的技术风格3.1.2 业务快速增长难以定性可以说,很多团队技术负责人的技术栈的广度和深度在很大程度上决定了一个产品的结构基调。有的负责人对dubo技术比较深入,一开始可能会选择使用dubo作为服务治理框架。如果熟悉springcloud,可能会更喜欢springcloud。;
3.1.3 整合微服务框架难题近年来,DDD的概念在市场上非常流行。事实上,真正推动DDD着陆仍然是相当昂贵的。这就要求公司的业务模式相对稳定、清晰、可控。如果一家公司的业务正在迅速扩张,它将充满更多的不确定性。不确定性带来的直接问题是,技术架构需要在选择上合理权衡,既不能太复杂,但也需要留出一定的可扩展性,为后续架构的扩展留出空间。由于业务不确定性,很难说采用微服务框架是服务治理的最佳模式;
许多学生一定会遇到这样的场景,比如你的springboot版本是1.5.X,现在需要升级到2.X版本似乎只是jar包版本的升级问题,但对于有升级经验的学生来说,这个升级过程可以与西方学习相媲美。更不用说痛苦了,很多坑都要填满,最后深入到代码层面进行调整...
框架层面的调整也很常见。例如,您的微服务治理框架使用springcloud,但发现配置中心的管理和使用git非常不方便,配置托管在外部网络上总是感到不安全,需要收回,或者您需要对整个平台的每个微服务进行统一的可视化流程控制,如果你认为hystrix在springcloud中不容易使用,你可能会想到引入其他第三方流控组件...
以上情况是,越来越多的第三方技术组件被引入到整个产品中,维护成本越来越高,越来越多的人力被投入到部署和运维中。更重要的是,一旦出现问题,解决问题的效率非常低。原因是原来的springcloud框架本身在微服务治理系统下,开始无法应对日益复杂的服务治理需求,所以很多人开始考虑springcloud-alibaba进行整合,实现架构调整。
事实上,虽然springcloud-alibaba也完美地支持springcloud,并进行了大量的升级,但从架构调整的角度来看,这也是一项大量的改造工作,主要问题集中在以下几点:
顶级pom依赖不兼容
springcloudspringcloud-毫不夸张地说,alibaba的根pom适配版本是不同的,springcloud-alibaba几乎不可能与springbott相匹配 1.5.X系列版一起工作;
内部部件需要调整和兼容
springcloud的注册中心是eureka,springcloud-alibaba的配置管理主要推广nacos。如何将eureka调整为使用nacos?还是直接在springcloud-alibaba中使用nacos?
调整部署模式
springcloud-阿里巴巴技术团队认可了alibaba,其部署模式在适应容器化和云本土化方面具有天然优势,而springcloud在这方面失去了竞争力,但同时也对技术团队从开发到运维提出了更高的要求;
团队成员的学习成本
3.2dubo与springcloud的融合难题相对而言,springloud-alibaba涉及的技术栈比较全面,如果以后考虑和云原生融合,学习成本甚至会更高,这就要求开发人员掌握更多的新技能,这是必须考虑的。
随着dubo在微服务治理中的优势越来越明显,springcloud-alibaba的治理系统也煞费苦心地吃掉一切。举个实际场景,如果你的团队现在使用springcloud-alibaba的框架,那么服务之间的RPC调用自然离不开ribon或openfeign;
以openfeign为例,对于服务消费者来说,使用起来非常方便,也非常方便,调用远程服务就像调用本地服务一样简单,但是,从单个API服务的治理和控制来看,openfeign和ribon都不是很友好,即使是治理,也需要使用外部手段,如agent,或编码,或者其他外部组件进行服务监控,更致命的是,如果都是内部服务,在高并发场景下调用openfeign,这个性能问题会暴露出来;
因此,架构师或技术负责人认为,如果他们能使用dubo,毕竟,dubo在API级服务治理方面仍然非常出色,并提供了一套完整的治理方案。但直接杀死openfeign并更换dubo是不现实的。如果有很多微服务模块,那就不仅仅是一两个应用程序的转换。
因此,进一步思考是否可以在现有的基础上引入dubo,即同时支持openfeign和dubo的调用?这种考虑似乎是可行的,也是一个相对合理的解决方案。事实上,许多产品也遵循这一理念。为了杀死旧技术,引入新技术,然后让新旧技术共存一段时间,然后逐渐废弃旧技术。
以上场景和解决方案为背景,以下是springcloud的实际操作案例演示-alibaba 整合dubbo和openfeign的完成过程。
四、springcloud-alibaba整合openfeign4.1 前置准备为便于以后的测试,提前创建数据库并创建测试数据表,数据库名称:dbuser01,建表sql如下:
CREATE TABLE `user` ( `id` varchar(32) NOT NULL, `user_name` varchar(32) NOT NULL, `pass_word` varchar(32) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERT INTO `dbuser01`.`user`(`id`, `user_name`, `pass_word`) VALUES ('001', 'jerry', '123456');INSERT INTO `dbuser01`.`user`(`id`, `user_name`, `pass_word`) VALUES ('002', 'mike', '123456');INSERT INTO `dbuser01`.`user`(`id`, `user_name`, `pass_word`) VALUES ('003', 'john', '123456');
4.2操作步骤4.2操作步骤.1 工程目录结构完整的工程目录结构如下
4.2.以下依赖于父模块的导入基本的全局版本号依赖在根pom中定义,主要包括:springcloud,springcloud-alingcloududaba等版本,其他版本可以在特定模块中添加。此外,还需要注意springcloud 与springcloud-alibaba和springbot的版本兼容性问题,可参考相关资料,网上有很多。
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.9.RELEASE</version> <relativePath/> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR10</spring-cloud.version> <mysql.version>5.1.47</mysql.version> <mybatis.version>2.1.1</mybatis.version> <dubbo.version>2.7.18</dubbo.version> </properties> <dependencyManagement> <dependencies> <!-- springCloud --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.2.6.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
4.2.服务提供商的核心依赖于服务提供商主要包括:mysql连接,mybatis,dubo的依赖可以添加到nacos服务注册中心和openfeign;
<dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.congge</groupId> <artifactId>biz-common</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--nacos配置管理依赖于nacos配置管理--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- Dubbo Spring Cloud Starter --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-dubbo</artifactId> </dependency> </dependencies>
4.2.公共模块定义实体类和服务界面在公共模块中定义实体类,并与数据库user映射。请注意,必须在这里实现序列化,以避免在以后调用dubo接口时出现序列化错误;
@Datapublic class User implements Serializable { private static final long serialVersionUID = -2896873555774275129L; private String id; private String userName; private String passWord;}
Userduboapiserviceer定义服务界面
public interface UserDubboApiService { User getById(String id);}
4.2.4服务提供商的核心代码提供商最后,通过mvninstall命令将common模块安装到本地仓库,然后引用到其他两个模块中
核心配置文件
server: port: 8081spring: application: name: biz-provider-service cloud: nacos: discovery: server-addr: localhost:8848 datasource: url: jdbc:mysql://IP:3306/dbuser01useUnicode=true&characterEncoding=utf-8 username: 用户名 password: 密码 driver-class-name: com.mysql.jdbc.Driver#mybatis相关配置mybatistis: type-aliases-package: com.congge.entity configuration: map-underscore-to-camel-case: true mapper-locations: classpath:mybatis/*.xml
为测试添加一个服务接口
@RestController@RequestMapping("/user")public class UserController { @Autowired private UserService userService; //http://localhost:8081/user/getById?id=001 @GetMapping("/getById") public Object getByUserId(@RequestParam("id") String id){ /*User user = new User(); user.setId("001"); user.setUserName("jerry"); user.setPassWord("123456"); return user;*/ return userService.getById(id); }}
服务实现
import com.congge.entity.User;import com.congge.mapper.UserMapper;import com.congge.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public User getById(String id) { return userMapper.getById(id); }}
服务启动类
@SpringBootApplication@EnableFeignClients@MapperScan("com.congge.mapper")public class BizProviderApp { public static void main(String[] args) { SpringApplication.run(BizProviderApp.class,args); }}
4.2.5服务提供方接口测试nacos被用作服务注册中心,所以在启动服务之前,需要启动nacos
启动提供商的服务,然后浏览器调用测试接口,看到以下效果说明整合完成;
4.2.6服务消费者核心代码工程目录结构,依赖于与生产方相同的结构,不再重复
添加Feign接口类
import com.congge.entity.User;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import java.util.Map;@FeignClient(name = "biz-provider-service",path = "/user")public interface UserFeignService { @GetMapping("/getById") public User get(@RequestParam("id") String id);}
为了方便省略服务实现类,建议在实际开发中添加测试接口。
@RestControllerpublic class BizMockController { @Autowired private UserFeignService userFeignService; //localhost:8082/getFromRemote @GetMapping("/getFromRemote") public Object getFromRemote(){ return userFeignService.get("001"); }}
核心配置文件主要是在nacos注册一个项目的服务名称;
server: port: 8082spring: application: name: biz-consumer-service cloud: nacos: discovery: server-addr: localhost:8848 #服务注册中心地址
服务启动类
@SpringBootApplication@EnableFeignClientspublic class BizConsumerApp { public static void main(String[] args) { SpringApplication.run(BizConsumerApp.class,args); }}
4.2.77服务消费者接口测试完成上述步骤后,启动消费者服务,通过上述测试接口调用,看到以下效果,表明消费者通过openfeign调用远程服务完成;
五、springcloud-alibaba整合openfeign,兼容dubobo回到第三章最后的原始需求,当项目需要适应结构调整时,引入dubo框架时该怎么办?改造的效果是,对于服务消费者来说,openfeign已经在使用,dubbo可以替换openfeign的调用,然后对上述两个模块代码进行一些调整;
5.1 服务提供商改造5.11.1 添加dubo依赖请注意,springcloud集成的dubo直接在这里引入,而不是单独引入dubo依赖,也可以单独引入,但需要注意版本匹配的问题(谨慎)
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-dubbo</artifactId> </dependency>
点击可以发现这里使用的是2.7.8.dubbo版本,在这个版本中,dubo在配置时不需要依赖zk,所以我没有引入zk或curator相关依赖;
5.1.增加dubbo相关配置dubbo的配置只添加了基本的配置信息。我相信使用过它的学生并不陌生,主要是dubo协议、端口和dubo扫描的业务实现包路径;
dubbo: protocol: name: dubbo port: 20881 scan: base-packages: com.congge.service.impl.dubbo
5.1.3代码改造引入dubbo后,为了方便包目录的清晰开发,降低后续维护成本,对项目目录结构进行了如下调整:
对目录结构做以下补充说明,也是生产中的一种实践经验:
- dubbo的实现类建议与项目本身的业务实现类分开;
- 本项目的业务实现类主要为当前项目编制自己的业务;
- dubbo业务实现类单独提供服务实现,供其他调用方使用;
- 为了降低编码的冗余和维护成本,建议根据两者相同或高度重用的逻辑单独拿出一个包目录。在目录下编写的一般业务逻辑被两个实现类别引用;
添加dubo实现类,这里的userduboapiservice是公共模块中定义的服务接口;
import com.congge.entity.User;import com.congge.service.UserDubboApiService;import com.congge.service.UserService;import org.apache.dubbo.config.annotation.DubboService;import org.springframework.beans.factory.annotation.Autowired;@DubboServicepublic class UserDubboApiServiceImpl implements UserDubboApiService { @Autowired private UserService userService; @Override public User getById(String id) { User dbUser = userService.getById(id); return dbUser; }}
服务提供商的代码在这里完成了改造。接下来,我们来看看消费者的改造
5.2服务消费者改造一般步骤与生产方相似,引入dubo依赖与上述步骤相同
5.2.1 配置文件改造补充dubbo配置,添加以下段落
dubbo: protocol: name: dubbo port: 20882
5.2.2.添加测试接口通过dubbo接口调用添加测试接口
@RestControllerpublic class BizMockController { @Autowired private UserFeignService userFeignService; //localhost:8082/getFromRemote @GetMapping("/getFromRemote") public Object getFromRemote(){ return userFeignService.get("001"); } @DubboReference(retries = 0, timeout = 10000) private UserDubboApiService userDubboApiService; //localhost:8082/getFromDubbo @GetMapping("/getFromDubbo") public Object getFromDubbo(){ return userDubboApiService.getById("002"); }}
5.2.3接口测试启动生产者和消费者的服务,调用上述新界面,通过dubo成功调用远程服务,看到以下效果说明;
综上所述,从单独使用openfeign到同时兼容dubo的效果都是通过上述改造完成的。当然,还有两点需要重新强调:
六、写在文末1)版本兼容性问题,这也是在实际引入新的外部组件时,与当前工程的springboot版本兼容性最容易踩坑的地方;
2)引入dubo后,如何更优雅地管理本项目自身的业务实现和dubo的业务实现;
本文从生产实践中可能遇到的技术结构调整问题,通过实际操作,详细说明了如何在openfeign中引入dubo,使当前的服务提供商与这两种服务呼叫模式兼容。事实上,在实际项目中,影响的外部因素可能比这更复杂,但它可以作为提供解决方案的参考。最后,谢谢你的观看。