微服务概念与演进
微服务概念与术语
服务器、服务(应用)、微服务(模块)、节点(服务或者服务器)
垂直扩展(增强硬件性能)、水平扩展(增加服务器或者程序实例来分散负载)
容错率(允许服务集群错误出现的范围与概率)、宕机(服务挂了)
高内聚低耦合(内聚:程序功能独立,耦合:程序交互)
流量(访问量)、服务依赖(项目、程序间的相互调用)
**单体架构:**一个系统业务量很小,所有代码放在一个项目,所有应用服务在同一机器(应用、Mysql、redis)。(开发部署简单,但存在单点故障问题)
**分布式:**拆分服务(应用、MySQL、reids)独立部署。
**集群:**解决单点故障,多部署几个通过负载均衡设施(Nginx、MyCat、Eureka等)来分散请求。
系统架构演进
单体应用架构 --> 垂直应用架构 --> 分布式架构 --> SOA架构 --> 微服务架构 -> 云原生 --> 。。。
单体应用架构
优点:架构简单,部署维护方便,运维成本低
缺点:不易于开发维护,模块耦合单点容错率低,无法针对模块进行性能优化和水平扩展
垂直应用架构
优点:系统拆分实现流量分担,解决并发问题。可以针对不同模块进行优化和水平扩展。一个系统不会影响其他系统,提高容错率。
缺点:系统相互独立,无法相互调用。存在重复开发的任务。
分布式架构
优点:抽取公共的功能为服务层,提高代码复用性。
缺点:系统耦合度变高,调用关系复杂,难以维护。
SOA架构(SOA:面向服务)
优点:使用注册中心解决了服务之间的服务之间调用关系的自动调节。
缺点:服务之间相互依赖,一旦某个环节出错,可能导致服务雪崩。服务关系复杂,运维测试部署成本高。
微服务架构
在某种程度上来说,微服务架构是SOA架构的进一步演进,强调服务的彻底拆分。
优点:服务原子化拆分,独立打包部署升级,保证每个微服务清洗的任务划分,利于扩展。微服务之间采用rest/rpc轻量级http协议相互调用。不受语言、人员限制。
缺点:分布式系统开发技术成本高(容器、分布式事务等)
微服务架构介绍
SpringCloud简介
一些列框架的集合。利用SpringBoot简化了分布式系统基础设施的开发。eg:服务注册发现、配置中心、消息总线、负载均衡、断路器、链路监控、数据监控。
SpringBoot和SpringCloud有啥关系?
SpringBoot专注于快速方便的开发单个单体应用服务。
SpringCloud关注全局的微服务协调治理,将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供:配置管理、服务注册发现、断路器、路由、事件总线、分布式事务等集成服务。
微服务环境搭建
本文以电商商品、订单模块为例子。
商品服务(提供列表查询、ID查询接口),订单服务(提供创建订单接口)。
服务调用:在微服务架构中最常见的场景是服务之间的相互调用。一般将调用方成为消费者,被调用方成为生产者(提供者)。
模块设计:
- cloud2-parent
- cloud2-product-api
- cloud2-product-server
- cloud2-order-api
- cloud2-order-server
创建Maven父工程cloud2_parent并导入依赖
<?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>
<packaging>pom</packaging>
<modules>
<module>cloud2-product-api</module>
<module>cloud2-product-server</module>
<module>cloud2-order-api</module>
<module>cloud2-order-server</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.hx</groupId>
<artifactId>cloud2_parent</artifactId>
<version>1.0</version>
<name>cloud2_parent</name>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR12</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.3.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<!-- 加上下面俩行,防止错误:Cannot resolve org.springframework.cloud:spring-cloud-dependencies:Hoxton.SR8 -->
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
创建product服务
创建cloud2-product-api项目,并导入依赖
<?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>
<parent>
<artifactId>cloud2_parent</artifactId>
<groupId>com.hx</groupId>
<version>1.0</version>
<relativePath/>
</parent>
<groupId>com.hx</groupId>
<artifactId>cloud2-product-api</artifactId>
<version>1.0</version>
<name>cloud2-product-api</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
</project>
编写实体类Product
@Data
@Accessors(chain = true)
// 微服务必须实现序列化接口
public class Product implements Serializable {
private String id;
private String name = "product";
private String createTime;
private String app = "product";
}
创建cloud2-product-server项目,并引入依赖
<?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">
<parent>
<artifactId>cloud2_parent</artifactId>
<groupId>com.hx</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-product-server</artifactId>
<name>cloud-product-server</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.hx</groupId>
<artifactId>cloud2-product-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
编写配置文件
server:
port: 8881
spring:
application:
name: cloud2-product-server
编写服务层和控制层代码(这里展示核心代码)
@Service
public class ProductServiceImpl implements ProductService {
@Override
public Product getById(String pid) {
return new Product().setId(pid)
.setCreateTime(SimpleDateFormat.getDateTimeInstance().format(new Date()));
}
@Override
public List<Product> list() throws InterruptedException {
List<Product> result = new ArrayList<>();
for (int i = 0; i < 15; i++) {
Thread.sleep(100);
result.add(new Product().setId(UUID.randomUUID().toString())
.setCreateTime(SimpleDateFormat.getDateTimeInstance().format(new Date())));
}
return result;
}
}
@RestController
@RequestMapping("prd")
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/{pid}")
public Product getById(@PathVariable("pid") String pid) {
return productService.getById(pid);
}
@RequestMapping("/list")
public List<Product> list() throws InterruptedException {
return productService.list();
}
}
创建Order服务
创建cloud2-order-api,并导入依赖
<?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">
<parent>
<artifactId>cloud2_parent</artifactId>
<groupId>com.hx</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud2-order-api</artifactId>
<name>cloud2-order-api</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
</project>
创建实体类Order
@Data
@Accessors(chain = true)
public class Order implements Serializable {
private String id;
private String name = "order";
private String createTime;
private String app = "order";
private String pid;
private String num;
private Object product;
}
创建cloud2-order-server项目,并导入依赖
<?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">
<parent>
<artifactId>cloud2_parent</artifactId>
<groupId>com.hx</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud2-order-server</artifactId>
<name>cloud2-order-server</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.hx</groupId>
<artifactId>cloud2-order-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>com.hx</groupId>
<artifactId>cloud2-product-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
编写配置文件
server:
port: 8882
spring:
application:
name: cloud2-order-server
编写service和controller代码(这里只展示核心代码)
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private RestTemplate restTemplate;
// @Autowired
// private DiscoveryClient discoveryClient;
@Override
public Order save(String pid, String num) {
System.out.println(" 正在创建订单");
System.out.println("订单商品编号:" + pid);
System.out.println("订单商品数量:" + num);
Order order = new Order().setId(UUID.randomUUID().toString())
.setPid(pid)
.setNum(num)
.setCreateTime(SimpleDateFormat.getDateTimeInstance().format(new Date()));
// 方案一:rest发起远程调用获取商品信息
Product product = this.restForProduct(pid);
order.setProduct(product);
return order;
}
private Product restForProduct(String pid) {
String uri = "http://localhost:8881/prd/" + pid;
Product product = restTemplate.getForObject(uri, Product.class);
return product;
}
}
@RestController
@RequestMapping("od")
public class OrderController {
@Autowired
private OrderService orderService;
// http://localhost:8882/od/save?pid=1&num=20
@RequestMapping("/save")
public Order save(String pid, String num) {
return orderService.save(pid, num);
}
}
服务治理Nacos Discovery
概念简介
什么是服务治理
服务治理是微服务架构中最核心的模块。用于实现各个微服务的自动注册与发现。对各种微服务IP/端口的同一管理。
**服务注册:**保存服务提供者和服务调用者的信息
**服务发现:**服务调用者订阅发现服务提供者信息
健康检查:检测服务提供者的健康状况,如果异常及时剔除
常见的注册中心
Zookeeper: CP原则。解决分布式应用中经常遇到的数据管理问题。eg:同一命名服务、状态同步服务、集群管理、分布式应用配置项的管理
Eureka: SpringCloudNetflix重要组件,提供服务注册与发现。
Consul: 基于Go开发的面向分布式,服务化的系统提供服务注册、发现和配置管理功能。包括:服务注册与发现、健康检查、Key/Value存储、多数据中心和分布式一致性保证等特性。本身是一个二进制可执行文件,安装部署简单。
Nacos: 易于构建云原生应用的动态服务发现、配置管理和服务管理平台。是SpringCloudAlibaba组件之一,复杂服务注册与发现、服务配置。
Nacos核心功能:
- 服务注册: NacosClient通过发送rest请求像NacosServer来注册服务,提供自身元数据(ip、port)信息。nacos server接收到注册请求后,就会把元数据存储到一个双层Map中。
- 服务心跳: 在服务注册后,nacos client会维护一个定时心跳来维持通知nacos server,说明服务一直处于可用状态,防止被剔除。默认间隔5s。
- 服务同步: nacos server集群中间会相互同步服务实例,用来保证服务信息的一致性。
- 服务发现: 服务消费者nacos client在调用服务提供的服务时,会发送与给rest请求给nacos server来获取注册的服务清单,并且缓存在nacos client本地,同时会在nacos client本地开启一个定时任务拉去服务最新的注册列表信息更新到本地缓存。
- 健康检查: nacos server会开启一个定时任务来检查注册服务实例的健康情况。对于超过15s(三次间隔时间)没有收到客户端心跳的实例会将他的healthy属性设置为false(此时客户端服务发现时,不会被发现),如果某个实例超过30s(6次间隔时间)没有收到心跳,直接剔除该实例(被剔除的实例如果恢复心跳发送,则重新注册)。
搭建Nacos环境
快速入门官方文档:https://nacos.io/zh-cn/docs/quick-start.html
安装nacos
- 下载地址:https://github.com/alibaba/nacos/releases
- 下载nacos-server-2.2.1.zip:https://github.com/alibaba/nacos/releases/download/2.2.1/nacos-server-2.2.1.zip
启动nacos
# 启动命令(standalone代表着单机模式运行,非集群模式):
startup.cmd -m standalone
# 执行或双击
shutdown.cmd
访问nacos
http://localhost:8848/nacos
即可访问服务,密码nacos/nacos
服务治理Nacos的使用
将商品服务注册到nacos
引入nacos依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${spring-cloud-alibaba.version}</version>
</dependency>
在启动类上添加@EnableDiscoveryClient注解
@SpringBootApplication
@RestController
@EnableDiscoveryClient
public class Cloud2ProdApp {
public static void main(String[] args) {
SpringApplication.run(Cloud2ProdApp.class, args);
}
@RequestMapping("/")
public String index() {
return "Cloud2ProdApp";
}
}
修改配置文件
server:
port: 8881
spring:
application:
name: cloud2-product-server
cloud:
nacos:
discovery:
server-addr: localhost:8848
获取服务信息,以及DiscoveryClient使用,修改orderService
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@Override
public Order save(String pid, String num) {
System.out.println("正在创建订单");
System.out.println("订单商品编号:" + pid);
System.out.println("订单商品数量:" + num);
Order order = new Order().setId(UUID.randomUUID().toString())
.setPid(pid)
.setNum(num)
.setCreateTime(SimpleDateFormat.getDateTimeInstance().format(new Date()));
// 方案二:nacos的discoveryClient
Product product = this.restByDiscoveryClient(pid);
order.setProduct(product);
return order;
}
private Product restByDiscoveryClient(String pid) {
List<ServiceInstance> instances = discoveryClient.getInstances("cloud2-product-server");
ServiceInstance instance = instances.get(0);
String uri = "http://" + instance.getHost() + ":" + instance.getPort() + "/prd/" + pid;
Product product = restTemplate.getForObject(uri, Product.class);
return product;
}
}