SpringCloud学习笔记——微服务简介
一、从单体架构到微服务架构的演进
1、单体架构
我们开发一个一个的Web项目,可以使用Spring、SpringMVC、Mybatis等技术。项目结构如下图1-1所示。
整个系统的架构非常简单,使用Spring+SpringMVC+Mybatis构建一个基础工程、MySQL数据库作为持久化存储,在这个工程中创建不同的Service实现项目中不同的业务场景,最后把项目构建成一个war包部署在Tomcat容器上即可使用,这时我们之前经常采用的架构。
通常来说,如果一个war包或者jar包里面包含一个应用的所有功能,则我们称这种架构为单体架构。
很多传统互联网公司或者创业型公司早期基本都会采用这样的架构,因为这样的架构足够简单,能够快速开发和上线。而且对于项目初期用户量不大的情况,这样的架构足以支撑业务的正常运行。
2、垂直和集群化
以商城系统为例。随着业务的发展,产品被越来越多的人使用,那么对于整个技术架构来说,可能会面临以下挑战:
- 用户量越来越大,网络访问量不断增大,导致后端服务器的负载越来越高。
- 用户量大了,产品需要满足不同用户的需求来留住用户,使得业务场景越来越多并且越来越复杂。
- 当服务器的负载越来越高的时候,如果不进行任何处理,用户在网站上操作的响应会越来越慢,甚至出现无法访问的情况,对于非常注重用户体验的互联网产品来说,这是无法容忍的。
- 业务的场景越多越复杂,意味着war包中的代码量会持续上升,并且各个业务代码之间的耦合度也会越来越高,后期的代码维护和版本发布涉及的测试和上线,也会很困那。举个最简单的例子,当你需要在库存模块里面添加一个方法时,带来的影响是需要把整个系统重新测试和部署,而当一个war包有几GB的大小时,部署的过程也是相当痛苦的。
因此,我们可以从两个方面进行优化:
1)通过横向增加服务器,把单台机器变成多台机器的集群。
2)按照业务的垂直领域进行拆分,减少业务的耦合度,以及降低单个war包带来的伸缩性困难问题。
如图1-2所示,我们把商城系统按照业务维度进行了垂直拆分:用户子系统、库存子系统、商品子系统,每个子系统由不同的业务团队负责维护并且独立部署。同时,我们针对Tomcat服务器进行了集群部署,也就是把多台Tomcat服务器通过网络进行连接组合,形成一个整体对外提供服务。这样做的好处是能够在改变应用本身的前提下,通过增加服务器来进行水平扩容从而提升整个系统的吞吐量。
需要注意的是,图1-2中针对数据库进行了垂直分库,主要是考虑到Tomcat服务器能够承载的流量大了之后,如果流量都传导到数据库上,会给数据库造成比较大的压力。
总体来说,数据库层面的拆分思想和业务系统的拆分思想是一样的,都是采用分而治之的思想。
3、SOA
举例:
- 假设一个用户执行下单操作,系统的处理逻辑是先去库存子系统检杳商品的库存,只有在库存足够的情况下才会提交订单,那么这个检查库存的逻辑是放在订单子系统中还是库存子系统呢?在整个系统中,一定会存在非常多类似的共享业务的场景,这些业务场景的逻辑肯定会被重复创建,从而产生非常多冗余的业务代码,这些冗余代码的维护成本会随着时间的推移越来越高,能不能够把这些共享业务逻辑抽离出来形成可重用的服务呢?
- 在一个集团公司下有很多子公司,每个子公司都有自己的业务模式和信息沉淀,各个子公司之间不进行交互和共享。这个时候每个子公司虽然能够创造一定的价值,但是由于各个子公司之间信息不是互联互通的,彼此之间形成了信息孤岛,使得价值无法最大化。
基于这些问题,引入了SOA (Service-Oriented Architecture ),也就是面向服务的架构,从语义上说,它和面向过程、面向对象、面向组件的思想是一样的,都是一种软件组建及开发的方式。核心目标是把一些通用的、会被多个上层服务调用的共享业务提取成独立的基础服务。这些被提取出来的共享服务相对来说比较独立,并且可以重用。所以在SOA中,服务是最核心的抽象手段,业务被划分为一些粗粒度的业务服务和业务流程。
如图1-3所示,提取出了用户服务、库存服务、商品服务等多个共享服务。在SOA中,会采用ESB (企业服务总线)来作为系统和服务之间的通信桥梁,ESB本身还提供服务地址的管理、不同系统之间的协议转化和数据格式转化等。调用端不需要关心目标服务的位置,从而使得服务之间的交互是动态的,这样做的好处是实现了服务的调用者和服务的提供者之间的高度解耦。总的来说,SOA主要解决的问题是:
- 信息孤岛
- 业务重用问题
4、微服务架构
业务系统实施服务化改造之后,原本共享的业务被拆分形成可复用的服务,可以在最大程度上避免共享业务的重复建设、资源连接瓶颈瓶颈等问题。那么被拆分出来的服务是否也需要以业务功能为维度来进行拆分和独立部署,以降低业务的耦合及提升容错性呢?
微服务就是这样一种解决方案,从名字上来看,面向服务(SOA)和微服务本质上都是服务化思想的一种体现。如果SOA是面向服务开发的思想的雏形,那么微服务就是针对可重用业务服务的更进一步优化,我们可以把SOA看成微服务的超集,一但服务规模扩大就意味着服务的构建、发布、运维的复杂度也会成倍增加,所以实施微服务的前提是软件交付链路及基础设施的成熟化。他本质上是服务化思想的最佳实践方向。由于SOA和微服务两者的关注点不一样,造成了这两者有非常大的区别:
- SOA关注的是服务的重用性及解决信息孤岛问题。
- 微服务关注的是解耦,虽然解耦和可重用性从特定的角度来看是一样的,但本质上是有区别的,解耦是降低业务之间的耦合度,而重用性关注的是服务的复用。
- 微服务会更多地关注在DevOps的持续交付上,因为服务粒度细化之后使得开发运维变得更加重要,因此微服务与容器化技术的结合更加紧密。
如图1-4所示,将每个具体的业务服务构成可独立运行的微服务,每个微服务只关注某个特定的功能,服务之间采用轻量级通信机制REST API进行通信。细心的读者会发现SOA中的服务和微服务架构中的服务粒度是一样的,不是说SOA是微服务的超集吗?其实我们可以把用户服务拆分的更细,比如用户注册服务、用户鉴权服务等。实际上,微服务到底要拆分到多大的粒度没有统一的标准,更多的时候是需要在粒度和团队之间找平衡的,微服务的粒度越小,服务独立性带来的好处就越多,但是管理大量的微服务也会越复杂。
二、微服务简介
比如我们网上购物,首先应该去购物网站搜索商品,这个搜索功能就可以开发成一个微服务。我们也可以看到相关商品的推荐,这些推荐项也可以是一个微服务。后面比如加入购物车、下订单、支付等功能都可以开发成一个一个的独立运行的微服务。
微服务应具有如下特点:
- 微服务是一种架构风格。
- 微服务把一个应用拆分为一组小型服务。
- 微服务每个服务运行在自己的进程内,也就是可独立部署和升级。
- 微服务的服务之间使用轻量级HTTP交互,一般使用Json交换数据。
- 服务围绕业务功能拆分。
- 可以由全自动部署机制独立部署。
- 去中心化,服务自治。服务可以使用不同的语言、不同的存储技术 。
1、微服务框架功能
微服务具有以上的这些特点,那么作为一个微服务框架,比如Spring Cloud,应该具备一些什么功能呢?微服务框架的功能主要体现在以下几个方面。
- 注册中心:服务提供者和消费者,能够从注册中心注册和得到服务信息。
- 配置中心:在微服务架构中设计服务较多需要对于配置文件统一管理。
- 服务链路追踪:对于服务之间的负载调用,要能通过链路追踪,得到具体参与者,调用链路出现问题能够快速定位。
- 负载均衡:服务调用服务会采用一定的负载均衡策略,来保证服务的高可用。
- 服务容错:通过熔断、降级服务容错策略,对系统进行有效的保护,降级是在服务或依赖的服务异常时,返回保底数据,熔断是指依赖服务多次失效,则熔断器打开,不再尝试调用,直接返回降级信息。熔断后,定期探测依赖服务可用性,若恢复则恢复调用。
- 服务网关:用户请求过载时进行限流、排队、过载保护、黑白名单、异常用户过滤拦截等都可以通过服务网关实现。
- 服务发布与回滚:蓝绿部署、灰度、AB Test等发布策略,可快速回滚应用。
- 服务动态伸缩、容器化:根据服务负载情况,可快速手动或自动进行节点增加和减少。
2、Spring Cloud简介
Spring Cloud是Spring提供的微服务框架。它利用Spring Boot的开发特性简化了微服务开发的复杂性,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,这些工作都可以借助Spring Boot的开发风格做到一键启动和部署。
Spring Cloud的目标是通过一系列组件,帮助开发者迅速构件一个分布式系统,Spring Cloud 是通过包装其它公司产品来实现的,比如Spring Cloud整合了开源的Netflix很多产品。Spring Cloud提供了微服务治理的诸多组件,例如服务注册和发现、配置中心、熔断器、智能路由、微代理、控制总线、全局锁、分布式会话等。
Spring Cloud实现微服务的治理功能产品很多,下面简单介绍下Spring Cloud各个产品的作用,以及采用的原则,如图1-6所示
3、Spring Cloud版本
Spring Cloud 和Spring Boot版本对应关系如图1-7:
三、搭建微服务工程
接下来模拟一个微服务调用的场景,有两个微服务,一个订单微服务,一个支付微服务,订单微服务通过RestTepleate调用支付微服务,完成支付功能。
- 支付微服务工程:服务提供者
- 订单微服务工程:服务使用者。
Idea先创建空模块,再在空模块中添加新的模块
1、公共模块
使用maven创建公共工程“c_cloud_common”工程下,统一管理案例使用的实体类和工具类。
1)pom.xml
<?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>com.dapan</groupId>
<artifactId>a_cloud_common</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
</dependencies>
</project>
2)实体类Payment
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment {
/**
* 订单编号
*/
private Integer id;
/**
* 支付状态
*/
private String message;
}
2、支付模块
使用Spring Initializr,创建支付微服务工程a_cloud_payment,模拟支付。
1)pom.xml
<?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.5.9</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.dapan</groupId>
<artifactId>a_cloud_payment</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>a_cloud_payment</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--引入公共模块-->
<dependency>
<groupId>com.dapan</groupId>
<artifactId>c_cloud_common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2)application.yml
server:
port: 9001
3)PaymentController
@RestController
@RequestMapping("/payment")
public class PaymentController {
@GetMapping("/{id}")
public ResponseEntity<Payment> payment(@PathVariable("id") Integer id) {
Payment payment = new Payment(id, "支付成功");
return ResponseEntity.ok(payment);
}
}
4)启动测试
3、订单模块
创建模块同上,pom文件同上
1)启动器
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Spring提供了一个RestTemplate模板工具类,对基于Http的客户端进行了封装,并且实现了对象与json的序列化和反序列化,非常方便。RestTemplate并没有限定Http的客户端类型,而是进行了抽象,目前常用的3种都有支持:
- HttpClient
- OkHttp
- JDK原生的URLConnection(默认的)
2)application.yml
server:
port: 9002
3)OrderController
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/payment/{id}")
public ResponseEntity<Payment> getPaymentById(@PathVariable("id") Integer id) {
String url = "http://localhost:9001/payment/" + id;
Payment payment = restTemplate.getForObject(url, Payment.class);
return ResponseEntity.ok(payment);
}
}
4)启动测试
启动项目访问http://localhost:9002/order/payment/123,效果如图1-9所示。
至此,一个简单的微服务搭建完毕