1. 微服务架构
1.1 单体应用架构
功能集中、代码和数据中心化、一个发布包、部署后运行在同一进程的应用程序,称之为单体架构应用,也叫单块架构应用。
优点:
1:部署简单:由于是完整的结构体,可以直接部署在一个服务器上即可。
2:技术单一:项目不需要复杂的技术栈,往往一套熟悉的技术栈就可以完成开发。
缺点:
1:系统启动慢,一个进程包含了所有的业务逻辑,涉及到的启动模块过多,导致系统的启动、重启时间周期过长;
2:系统错误隔离性差、可用性差,任何一个模块的错误均可能造成整个系统的宕机;
3:可伸缩性差:系统的扩容只能只对这个应用进行扩容,无法结合业务模块的特点进行伸缩。
4: 线上问题修复周期长:任何一个线上问题修复需要对整个应用系统进行全面升级。
5: 跨语言程度差
6: 不利于安全管理,所有开发人员都拥有全量代码。
小型项目适合单体应用架构. 比如:OA办公系统。管理类的项目 仓库管理系统。
1.2 微服务应用
微服务架构论文: Microservices
译文: 微服务译文理解_微服务架构翻译_发了个版的博客-CSDN博客
简单来说,微服务架构风格[1]是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常用HTTP资源API)。这些服务围绕业务能力构建并且可通过全自动部署机制独立部署。这些服务共用一个最小型的集中式的管理,服务可用不同的语言开发,使用不同的数据存储技术。
微服务的特点:
1:微服务是一种==项目架构思想==(风格)
2:微服务架构是一系列小服务的组合(组件化与多服务)
3:任何一个微服务,都是一个独立的进程(独立开发、独立维护、独立部署)
4:轻量级通信http协议(跨语言,跨平台)
5:服务粒度(围绕业务功能拆分---模块拆分【系统管理服务】【日志服务】【焦虑测试】【抑郁测试系统】)
6:去中心化管理(去中心化"地治理技术、去中心化地管理数据)
1.3 微服务架构的优势
1.易于开发和维护
一个微服务只关注一个特定的业务功能,所以它的业务清晰、代码量较少。开发和维护单个微服务相对比较简单,整个应用是由若干个微服务构建而成,所以整个应用也会维持在可控状态。
2. 单个微服务启动较快
单个微服务代码量较少,所以启动会比较快。
3. 局部修改容易部署
单体应用只要有修改,就要重新部署整个应用,微服务解决了这样的问题。一般来说,对某个微服务进行修改,只需要重新部署这个服务即可。
4. 技术栈不受限
在微服务中,我们可以结合项目业务及团队的特点,合理地选择技术栈。
5. 按需伸缩
若某一个系统访问量大,只需要对其系统进行扩展。
1.4 微服务架构的挑战
1. 服务太多,导致服务间的依赖错综复杂,运维难度大
2. 微服务放大了分布式架构的系列问题
(1)分布式事务
(2)分布式锁怎么处理
(3)服务注册与发现
(4)依赖服务不稳定导致服务雪崩
3. 运维复杂度上升,部署数量多,监控进程多导致整体运维复杂度提升
springcloud可以来解决上面微服务面临的挑战
1.5 SpringCloud与微服务的关系
1. SpringCloud为微服务思想提供了完美的解决方案
2. SpringCloud是一系列框架的集合体(服务注册与发现【注册中心】、服务间远程调用、服务降级、服务熔断、服务限流、分布式事务等)
一般我们说springcloud其实指的是Springcloud-netfix,SpringCloud并不是造轮子,只是把Netfix公司的组件做二次开发。
1.6 SpringBoot和SpringCloud的关系
1. SpringBoot专注于快速方便的开发单个个体微服务。
2. SpringCloud是关注全局的微服务协调、整理、治理的框架,它将SpringBoot开发的单体整合并管理起来。
3. SpringBoot可以离开SpringCloud独立使用开发项目,但是SpringCloud离不开SpringBoot,属于依赖关系。
2. 创建微服务工程
2.1 创建父工程
(1)添加依赖
<?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.3.12.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.aaa</groupId> <artifactId>qy11-parent</artifactId> <version>0.0.1-SNAPSHOT</version> <name>qy11-parent</name> <description>qy11-parent</description> <!--父工程那么它的打包方式必须为pom打包--> <packaging>pom</packaging> <properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-cloud.version>Hoxton.SR8</spring-cloud.version> <spring-cloud-alibaba.version>2.2.3.RELEASE</spring-cloud-alibaba.version> </properties> <dependencyManagement> <!--管理了cloud版本--> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!--alibaba的版本--> <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> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </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> </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.2 公共模块
(1)依赖
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
</dependencies>
(2)实体类
product实体类
@Data
@TableName(value = "shop_product")
public class Product {
@TableId(value = "pid",type = IdType.AUTO)
private Integer pid;
private String pname;
private BigDecimal pprice;
private Integer stock;
}
order实体类
@Data
@TableName(value = "shop_order")
public class Order {
@TableId(value = "oid",type = IdType.AUTO)
private Integer oid;
private Integer uid;
private String username;
private Integer pid;
private String pname;
private BigDecimal pprice;
private Integer number;
}
2.3 product商品微服务
(1)依赖
<dependencies>
<dependency>
<groupId>com.aaa</groupId>
<artifactId>qy11-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
(2)配置文件
#端口号 server.port=8080 #数据源 spring.datasource.url=jdbc:mysql://localhost:3306/shop_product spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver #打印sql日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
(3)dao层
public interface ProductDao extends BaseMapper<Product> {
}
(4)主启动类
@SpringBootApplication
@MapperScan(basePackages = "com.xwh.product.dao")
public class Qy11ProductApplication {
public static void main(String[] args) {
SpringApplication.run(Qy11ProductApplication.class, args);
}
}
(5)service层
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductDao productDao;
@Override
public Product get(Integer pid) {
Product product = productDao.selectById(pid);
return product;
}
}
(6) controller层
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping("getById/{pid}")
public Product get(@PathVariable Integer pid){
Product product = productService.get(pid);
return product;
}
}
2.4 order微服务
(1)依赖
<dependency> <groupId>com.aaa</groupId> <artifactId>qy11-common</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency>
(2)配置文件
#端口号 server.port=8090 #数据源 spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/shop_order #日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
(3)dao层
public interface OrderDao extends BaseMapper<Order> {
}
(4)主启动类
@SpringBootApplication
@MapperScan(basePackages = "com.xwh.order.dao")
public class Qy11OrderApplication {
public static void main(String[] args) {
SpringApplication.run(Qy11OrderApplication.class, args);
}
}
(5)service层
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Override
public Integer save(Order order) {
return orderDao.insert(order);
}
}
(6)controller层
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private RestTemplate restTemplate;
@GetMapping("save")
public String save(Integer pid,Integer num){
Order order = new Order();
order.setUid(1);
order.setUsername("aaa");
order.setNumber(num);
Product product = restTemplate.getForObject("http://localhost:8080/product/getById/" + pid, Product.class);
if(product==null){
return "下单失败";
}
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
orderService.save(order);
return "下单成功";
}
}
3. 服务治理
3.1 服务治理概念
在了解服务治理概念前先思考一个问题?
通过上面的操作,我们已经可以实现微服务之间的调用。但是我们把服务提供者的网络地址
(ip,端口)等硬编码到了代码中,这种做法存在许多问题:
(1) 一旦服务提供者地址变化,就需要手工修改代码
(2)一旦是多个服务提供者,无法实现负载均衡功能
(3) 一旦服务变得越来越多,人工维护==调用关系==困难
那么应该怎么解决呢, 这时候就需要通过注册中心动态的实现服务治理。
什么是服务治理?
服务治理是微服务架构中最核心最基本的模块。用于实现各个微服务的自动化注册与发现。
服务注册:在服务治理框架中,都会构建一个*注册中心*,每个服务单元向注册中心登记自己提供服务的详细信息。并在注册中心形成一张服务的*清单*,服务注册中心需要以*心跳30s 90s*的方式去监测清单中 的服务是否可用,如果不可用,需要在服务清单中剔除不可用的服务。
服务发现:服务调用方向服务注册中心咨询服务,并获取*所有服务*的实例清单,实现对具体服务实例的访问。
3.2 常见的服务治理组件
eureka: 它是netflix公司提供的一款组件,这款组件已经停止更新。
nacos: 它是阿里巴巴提供的一款服务治理组件。
zookeeper: 它是apache公司提供的服务治理组件。
consul: 服务治理的组件
我们以nacos为例
3.3 安装nacos组件
Releases · alibaba/nacos · GitHub
账号和密码nacos/nacos
3.4 微服务注册到注册中心
(1)依赖
<!--nacos依赖-->
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>2.2.3.RELEASE</version> </dependency>
(2)配置文件指定注册中心的地址
#注册中心的地址
spring.cloud.nacos.discovery.server-addr=localhost:8848
(3)为微服务起名
#起名字 单词之间使用-划线
spring.application.name=product
(4)修改order微服务控制层
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("save")
public String save(Integer pid,Integer num){
Order order = new Order();
order.setUid(6);
order.setUsername("aaa");
order.setNumber(num);
List<ServiceInstance> instances = discoveryClient.getInstances("product");
ServiceInstance serviceInstance = instances.get(0);
String path = serviceInstance.getUri().toString();
Product product = restTemplate.getForObject(path+"/product/getById/" + pid, Product.class);
if(product==null){
return "下单失败";
}
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
orderService.save(order);
return "下单成功";
}
}
4. 负载均衡
4.1 什么是负载均衡
通俗的讲, 负载均衡就是将负载(工作任务,访问请求)进行分摊到多个操作单元(服务器,组件)上进行执行。
4.2 手动完成负载均衡
修改order微服务
package com.xwh.order.controller;
import com.xwh.order.sevice.OrderService;
import com.xwh.pojo.Order;
import com.xwh.pojo.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.Random;
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("save")
public String save(Integer pid,Integer num){
Order order = new Order();
order.setUid(6);
order.setUsername("aaa");
order.setNumber(num);
List<ServiceInstance> instances = discoveryClient.getInstances("product");
//随机负载
int i = new Random().nextInt(instances.size());
ServiceInstance serviceInstance = instances.get(i);
String path = serviceInstance.getUri().toString();
Product product = restTemplate.getForObject(path+"/product/getById/" + pid, Product.class);
if(product==null){
return "下单失败";
}
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
orderService.save(order);
return "下单成功";
}
}
4.3 使用ribbon完成负载均衡
思考: 上面的负载均衡,是不是自己写的代码。缺点: 代码比较复杂。 如果想改变复杂均衡的策略,必须都是修改order的代码。
4.3.1 什么是ribbon
基于Ribbon实现服务调用, 是通过拉取到的所有服务列表组成(服务名-请求路径的)映射关系。借助 RestTemplate 最终进行调用
4.3.2 如何使用ribbon
(1)在著启动分类中的RestTemplate类上加注解@LoadBalance
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
(2)修改controller层代码
@RestController
@RequestMapping("/order")
public class OrderController2 {
@Autowired
private OrderService orderService;
@Autowired
private RestTemplate restTemplate;
@GetMapping("save")
public String save(Integer pid,Integer num){
Order order = new Order();
order.setUid(7);
order.setUsername("bbb");
order.setNumber(num);
Product product = restTemplate.getForObject("http://product/product/getById/" + pid, Product.class);
if(product==null){
return "下单失败";
}
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
orderService.save(order);
return "下单成功";
}
}
ribbon默认采用的是轮询策略,那么是否可以修改ribbon的负载策略呢?
4.3.3 ribbon内置的负载均衡策略
4.3.4 如何改变ribbon的负载均衡策略
修改微服务的配置文件
#修改ribbon负载均衡策略 product.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
使用ribbon完成了从nacos自动拉取服务,并完成负载均衡功能。
5. 使用OpenFeign完成远程调用
上面服务之间的调用 使用的是RestTemplate. 使用Resttemplate远程调用,它不符合我们的编程习惯。Controller---Service---Dao 再Controller注入一个service对象,然后调用service中的方法,根据方法传递相应的参数以及接受方法的返回的结果。同样的结果我们可以通过OpenFeign组件来完成。
5.1 什么是openfeign
OpenFeign是Spring Cloud提供的一个声明式的伪Http客户端, 它使得调用远程服务就像调用本地服务一样简单, 只需要创建一个接口并添加一个注解即可。
Nacos很好的兼容了OpenFeign, OpenFeign负载均衡默认集成了 Ribbon, 所以在Nacos下使用OpenFegin默认就实现了负载均衡的效果。
总结: openfeign就是为了完成服务的远程调用,它就像调用本地方法一样。
5.2 如何使用openfeign
(1)依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
(2)定义接口并添加@FeignClient(value="服务提供者的名")注解
@FeignClient(value = "product")
public interface ProductFeign {
@GetMapping("/product/getById/{pid}")
public Product get(@PathVariable Integer pid);
}
(3)再主启动类开启openfeign注解驱动
(4)再使用的类中注入Openfeign注解
@RestController
@RequestMapping("/order")
public class OrderController3 {
@Autowired
private OrderService orderService;
@Autowired
private ProductFeign productFeign;
@GetMapping("save")
public String save(Integer pid,Integer num){
Order order = new Order();
order.setUid(8);
order.setUsername("ccc");
order.setNumber(num);
Product product = productFeign.get(pid);
if(product==null){
return "下单失败";
}
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
orderService.save(order);
return "下单成功";
}
}
6. Nacos注册中心集群的搭建
我们可以猜想一下,如果注册中心出现故障,导致微服务之间无法远程调用,为了提高注册中心的高可用我们可以搭建多台注册中心-------集群模式
我们要想搭建nacos集群模式,这些集群中的每台nacos服务,都必须连接同一个数据库。因为我们的nacos都放在同一台主机上,所以我们必须为每台nacos起不同的端口。
6.1 修改端口号 8849 8859 8851
/conf/application.properties
6.2 连接mysql数据库
/conf/application.properties
6.3 创建nacos数据库并导入相关的表结构和数据
把sql语句运行到自己的nacos数据库
6.4 修改集群配置文件 ![](https://i-blog.csdnimg.cn/blog_migrate/1a6f31a77801f867af3770241cd2f637.png)
定义自己集群机器的ip和端口号,必须关闭虚拟网卡
6.5 启动nacos主机
7. 网关
思考:
(1)如果某个微服务的ip和端口号发生改变,那么我们必须修改代码
(2)后台服务器可能存在很多个服务,难道前端必须记住每个服务器的ip和端口号?
(3)在每个微服务的访问前,需要验证当前用户是否登录
为了解决以上可能出现的问题,我们需要在所有微服务前搞一个代理服务器。前端自己记住代理服务器的地址,有代理服务器把请求转发给后面的微服务。
所谓的API网关,就是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服 务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控(黑白名单)、路由转发等等。 添加上API网关之后,系统的架构图变成了如下所示:
常见的代理组件由哪些?
(1)Kong基于Nginx+Lua[再nginx中可以编写代码]开发,性能高,稳定,有多个可用的插件(限流、鉴权等等)可以开箱即用。 问题: 只支持Http协议;二次开发,自由扩展困难;提供管理API,缺乏更易用的管控、配置方式。
(2)Zuul 1.0(慢 servlet 2.0 ) zuul2.0 没出来。
Netflix开源的网关,功能丰富,使用JAVA开发,易于二次开发 问题:缺乏管控,无法动态配
置;依赖组件较多;处理Http请求依赖的是Web容器,性能不如Nginx
(3)Spring Cloud Gateway
Spring公司为了替换Zuul而开发的网关服务,将在下面具体介绍。
注意:**SpringCloud alibaba技术栈中并没有提供自己的网关,我们可以采用Spring Cloud Gateway来做网关**
7.1 springcloud-gateway网关的简介
Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0 和 Project Reactor 等术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。它的目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控和限流。
优点:
(1)性能强劲:是第一代网关Zuul的1.6倍
(2)功能强大:内置了很多实用的功能,例如转发、监控、限流等
(3)设计优雅,容易扩展.
缺点:
gateway内置了服务器 netty服务器。千万不要在使用tomcat作为服务器。
7.2 如何使用gateway网关
7.2.1 创建一个网关微服务
7.2.2 引入gateway依赖
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
7.2.3 主启动类
@SpringBootApplication
public class GateWayApp {
public static void main(String[] args) {
SpringApplication.run(GateWayApp.class,args);
}
}
7.2.4 配置文件
#端口号
server:
port: 7000#服务名称
spring:
application:
name: gateway#配置路由
cloud:
gateway:
routes:
- id: qy11-product #唯一即可,如果没有默认UUID生成
order: 0 #优先级 值越小优先级越高
uri: http://localhost:8080 #真实要转发的微服务地址 http://localhost:8080/product/getById/1
predicates:
- Path=/product/** #断言:当满足该断言,则会转发到uri地址。 path:路径断言- id: qy11-order
uri: http://localhost:8090
predicates:
- Path=/order/**
7.3 增强版
上面我们在配置文件中写死了转发路径的地址, 前面我们已经分析过地址写死带来的问题,. 接下来我们从注册中心获取此地址
7.3.1 在微服务引入nacos依赖
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
7.3.2 修改配置文件
7.4 简约版
如果后面增加一个微服务-那么我们就得修改gateway的配置。 自动定位微服务
7.4.1 修改配置文件
7.5 gateway基本概念
路由(Route) 是 gateway 中最基本的组件之一,表示一个具体的路由信息载体。主要定义了下面的几个信息:
l id,路由标识符,区别于其他 Route。默认生成一个
l uri,路由指向的目的地 uri,即客户端请求最终被转发到的微服务。
l order,用于多个 Route 之间的排序,数值越小排序越靠前,匹配优先级越高。--不改
l predicate,断言的作用是进行条件判断,只有断言都返回真,才会真正的执行路由。
l filter,过滤器用于修改请求和响应信息。
7.6 gateway路由流程
7.7 gateway提供了那些断言
l *基于**Datetime**类型的断言工厂*
此类型的断言根据时间做判断,主要有三个:
AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期
BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期
BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内
-After=2019-12-31T23:59:59.789+08:00[Asia/Shanghai]
l *基于远程地址的断言工厂* *RemoteAddrRoutePredicateFactory**:*
接收一个IP地址段,判断请求主机地址是否在地址段中
-RemoteAddr=192.168.1.1/24
l *基于**Cookie**的断言工厂*
CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。 判断请求
cookie是否具有给定名称且值与正则表达式匹配。
-Cookie=chocolate, ch.
l *基于**Header**的断言工厂*
HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。 判断请求Header是否
具有给定名称且值与正则表达式匹配。 key value
-Header=X-Request-Id, \d+
l *基于**Host**的断言工厂*
HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则。
-Host=**.testhost.org
l *基于**Method**请求方法的断言工厂*
MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。
-Method=GET
l *基于**Path**请求路径的断言工厂*
PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。
-Path=/foo/{segment}基于Query请求参数的断言工厂
QueryRoutePredicateFactory :接收两个参数,请求param和正则表达式, 判断请求参数是否具
有给定名称且值与正则表达式匹配。
-Query=baz, ba.
l *基于路由权重的断言工厂*
WeightRoutePredicateFactory:接收一个[组名,权重], 然后对于同一个组内的路由按照权重转发
routes:
-id: weight_route1 uri: host1 predicates:
-Path=/product/**
-Weight=group3, 1
-id: weight_route2 uri: host2 predicates:
-Path=/product/**
-Weight= group3, 9
7.8 过滤器
(1)局部过滤:只对当前路由有效
(2)全局过滤:对所有路由有效
以全局过滤器为例,认证校验功能
l 当客户端第一次请求服务时,服务端对用户进行信息认证(登录)
l 认证通过,将用户信息进行加密形成token,返回给客户端aaaa,作为登录凭证
l 以后每次请求,客户端都携带认证的token
l 服务端对token进行解密,判断是否有效。
如上图,对于验证用户是否已经登录鉴权的过程可以在网关统一检验。
检验的标准就是请求中是否携带token凭证以及token的正确性。
下面的我们自定义一个GlobalFilter,去校验所有请求的请求参数中是否包含“token”,如何不包含请求参数“token”则不转发路由,否则执行正常的逻辑
自定义全局过滤器 要求:必须实现GlobalFilter,Ordered接口
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//获取请求路径
String path = request.getPath().toString();
//该路径是否为放行路径
if(path!=null && path.equals("/login")){//因为不满足断言
return chain.filter(exchange);// 放行
}
//获取当前请求是否携带token令牌---请求头中放置
String token = request.getHeaders().getFirst("token");
if(token!=null && token.equals("admin")){ //注意值:admin随便写。
return chain.filter(exchange);// 放行
}
//请先登录----
//3.2封装返回数据
Map<String, Object> map = new HashMap<>();
map.put("msg", "未登录");
map.put("code", 401);
//3.3作JSON转换--fastjson
byte[] bytes = JSON.toJSONString(map).getBytes(StandardCharsets.UTF_8);
//3.4调用bufferFactory方法,生成DataBuffer对象
DataBuffer buffer = response.bufferFactory().wrap(bytes);
//4.调用Mono中的just方法,返回要写给前端的JSON数据
return response.writeWith(Mono.just(buffer));
}
}
8.链路追踪
在一次调用链路中,可能设计到多个微服务,如果在线上,某个微服务出现故障,如何快速定位故障所在额微服务呢。可以使用链路追踪技术
8.1 链路追踪简介
在大型系统的微服务化构建中,一个系统被拆分成了许多微服务。这些模块负责不同的功能,组合成系统,最终可以提供丰富的功能。在这种架构中,一次请求往往需要涉及到多个服务。互联网应用构建在不同的软件模块集上,这些软件模块,有可能是由不同的团队开发、可能使用不同的编程语言来实现、有可能布在了几千台服务器,横跨多个不同的数据中心【区域】,也就意味着这种架构形式也会存在一些问题:
(1)如何快速发现问题?
(2)如何判断故障影响范围?
(3) 如何梳理服务依赖?
(4)如何分析链路性能问题以及实时容量规划?
分布式链路追踪(Distributed Tracing),就是将一次分布式请求还原成调用链路,进行==日志记录==,==性能监控==并将一次分布式请求的调用情况集中展示。比如各个服务节点上的耗时、请求具体到达哪台机器上IP、每个服务节点的请求状态200 500等等。
9.2 链路追踪使用的组件有那些
1.cat 由大众点评开源,基于Java开发的实时应用监控平台,包括实时应用监控,业务监控 。 集成 方案是通过代码埋点的方式来实现监控,比如: 拦截器,过滤器等。 对代码的侵入性很大,集成成本较高。风险较大。
2.pinpoint Pinpoint是韩国人开源的基于字节码注入的调用链分析,以及应用监控分析工具。特点 是支持多种插件,UI功能强大,接入端无代码侵入。 你开源
3.skywalking 【未来企业会使用的多】
SkyWalking是本土开源的基于字节码注入的调用链分析,以及应用监控分析工具。特点是支持多
种插件,UI功能较强,接入端无代码侵入。目前已加入Apache孵化器--开源。
4.Sleuth **(日志记录每一条链路上的所有节点,以及这些节点所在的机器,和耗时。)
zipkin** 由Twitter公司开源,开放源代码分布式的跟踪系统,用于收集服务的定时数据,以解决微 服务架构中的延迟问题,包括:==数据的收集、存储、查找和展现《图形化》==。该产品结合spring-cloud-sleuth 使用较为简单, 集成很方便, 但是功能较简单。
SpringCloud 提供的分布式系统中链路追踪解决方
我们以 sleuth+zipkin为例
8.3 sleuth 简介
springCloud Sleuth主要功能就是在分布式系统中提供追踪解决方案。它大量借用了Google Dapper的设计, 先来了解一下Sleuth中的术语和相关概念。
*1.Trace* *(一条完整链路--包含很多span(微服务接口))*
由一组Trace Id(贯穿整个链路)相同的Span串联形成一个树状结构。为了实现请求跟踪,当请求到达分布式系统的入口端点时,只需要服务跟踪框架为该请求创建一个唯一的标识(即TraceId),同时在分布式系统内部流转的时候,框架始终保持传递该唯一值,直到整个请求的返回。那么我们就可以使用该唯一标识将所有的请求串联起来,形成一条完整的请求链路。
*2.Span*
代表了一组基本的工作单元。为了统计各处理单元的延迟,当请求到达各个服务组件的时候,也通过一个唯一标识(SpanId)来标记它的开始、具体过程和结束。通过SpanId的开始和结束时间戳,就能统计该span的调用时间,除此之外,我们还可以获取如事件的名称。请求信息等元数据。
*3. Annotation*
用它记录一段时间内的事件,内部使用的重要注释:
l cs(Client Send)客户端发出请求,开始一个请求的命令
l sr(Server Received)服务端接受到请求开始进行处理, sr-cs = 网络延迟(服务调用的时间)
l ss(Server Send)服务端处理完毕准备发送到客户端,ss - sr = 服务器上的请求处理时间
l cr(Client Reveived)客户端接受到服务端的响应,请求结束。 cr - cs = 请求的总时间
8.4 如何使用sleuth
在每一个微服务中引入相关依赖就可以
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency>
启动微服务,调用之后,我们可以在控制台观察到sleuth的日志输出
思考: 我们可以通过sleuth的日志,观察每个微服务执行的时间。但是这样会非常麻烦。能否把这些sleuth生成的日志,以图形化的形式展示。--我们可以使用zipkin来搜集sleuth生成的日志,并以图形化展示。
8.5 zipkin介绍
Zipkin 是 Twitter 的一个开源项目,它基于Google Dapper实现,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集、存储展现、查找和我们可以使用它来收集各个服务器上请求链路的跟踪数据,并通过它提供的REST API接口来辅助我们查询跟踪数据以实现对分布式系统的监控程序,从而及时地发现系统中出现的延迟升高问题并找出系统性能瓶颈的根源
除了面向开发的 API 接口之外,它也提供了方便的UI组件来帮助我们直观的搜索跟踪信息和分析请求链路明细,比如:可以查询某段时间内各用户请求的处理时间等。
Zipkin 提供了可插拔数据存储方式:In-Memory、MySql、Cassandra 以及 Elasticsearch。
从上面的图可以看出zipkin需要一个服务端,而每个微服务就是客户端。
8.6 启动zipkin服务器
微服务接入zipkin服务端
(1)引入zipkin依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
(2)每个微服务接入zipkin服务端
#zipkin服务端的地址
spring.zipkin.base-url=http://localhost:9411/
(3)访问一个链路
9. 配置中心
思考:
商品微服务--搭建n个集群---它的配置一模一样。如果修改某个配置内容。你需要给每个微服务都要修改配置内容
商品微服务和订单微服务他们之间有没有公共配置。---需要对每个微服务都要修改
使用配置中心来管理每个微服务的配置文件。
9.1 那些组件可以作为配置中心
l Apollo**------>很多使用apollo**
Apollo是由携程开源的分布式配置中心。特点有很多,比如:配置更新之后可以实时生效,支持灰度发布功能,并且能对所有的配置进行版本管理、操作审计等功能,提供开放平台API。并且资料 也写的很详细。
l Disconf
Disconf是由百度开源的分布式配置中心。它是基于Zookeeper来实现配置变更后实时通知和生效的。
l SpringCloud Config
这是Spring Cloud中带的配置中心组件。它和Spring是无缝集成,使用起来非常方便,并且它的配置存储支持Git。不过它没有可视化的操作界面,配置的生效也不是实时的,需要重启或去刷新。
l Nacos
这是SpingCloud alibaba技术栈中的一个组件,前面我们已经使用它做过服务注册中心。其实它也集成了服务配置的功能,我们可以直接使用它作为服务配置中心
9.2 在nacos配置中心创建微服务配置文件
9.3 微服务使用配置中心中指定配置文件
#微服务的名称 spring.application.name=product #指定配置中心的地址 spring.cloud.nacos.config.server-addr=192.168.28.182:8848
9.4 在controller层验证读取配置文件信息
@Value("${student.age}")
private Integer age;
@GetMapping("getInfo")
public String get (){
return "我永远"+age+"岁";
}
9.5 bootstrap和application的区别
(1)加载顺序的区别
bootstrap配置文件是比application配置文件优先加载的,因为bootstrap是由spring父上下文加载,而application是由子上下文加载
(2)优先级的区别
bootstrap加载的配置信息是不能被application的相同配置覆盖的,如果两个配置文件同时存在,也是以bootstrap为主
(3)应用场景的区别
bootstrap常见应用场景
1.配置一些固定的,不能被覆盖的属性.用于一些系统级别的参数配置
本地的配置文件是默认不能覆盖远程的配置的
2.一些需要加密/解密的场景
3.当你使用了nacos配置中心时,这时需要在boostrap配置文件中添加连接到配置中心的配置属性来加载外部配置中心的配置信息,专业翻译如下