赞
踩
随着互联网的发展,网站应用的规模也在不断的扩大,进而导致系统架构也在不断的进行变化。
从互联网早起到现在,系统架构大体经历了下面几个过程:
单体应用架构--->垂直应用架构--->分布式架构--->SOA架构--->微服务架构,
当然还有悄然兴起的Service Mesh(服务网格化)。
接下来了解一下每种系统架构是什么样子的, 以及各有什么优缺点。
互联网早期,一般的网站应用流量较小,只需一个应用,将所有功能代码都部署在一起就可以,
这样可以减少开发、部署和维护的成本。比如说一个电商系统,
里面会包含很多用户管理,商品管理,订单管理,物流管理等等很多模块,
我们会把它们做成一个web项目,然后部署到一台tomcat服务器上。
优点:
1.项目架构简单,小型项目的话,开发成本低。
2.项目部署在一个节点上,维护方便
缺点:
1.全部功能集成在一个工程中,对于大型项目来讲不易开发和维护[修改代码]。
2.项目模块之间紧密耦合,单点容错率低。
3.无法针对不同模块进行针对性优化和水平扩展
随着访问量的逐渐增大,单一应用只能依靠增加节点来应对, 但是这时候会发现并不是所有的模块都会有比较大的访问量. 以电商为例子, 用户访问量的增加可能影响的只是用户和订单模块, 但是对消息模块的影响就比较小. 那么此时我们希望只多增加几个订单模块, 而不增加消息模块. 此时单体应用就做不到了, 垂直应用就应运而生了. 所谓的垂直应用架构,就是将原来的一个应用拆成互不相干的几个应用,以提升效率。 比如我们可 以将上面电商的单体应用拆分成: 电商系统(用户管理 商品管理 订单管理) 后台系统(用户管理 订单管理 客户管理) CMS系统(广告管理 营销管理) 这样拆分完毕之后,一旦用户访问量变大, 只需要增加电商系统的节点就可以了,而无需增加后台和CMS的节点。 建立三个工程。
优点:
1.系统拆分实现了流量分担,解决了并发问题,可以针对不同模块进行优化和水平扩展
2.一个系统的问题不会影响到其他系统,提高容错率
缺点:
1.系统之间相互独立, 无法进行相互调用
2.系统之间相互独立, 会有重复的开发任务
当垂直应用越来越多,重复的业务代码就会越来越多。
这时候,我们就思考可不可以将重复的代码 抽取出来,做成统一的业务层作为独立的服务(service),
然后由前端控制层(controller)调用不同的业务层服务呢?
这就产生了新的分布式系统架构。
它将把工程拆分成表现层controller和服务层service两个部分,
服务层中包含业务 逻辑。
表现层只需要处理和页面的交互,业务逻辑都是调用服务层的服务来实现。
优点:
1. 抽取公共的功能为服务层,提高代码复用性
缺点:
2.调用关系错综复杂,难以维护
在分布式架构下,当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,
此时需增加一个调度中心对集群进行实时管理。
此时,用于资源调度和治理中心(SOA Service Oriented Architecture,面向服务的架构)是关键。
优点:
1. 使用注册中心解决了服务间调用关系的自动调节
缺点:
1. 服务间会有依赖关系,一旦某个环节出错会影响较大( 服务雪崩 )
2. 服务关系复杂,运维、测试部署困难。
微服务架构在某种程度上是面向服务的架构SOA继续发展的下一步, 它更加强调服务的"彻底拆分"---->必须要springboot(独立的系统) 必须依赖于springboot技术。 Springcloud如果没有springboot 那么springcloud也无法使用。 springboot可以独立使用。 因为springboot里面内置了tomcat可以独立运行。 只适合大工程 如移动互联网 电商项目 p2p 等, 系统管理--->系统微服务 入库管理 -----入库微服务 优点: 1. 服务原子化拆分,独立打包、部署和升级,保证每个微服务清晰的任务划分,利于扩展 2. 微服务之间采用Restful等轻量级http协议相互调用 缺点: 小型项目----微服务架构不合适。仓库系统---微服务。 微服务系统开发的技术成本高《高》(容错、分布式事务等)
微服务架构,
简单的说就是将单体应用进一步拆分,拆分成更小的服务,
每个服务都是一个可以独立运行的项目。
针对每个问题使用不同的组件来讲解。
一旦采用微服务系统架构,就势必会遇到这样几个问题:
1. 这么多小服务,如何管理他们?
2. 这么多小服务,他们之间如何通讯?调用
3. 这么多小服务,客户端怎么访问他们?前端
4. 这么多小服务,一旦出现问题了,应该如何自处理?
5. 这么多小服务,一旦出现问题了,应该如何排错?
Springcloud微服务就是针对上面这些问题 提出的解决方案。
对于上面的问题,是任何一个微服务设计者都不能绕过去的,
因此大部分的微服务产品都针对每一个问题提供了相应的组件来解决它们。
服务治理:进行服务的自动化管理,其核心是服务的自动注册与发现。
服务注册:服务实例将自身服务信息注册到注册中心。
服务发现:服务实例通过注册中心,获取到注册到其中的服务实例的信息,通过这些信息去请求它们提供的服务。
服务剔除:服务注册中心将出问题的服务自动剔除到可用列表之外,使其不会被调用到。
在微服务架构中,通常存在多个服务之间的远程调用的需求。
目前主流的远程调用技术有基于HTTP的RESTFul接口以及基于TCP的RPC协议。
REST(Representational State Transfer) 是一种HTTP调用的格式,更标准,更通用,无论哪种语言都支持http协议。
RPC(Remote Promote Call)
Rpc @Autowire Bservice bservice.方法()一种进程间通信方式。
允许像调用本地服务一样调用远程服务。
RPC框架的主要目标就是让远程服务调用更简单、透明。
RPC框架负责屏蔽底层的传输方式、序列化方式和通信细节。
开发人员在使用的时候只需要了解谁在什么位置提供了什么样的远程服务接口即可,
并不需要关心底层通信细节和调用过程。
区别与联系
随着微服务的不断增多,不同的微服务一般会有不同的网络地址,
而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,
如果让客户端直接与各个微服务通信可能出现:
1. 客户端需要调用不同的url地址,增加难度
2. 在一定的场景下,存在跨域请求的问题
3. 每个微服务都需要进行单独的身份认证
针对这些问题,API网关顺势而生。
API网关直面意思是将所有API调用统一接入到API网关层,由网关层统一接入和输出。
一个网关的基本功能有:
统一接入、安全防护、协议适配、流量管控、长短链接支持、容错能力。
有了网关之后,各个API服务提供团队可以专注于自己的的业务逻辑处理,
而API网关更专注于安全、流量、路由等问题。
在微服务当中,一个请求经常会涉及到调用几个服务,
如果其中某个服务不可用,没有做服务容错的话,
极有可能会造成一连串的服务不可用,这就是雪崩效应。
我们没法预防雪崩效应的发生,只能尽可能去做好容错。服务容错的三个核心思想是:
1. 不被外界环境影响
2. 不被上游请求压垮
3. 不被下游响应拖垮
随着微服务架构的流行,服务按照不同的维度进行拆分,一次请求往往需要涉及到多个服务。
互联网应用构建在不同的软件模块集上,这些软件模块,
有可能是由不同的团队开发、可能使用不同的编程语言来实现、
有可能布在了几千台服务器,横跨多个不同的数据中心。
因此,就需要对一次请求涉及的多个服务链路进行日志记录,性能监控即链路追踪
Apache ServiceComb,
前身是华为云的微服务引擎 CSE (Cloud Service Engine) 云服务,
是全球首个Apache微服务顶级项目。
它提供了一站式的微服务开源解决方案,致力于帮助企业、用户和开发者将企业应用轻松微服务化上云,
并实现对微服务应用的高效运维管理.
springcloud 很多组件都是拿的是Netflix公司,这家公司这些组件停止维护和更新。
Spring Cloud是一系列框架的集合。
它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,
如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,
都可以用Spring Boot的开发风格做到一键启动和部署。
Spring Cloud并没有重复制造轮子,
它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,
通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,
最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
Netflix已经停更了。Euruka(注册中),feign远程调用,hystrix 容错,zuul网关
引入springcloud很多组件都更新了,springcloud alibaba
Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。
此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。
Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。
此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。
依托 Spring Cloud Alibaba,只需要添加一些注解和少量配置,
就可以将 Spring Cloud 应用接入阿里微服务解决方案,
通过阿里中间件来迅速搭建分布式应用系统。
服务限流降级:默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、 Spring Cloud Gateway,Zuul, Dubbo 和 RocketMQ 限流降级功能的接入, 可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。 服务注册与发现:适配 Spring Cloud 服务注册与发现标准nacos,默认集成了 Ribbon 的支持。 分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。 消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。 分布式事务:使用 @GlobalTransactional 注解,高效并且对业务零侵入地解决分布式事务问题。 阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、 任何时间、任何地点存储和访问任意类型的数据。 分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。 同时提供分布式的任务执行模型,如网格任务。 网格任务支持海量子任务均匀分配到所有Worker(schedulerx-client)上执行。 阿里云短信服务:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建 客户触达通道。
Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 RocketMQ:一款开源的分布式消息系统,基于高可用分布式集群技术, 提供低延时的、高可靠的消息发布与订阅服务。 Dubbo:Apache Dubbo™ 是一款高性能 Java RPC 框架。 Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。 Alibaba Cloud ACM:一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。 Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS), 是阿里云提供的海量、安全、低成本、高可靠的云存储服务。 您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。 Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品, 提供秒级、精 准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。 Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力, 帮助企业迅速搭建客户触达通道。 微服务拆分的原则 原则一,做到单一服务内部功能的高内聚和低耦合。 原则二,你需要关注服务拆分的粒度,先粗略拆分再逐渐细化。 原则三,拆分的过程,要尽量避免影响产品的日常功能迭代。 原则四,服务接口的定义要具备可扩展性。
maven:3.5.0+
数据库:MySQL 5.7 以上
持久层: Mybatis-plus 《Mybatis Mapper Mybatis-plus》
其他: SpringCloud Alibaba 技术栈 druid
springcloud-alibaba 父工程 ----jar的版本管理 公共jar的引入
springcloud-common 公共模块【实体类】 《实体类,公共依赖,工具类。》
springcloud-product 商品微服务 【端口: 8080~8089 搭建集群】
springcloud-order 订单微服务 【端口: 8090~8099 搭建集群】
在微服务架构中,最常见的场景就是微服务之间的相互调用。
我们以电商系统中常见的用户下单为例来演示微服务的调用:
客户向订单微服务发起一个下单的请求,在进行保存订单之前需要调用商品微服务查询商品的信息。
一般把服务的主动调用方称为服务消费者,把服务的被调用方称为服务提供者。
在这种场景下,订单微服务就是一个服务消费者, 商品微服务就是一个服务提供者。
数据库数据准备:
shop_order表
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for shop_order -- ---------------------------- DROP TABLE IF EXISTS `shop_order`; CREATE TABLE `shop_order` ( `oid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单id', `uid` int(11) NULL DEFAULT NULL COMMENT '用户id', `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户名', `pid` bigint(20) NULL DEFAULT NULL COMMENT '商品id', `pname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '商品名称', `pprice` decimal(10, 2) NULL DEFAULT NULL COMMENT '商品价格', `number` int(11) NULL DEFAULT NULL COMMENT '购买数量', PRIMARY KEY (`oid`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 249 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;
shop_product表
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for shop_product -- ---------------------------- DROP TABLE IF EXISTS `shop_product`; CREATE TABLE `shop_product` ( `pid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '商品id', `pname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '商品名', `pprice` decimal(10, 2) NULL DEFAULT NULL COMMENT '商品价格', `stock` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '商品库存', PRIMARY KEY (`pid`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of shop_product -- ---------------------------- INSERT INTO `shop_product` VALUES (1, '华为手机', 3999.22, '100'); INSERT INTO `shop_product` VALUES (2, 'vivo手机', 2999.00, '100'); SET FOREIGN_KEY_CHECKS = 1;
创建一个springboot工程
(1)引入依赖
<!--定义版本号--> <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:它只负责jar的版本号管理,不负责jar的下载,交于子模块,子模块在使用时无需指定版本号 :springboot springcloud springcloudalibaba之间版本一定要匹配 --> <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> <!--springcloudalibaba的版本管理--> <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>
版本对应关系
(1) 创建 product-common 模块(maven项目即可),在pom.xml中添加依赖
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
</dependencies>
(2) 创建实体类
@Data
@TableName("shop_order")
public class Order {
@TableId(type = IdType.AUTO)
private Long oid; //订单id
private Integer uid;//用户id
private String username;//用户名
private Integer pid;//商品id
private String pname;//商品名称
private Double pprice;//商品价格
private Integer number;//购买数量
}
@Data
@TableName(value="shop_product")
public class Product {
@TableId(type= IdType.AUTO)
private Integer pid;
private String pname;//商品名称
private Double pprice;//商品价格
private Integer stock;//库存
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result {
private Integer code;
private String msg;
private Object data;
}
同样是创建maven项目即可
步骤:
1 创建模块 导入依赖
2 创建SpringBoot主类
3 加入配置文件
4 创建必要的接口和实现类(controller service dao)
新建一个 springcloud-product 模块,然后进行下面操作
(1) 修改pom.xml
<dependencies> <dependency> <groupId>com.wx</groupId> <artifactId>springcloud-common</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies>
(2)编写主类
@SpringBootApplication
public class ProductApp {
public static void main(String[] args) {
SpringApplication.run(ProductApp.class,args);
}
}
(3) 创建配置文件
server.port=8081
spring.datasource.url=jdbc:mysql://localhost:3306/springcloud?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
(4)创建业务层 dao层 控制层
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("findById/{pid}")
public Result findById(@PathVariable int pid){
return productService.findById(pid);
}
}
@Mapper
public interface ProductMapper extends BaseMapper<Product> {
}
@Service public class ProductServiceImpl implements ProductService { @Autowired private ProductMapper productMapper; @Override public Result findById(int pid) { Product product = productMapper.selectById(pid); if(product!=null){ return new Result(2000,"查询成功",product); }else{ return new Result(5000,"查询失败",product); } } }
public interface ProductService {
Result findById(int pid);
}
新建一个 springcloud-order 模块,然后进行下面操作
(1)引入依赖
<dependencies> <dependency> <groupId>com.wx</groupId> <artifactId>springcloud-common</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</artifactId> <version>2.4</version> <classifier>jdk15</classifier> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies>
(2)修改配置文件
server.port=8091
spring.datasource.url=jdbc:mysql://localhost:3306/springcloud?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
(3)创建主类
@SpringBootApplication
public class OrderApp {
public static void main(String[] args) {
SpringApplication.run(OrderApp.class,args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
(4)创建业务层 dao层 控制层
public interface OrderService {
public Result saveOrder(Order order);
}
@Service public class OrderServiceImpl implements OrderService { @Autowired private OrderDao orderDao; @Override public Result saveOrder(Order order) { int insert = orderDao.insert(order); if(insert>0){ return new Result(2000,"添加订单成功",insert); }else{ return new Result(5000,"添加订单失败",insert); } } }
@RestController @RequestMapping("order") public class OrderController { @Autowired private OrderService orderService; @Autowired private RestTemplate restTemplate; @RequestMapping("placeOrder/{pid}/{number}") public Result saveOrder(@PathVariable int pid, @PathVariable int number){ Order order = new Order(); order.setUid(1); order.setUsername("张三"); order.setNumber(number); order.setPid(pid); Result result = restTemplate.getForObject("http://localhost:8081/product/findById/" + pid, Result.class); JSONObject jsonObject=JSONObject.fromObject(result.getData()); Product product = (Product) JSONObject.toBean(jsonObject, Product.class); order.setPname(product.getPname()); order.setPprice(product.getPprice()); return orderService.saveOrder(order); } }
两个微服务创建成功
通过上一章的操作,我们已经可以实现微服务之间的调用。
但是我们把服务提供者的网络地址 (ip,端口)等硬编码到了代码中,这种做法存在许多问题:
一旦服务提供者地址变化,就需要手工修改代码
一旦是多个服务提供者,无法实现负载均衡功能
一旦服务变得越来越多,人工维护调用关系困难
那么应该怎么解决呢, 这时候就需要通过注册中心动态的实现服务治理。
什么是服务治理
服务治理是微服务架构中最核心最基本的模块。用于实现各个微服务的自动化注册与发现。
服务注册:
在服务治理框架中,都会构建一个注册中心,
每个服务单元向注册中心登记自己提供服务的详细信息并在注册中心形成一张服务的清单,
服务注册中心需要以心跳30s 90s的方式去监测清单中的服务是否可用,
如果不可用,需要在服务清单中剔除不可用的服务。
服务发现:服务调用方向服务注册中心咨询服务,并获取所有服务的实例清单,实现对具体服务实
例的访问。
通过上面的调用图会发现,除了微服务,还有一个组件是服务注册中心,
它是微服务架构非常重要的一个组件,
在微服务架构里主要起到了协调者的一个作用。注册中心一般包含如下几个功能:
1. 服务发现:
服务注册:保存服务提供者和服务调用者的信息
服务订阅:服务调用者订阅服务提供者的信息,注册中心向订阅者推送提供者的信息
2. 服务配置:
配置订阅:服务提供者和服务调用者订阅微服务相关的配置
配置下发:主动将配置推送给服务提供者和服务调用者
3. 服务健康检测
检测服务提供者的健康情况,如果发现异常,执行服务剔除
常见的注册中心
Zookeeper zookeeper是一个分布式服务框架,是Apache Hadoop 的一个子项目, 它主要是用来解决分布式应用中经常遇到的一些数据管理问题, 如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。 Eureka Eureka是Springcloud Netflix中的重要组件,主要作用就是做服务注册和发现。 但是现在已经闭源 ,停更不停用。 Consul Consul是基于GO语言开发的开源工具, 主要面向分布式,服务化的系统提供服务注册、服务发现和配置管理的功能。 Consul的功能都很实用,其中包括:服务注册/发现、健康检查、Key/Value 存储、多数据中心和分布式一致性保证等特性。 Consul本身只是一个二进制的可执行文件, 所以安装和部署都非常简单,只需要从官网下载后,在执行对应的启动脚本即可。 Nacos (服务治理 配置中心) Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 它是 SpringCloud Alibaba 组件之一,负责服务注册发现和服务配置, 可以这样认为nacos=eureka+config。
Nacos 致力于帮助您发现、配置和管理微服务。
Nacos 提供了一组简单易用的特性集,
帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos的作用就是一个注册中心,用来管理注册上来的各个微服务
接下来,我们就在现有的环境中加入nacos,并将我们的两个微服务注册上去。
https://github.com/alibaba/nacos/releases
第1步: 安装nacos
下载地址: https://github.com/alibaba/nacos/releases
下载zip格式的安装包,然后进行解压缩操作
第2步: 启动nacos
#切换目录
cd nacos/bin 默认为集群模式启动 但现在没有配置集群 所以使用单例模式启动
#命令启动
startup.cmd -m standalone
第3步: 访问nacos
打开浏览器输入http://localhost:8848/nacos,即可访问服务, 默认密码是nacos/nacos
接下来开始修改 springcloud-product 模块的代码, 将其注册到Nacos服务上
(1) 在pom.xml中添加nacos的依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery
</artifactId>
</dependency>
(2. 在主启动类上添加nacos的开启注解
(3)在application.properties添加nacos的配置
spring.cloud.nacos.discovery.server-addr=localhost:8848
#自己创建的服务名
spring.application.name=springcloud-product
前两步一样 第三步修改一下
spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.application.name=springcloud-order
(4) 启动服务, 观察nacos的控制面板中是否有注册上来的商品微服务
(5) 修改OrderController中的代码
@RestController @RequestMapping("order") public class OrderController { @Autowired private OrderService orderService; @Autowired private RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; @RequestMapping("placeOrder/{pid}/{number}") public Result saveOrder(@PathVariable int pid, @PathVariable int number){ Order order = new Order(); order.setUid(1); order.setUsername("张三"); order.setNumber(number); order.setPid(pid); List<ServiceInstance> instances = discoveryClient.getInstances("springcloud-product"); ServiceInstance serviceInstance = instances.get(0); Result result = restTemplate.getForObject(serviceInstance.getUri()+"/product/findById/" + pid, Result.class); JSONObject jsonObject=JSONObject.fromObject(result.getData()); Product product = (Product) JSONObject.toBean(jsonObject, Product.class); order.setPname(product.getPname()); order.setPprice(product.getPprice()); return orderService.saveOrder(order); } }
DiscoveryClient是专门负责服务注册和发现的,我们可以通过它获取到注册到注册中心的所有服务。
(6)启动订单微服务
通俗的讲,
负载均衡就是将负载(工作任务,访问请求)进行分摊到多个操作单元(服务器,组件上进行执行。
根据负载均衡发生位置的不同,一般分为服务端负载均衡和客户端负载均衡。
服务端负载均衡指的是发生在服务提供者一方,比如常见的nginx负载均衡
而客户端负载均衡指的是发生在服务请求的一方,也就是在发送请求之前已经选好了由哪个实例处理请求
我们在微服务调用关系中一般会选择客户端负载均衡,
也就是在服务调用的一方来决定服务由哪个提供者执行.
(1) 通过idea再启动一个 springcloud-product 微服务,设置其端口为8082
(2) 通过nacos查看微服务的启动情况
(3) 修改 product-order 的代码,实现负载均衡
@RestController @RequestMapping("order") public class OrderController { @Autowired private OrderService orderService; @Autowired private RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; @RequestMapping("placeOrder/{pid}/{number}") public Result saveOrder(@PathVariable int pid, @PathVariable int number){ Order order = new Order(); order.setUid(1); order.setUsername("张三"); order.setNumber(number); order.setPid(pid); List<ServiceInstance> instances = discoveryClient.getInstances("springcloud-product"); ServiceInstance serviceInstance = instances.get(new Random().nextInt(instances.size())); Result result = restTemplate.getForObject(serviceInstance.getUri()+"/product/findById/" + pid, Result.class); JSONObject jsonObject=JSONObject.fromObject(result.getData()); Product product = (Product) JSONObject.toBean(jsonObject, Product.class); order.setPname(product.getPname()); order.setPprice(product.getPprice()); return orderService.saveOrder(order); } }
(4) 启动两个服务提供者和一个服务消费者,多访问几次消费者测试效果
什么是Ribbon
1. 是 Netflix 发布的一个负载均衡器,有助于控制 HTTP 和 TCP客户端行为。
在 SpringCloud 中, nacos一般配合Ribbon进行使用,
Ribbon提供了客户端负载均衡的功能,Ribbon利用从nacos中读取到的服务信息,
在调用服务节点提供的服务时,会合理(策略)的进行负载。
在SpringCloud中可以将注册中心和Ribbon配合使用,
Ribbon自动的从注册中心中获取服务提供者的列表信息,并基于内置的负载均衡算法,请求服务。
2. 是 Netflix 发布的一个负载均衡器,
Ribbon自动的从注册中心中获取服务提供者的 列表信息,
并基于内置的负载均衡算 法,请求服务。
2.Ribbon的主要作用
(1)服务调用
基于Ribbon实现服务调用, 是通过拉取到的所有服务列表组成(服务名-请求路径的)映射关系。
借助RestTemplate 最终进行调用
(2)负载均衡
当有多个服务提供者时,Ribbon可以根据负载均衡的算法自动的选择需要调用的服务地址
Ribbon是Spring Cloud的一个组件, 它可以让我们使用一个注解就能轻松的搞定负载均衡
第1步:在RestTemplate 的生成方法上添加@LoadBalanced注解
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
第2步:修改服务调用的方法
@RestController @RequestMapping("order") public class OrderController { @Autowired private OrderService orderService; @Autowired private RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; @RequestMapping("placeOrder/{pid}/{number}") public Result saveOrder(@PathVariable int pid, @PathVariable int number){ Order order = new Order(); order.setUid(1); order.setUsername("张三"); order.setNumber(number); order.setPid(pid); /*List<ServiceInstance> instances = discoveryClient.getInstances("springcloud-product"); ServiceInstance serviceInstance = instances.get(new Random().nextInt(instances.size())); System.out.println(serviceInstance.getPort());*/ Result result = restTemplate.getForObject("http://springcloud-product/product/findById/" + pid, Result.class); JSONObject jsonObject=JSONObject.fromObject(result.getData()); Product product = (Product) JSONObject.toBean(jsonObject, Product.class); order.setPname(product.getPname()); order.setPprice(product.getPprice()); return orderService.saveOrder(order); } }
(3) 启动服务 看端口号对应的日志 发现是轮询策略
==Ribbon支持的负载均衡策略 ==
Ribbon内置了多种负载均衡策略,内部负载均衡的顶级接口为
com.netflix.loadbalancer.IRule , 具体的负载策略如下图所示:
我们可以通过修改配置来调整Ribbon的负载均衡策略,具体代码如下
#设置随机
springcloud-product.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
上面使用ribbon完成负载均衡缺点:restTemplate---url地址。
1.代码可读性较差.
2.编码风格不一样.
习惯的编码风格service 调用dao service中注入dao,dao对象调用相应的方法
OpenFeign是Spring Cloud提供的一个声明式的伪Http客户端,
它使得调用远程服务就像调用本地服务一样简单, 只需要创建一个接口并添加一个注解即可。
Nacos很好的兼容了Feign, Feign负载均衡默认集成了 Ribbon,
所以在Nacos下使用Fegin默认就实现了负载均衡的效果。
1 加入Fegin的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2 在主启动类上加入开启feign的注解
@EnableFeignClients
3 创建feign的接口
@FeignClient(value = "springcloud-product")
public interface ProductFeign {
@RequestMapping("/product/findById/{pid}")
public Result findById(@PathVariable int pid);
}
4 修改OrderController的代码
@RestController @RequestMapping("order") public class OrderController { @Autowired private OrderService orderService; @Autowired private RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; @Autowired private ProductFeign productFeign; @RequestMapping("placeOrder/{pid}/{number}") public Result saveOrder(@PathVariable int pid, @PathVariable int number){ Order order = new Order(); order.setUid(1); order.setUsername("张三"); order.setNumber(number); order.setPid(pid); Result result = productFeign.findById(pid); JSONObject jsonObject=JSONObject.fromObject(result.getData()); Product product = (Product) JSONObject.toBean(jsonObject, Product.class); order.setPname(product.getPname()); order.setPprice(product.getPprice()); return orderService.saveOrder(order); } }
5 重启order微服务,查看效果
在实际开发过程中,如果使用Nacos的话,为了确保高可用,我们一般都会对其进行集群的部署。Nacos规定集群中Nacos节点的数量需要大于等于3个;
同时,单机模式下Nacos的数据默认保存在其内嵌数据库中deby,不方便观察数据存储的基本情况。
而且如果集群中启动多个默认配置下的Nacos节点,数据存储是存在一致性问题的。
为了解决这个问题,Nacos采用了集中式存储的方式来支持集群化部署,目前只支持MySQL的存储;
此外,我们还需要借助Nginx实现负载均衡。这一过程的部署架构图如下所示:
步骤
1.准备三个nacos服务器---为了操作方便统一部署到同一台电脑 【8849.8850.8851】
2.准备mysql---创建一个nacos-config--创建相应的表
1. 创建数据库
1. 打开nacos文件 nacos->conf->nacos-mysql.sql
将该数据库中的内容复制到navicat(或者其他的sql图形化界面)起名为nacos-config
2.进入application.properties 修改配置内容 修改内容如下图
3.修改cluster.conf.example文件名改为cluster.conf
4. 配置集群 打开cluster.conf
5 .复制nacos三份 并修改application.properties 文件中端口号
6. 启动三个nacos
7. 使用nginx搭建集群 ,使用window版本即可 ,修改之后启动即可
upstream nacoscluster{
server 192.168.1.65:8849;
server 192.168.1.65:8850;
server 192.168.1.65:8851;
}
server {
listen 81;
server_name localhost;
location / {
proxy_pass http://nacoscluster;
}
}
8. 进入浏览器查看 http://localhost:81/nacos
9. 微服务注册到nacos集群上
启动项目 然后测试接口即可
没有nacos好用
Eureka是Netflix开发的服务发现框架,
SpringCloud将它集成在自己的子项目spring-cloud-netflix中,实现SpringCloud的服务发现功能。
<dependencies> <!--eureka-server--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> </dependencies>
server:
port: 7001
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:7001/eureka/
# 安全设置
security:
basic:
enabled: false
@SpringBootApplication
@EnableEurekaServer
public class EurekaApp {
public static void main(String[] args) {
SpringApplication.run(EurekaApp.class,args);
}
}
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
yml文件中添加配置
#配置服务器的地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
在微服务架构里,服务的粒度被进一步细分,
各个业务服务可以被独立的设计、开发、测试、部署和管理。
这时,各个独立部署单元可以用不同的开发测试团队维护,
可以使用不同的编程语言和技术平台进行设计,
这就要求必须使用一种语言和平 台无关的服务协议作为各个单元间的通讯方式。
优点:
1.性能强劲:是第一代网关Zuul的1.6倍
2.功能强大:内置了很多实用的功能,例如转发、监控、限流等
3.设计优雅,容易扩展.
缺点:
1.其实现依赖Netty与WebFlux,不是传统的Servlet编程模型,学习成本高
2.不能将其部署在Tomcat、Jetty等Servlet容器里,只能打成jar包执行 web.Jar
3.需要Spring Boot 2.0及以上的版本,才支持
gateway内置了服务器 netty服务器。
通过浏览器访问api网关,然后通过网关将请求转发到商品微服务
步骤
(1)创建一个 springcloud-gateway 的工程(模块 new Module)并加入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
(2)创建配置yml文件
server: port: 7000 spring: application: name: springcloud-gateway #路由转发 List<RouteDefinition> routes cloud: gateway: routes: - id: springcloud-product #product微服务路由转发的真是地址 uri: http://localhost:8081 # predicates:当满足断言时,才会转发到真实的uri地址 predicates: - Path=/product/**
(3)创建主启动类
@SpringBootApplication
public class GateWayApp {
public static void main(String[] args) {
SpringApplication.run(GateWayApp.class,args);
}
}
测试
发现:
路由配置时uri的地址还是一个固定的网络地址,
如果后期微服务地址发送改变,也需要修改网关的配置。 不能完成负载均衡。
解决: gateway也是可一个微服务,它可以从注册中心拉取服务信息。
(1)引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
(2)配置文件中指定注册中心的地址
server: port: 7000 spring: application: name: springcloud-gateway #路由转发 cloud: gateway: routes: - id: springcloud-product #路由转发的真是地址 uri: lb://springcloud-product predicates: - Path=/product/** - id: springcloud-order uri: lb://springcloud-order predicates: - Path=/order/** nacos: server-addr: localhost:81
启动项目并测试
发现:
有一个微服务就需要配置一个路由,
如果这时增加一个新的微服务,则需要在配置文件中增加一个新的路由配置。
能不能自动路由转发微服务呢?
gateway自动路由
(1)修改配置文件
(2)启动项目
路由(Route) 是 gateway 中最基本的组件之一,表示一个具体的路由信息载体。
主要定义了下面的几个信息:
id: 路由标识符,区别于其他 Route。默认生成一个
uri: 路由指向的目的地 uri,即客户端请求最终被转发到的微服务。
order: 用于多个 Route 之间的排序,数值越小排序越靠前,匹配优先级越高。
predicate: 断言的作用是进行条件判断,只有断言都返回真,才会真正的执行路由。
filter: 过滤器用于修改请求和响应信息。
执行流程大体如下:
1. Gateway Client向Gateway Server发送请求
2. 请求首先会被HttpWebHandlerAdapter进行提取组装成网关上下文
3. 然后网关的上下文会传递到DispatcherHandler,它负责将请求分发RoutePredicateHandlerMapping
4. RoutePredicateHandlerMapping负责路由查找,并根据路由断言判断路由是否可用
5. 如果过断言成功,由FilteringWebHandler创建过滤器链并调用
6. 请求会一次经过PreFilter--微服务--PostFilter的方法,最终返回响应
Predicate(断言, 谓词) 用于进行条件判断,只有断言都返回真,才会真正的执行路由。
断言就是说: 在 什么条件下 才能进行路由转发
SpringCloud Gateway包括许多内置的断言工厂,所有这些断言都与HTTP请求的不同属性匹配体如下: 1. 基于Datetime类型的断言工厂 此类型的断言根据时间做判断,主要有三个: AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期 BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期 BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内 -After=2019-12-31T23:59:59.789+08:00[Asia/Shanghai] 2.基于远程地址的断言工厂 RemoteAddrRoutePredicateFactory: 接收一个IP地址段,判断请求主机地址是否在地址段中 -RemoteAddr=192.168.1.1/24 3.基于Cookie的断言工厂 CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。 判断请求 cookie是否具有给定名称且值与正则表达式匹配。 -Cookie=chocolate, ch. 4.基于Header的断言工厂 HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。 判断请求Header是否具有给定名称且值与正则表达式匹配。 key value -Header=X-Request-Id, \d+ 5. 基于Host的断言工厂 HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则。 -Host=**.testhost.org 6.基于Method请求方法的断言工厂 MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。 -Method=GET 7.基于Path请求路径的断言工厂 PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。 -Path=/foo/{segment}基于Query请求参数的断言工厂 QueryRoutePredicateFactory :接收两个参数,请求param和正则表达式, 判断请求参数是否具 有给定名称且值与正则表达式匹配。 -Query=baz, ba. 8.基于路由权重的断言工厂 WeightRoutePredicateFactory:接收一个[组名,权重], 然后对于同一个组内的路由按照权重转发 routes: -id: weight_route1 uri: host1 predicates: -Path=/product/** -Weight=group3, 1 -id: weight_route2 uri: host2 predicates: -Path=/product/** -Weight= group3, 9 9.内置路由断言工厂的使用
server: port: 7000 spring: application: name: api-gateway # 配置api cloud: nacos: discovery: server-addr: localhost:8848 gateway: # discovery: # locator: # enabled: true routes: - id: product_route # 路由的唯一标识,只要不重复都可以,如果不写默认会通过UUID产生,一般写成被路由的服务名称 uri: lb://shop-product # 被路由的地址 order: 1 #表示优先级 数字越小优先级越高 predicates: #断言: 执行路由的判断条件 - Path=/product_serv/** - Before=2020-11-28T00:00:00.000+08:00 # 表示在2020前访问 - Method=POST # 请求方式必须为POST filters: # 过滤器: 可以在请求前或请求后作一些手脚 - StripPrefix=1
如果上面内置的断言不能满足你的需求:可以自定义断言。
注意: 名称必须为XXXRoutePredicateFactory并且继承AbstractRoutePredicateFactory
1 作用: 过滤器就是在请求的传递过程中,对请求和响应做一些手脚 2 生命周期: Pre Post 3 分类: 局部过滤器(作用在某一个路由上) 全局过滤器(作用全部路由上) 在Gateway中, Filter的生命周期只有两个:“pre” 和 “post”。 1.PRE: 这种过滤器在请求被路由之前调用。 我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。 2. POST:这种过滤器在路由到微服务以后执行。 这种过滤器可用来为响应添加标准的HTTP Header、 收集统计信息和指标、将响应从微服务发送给客户端等。 Gateway 的Filter从作用范围可分为两种: GatewayFilter与GlobalFilter。 1.GatewayFilter:应用到单个路由或者一个分组的路由上。 2.GlobalFilter:应用到所有的路由上。
作用: 认证校验 黑白名单 敏感词
定义全局过滤器
(1)在配置文件添加过滤路径
anon:
url:
- '/sso/login'
- '/product/getMsg'
(2)写实体类 用来读取配置文件中的值
@Data
@Component
@ConfigurationProperties(prefix = "anon")
public class Anon {
private List<String> url;
}
(3)在springcloud-product中的controller层加入代码
@GetMapping("getMsg")
public String getMsg(){
return "getMsg===-====";
}
(4)自定义过滤器类
@Component public class LoginFilter implements GlobalFilter, Ordered { @Autowired private Anon anon; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); String path = request.getPath().toString(); System.out.println(anon); //1.判断当前的请求路径是否为放行路径 if(anon.getUrl().contains(path)){ return chain.filter(exchange); } //判断是否携带token 模拟一下 未来可以使用redis String token = request.getHeaders().getFirst("token"); if(StringUtils.hasText(token)&&"admin".equals(token)){ return chain.filter(exchange); } //返回json数据 //设置状态码 response.setStatusCode(HttpStatus.UNAUTHORIZED); //封装返回的数据 Map<String,Object> map = new HashMap<>(); map.put("msg","未登录"); map.put("code",4000); //做json转换 byte[] bytes = JSON.toJSONString(map).getBytes(StandardCharsets.UTF_8); //调用bufferFactory方法,生成DataBuffer对象 DataBuffer wrap = response.bufferFactory().wrap(bytes); //调用Mono中的just方法,返回要写给前端的JSON数据 return response.writeWith(Mono.just(wrap)); } @Override public int getOrder() { return 0; } }
测试:
在大型系统的微服务化构建中,一个系统被拆分成了许多微服务。
这些模块负责不同的功能,组合成系统,最终可以提供丰富的功能。
在这种架构中,一次请求往往需要涉及到多个服务。
互联网应用构建在不同的软件模块集上,
这些软件模块,有可能是由不同的团队开发、可能使用不同的编程语言来实现、
有可能布在了几千台服务器,横跨多个不同的数据中心,
也就意味着这种架构形式也会存在一些问题:
如何快速发现问题?
如何判断故障影响范围?
如何梳理服务依赖以及依赖的合理性?
如何分析链路性能问题以及实时容量规划?
分布式链路追踪(Distributed Tracing): 将一次分布式请求还原成调用链路,进行日志记录, 性能监控并将一次分布式请求的调用情况集中展示。 比如各个服务节点上的耗时、请求具体到达哪台机器上IP、每个服务节点的请求状态200 、500等。 常见的链路追踪技术有下面这些: 1. cat 由大众点评开源,基于Java开发的实时应用监控平台, 包括实时应用监控,业务监控 。 集成方案是通过代码埋点的方式来实现监控, 比如: 拦截器,过滤器等。 对代码的侵入性很大,集成成本较高。风险较大。 2. zipkin 由Twitter公司开源,开放源代码分布式的跟踪系统, 用于收集服务的定时数据,以解决微服务架构中的延迟问题, 包括:数据的收集、存储、查找和展现《图形化》。 该产品结合spring-cloud-sleuth 使用较为简单, 集成很方便, 但是功能较简单。 3. Pinpoint是韩国人开源的基于字节码注入的调用链分析, 以及应用监控分析工具。特点是支持多种插件,UI功能强大,接入端无代码侵入。 4.skywalking 【未来企业会使用的多】 SkyWalking是本土开源的基于字节码注入的调用链分析,以及应用监控分析工具。 特点是支持多种插件,UI功能较强,接入端无代码侵入。目前已加入Apache孵化器。 5. Sleuth (日志记录每一条链路上的所有节点,以及这些节点所在的机器,和耗时。) SpringCloud 提供的分布式系统中链路追踪解决方案。 注意:SpringCloud alibaba技术栈中并没有提供自己的链路追踪技术的, 我们可以采用Sleuth + Zipkin来做链路追踪解决方案
Springcloud 并不是自己技术—而是把所有框架整合在一起来解决微服务上的问题。
SpringCloud Sleuth主要功能就是在分布式系统中提供追踪解决方案。
它大量借用了Google Dapper的设计。 Sleuth中的术语和相关概念: 1.Trace (一条完整链路--包含很多span(微服务接口)), 由一组Trace Id(贯穿整个链路相同的Span串联形成一个树状结构。 为了实现请求跟踪,当请求到达分布式系统的入口端点时, 只需要服务跟踪框架为该请求创建一个唯一的标识(即TraceId), 同时在分布式系统内部流转的时候,框架始终保持传递该唯一值,直到整个请求的返回。 那么我们就可以使用该唯一标识将所有的请求串联起来,形成一条完整的请求链路。 2.Span 代表了一组基本的工作单元。 为了统计各处理单元的延迟, 当请求到达各个服务组件的时候, 也通过一个唯一标识(SpanId)来标记它的开始、具体过程和结束。 通过SpanId的开始和结束时间戳,就能统计该span的调用时间, 除此之外,我们还可以获取如事件的名称。请求信息等元数据。 3.Annotation 用它记录一段时间内的事件,内部使用的重要注释: cs(Client Send)客户端发出请求,开始一个请求的命令 sr(Server Received)服务端接受到请求开始进行处理, sr-cs = 网络延迟(服务调用的时间) ss(Server Send)服务端处理完毕准备发送到客户端,ss - sr = 服务器上的请求处理时间 cr(Client Reveived)客户端接受到服务端的响应,请求结束。 cr - cs = 请求的总时间
接下来通过之前的项目案例整合Sleuth,完成入门案例的编写。
(1)修改父工程引入Sleuth依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
启动微服务,调用之后,我们可以在控制台观察到sleuth的日志输出
其中 5399d5cb061971bd 是TraceId,
5399d5cb061971bd 是SpanId,
依次调用有一个全局的TraceId,将调用链路串起来。
仔细分析每个微服务的日志,不难看出请求的具体过程。
查看日志文件并不是一个很好的方法,当微服务越来越多日志文件也会越来越多,
通过Zipkin可以将日志聚合,并进行可视化展示和全文检索。
Zipkin 是 Twitter 的一个开源项目,
它基于Google Dapper实现,
它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,
包括数据的收集、存储展现、查找和我们可以使用它来收集各个服务器上请求链路的跟踪数据,
并通过它提供的REST API接口来辅助我们查询跟踪数据以实现对分布式系统的监控程序,
从而及时地发现系统中出现的延迟升高问题并找出系统性能瓶颈的根源
除了面向开发的 API 接口之外,
它也提供了方便的UI组件来帮助我们直观的搜索跟踪信息和分析请求链路明细,
比如:可以查询某段时间内各用户请求的处理时间等。
Zipkin 提供了可插拔数据存储方式:In-Memory、MySql、Cassandra 以及 Elasticsearch。
第1步: 下载ZipKin的jar包
https://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/
第2步: 通过命令行,输入下面的命令启动ZipKin Server
java -jar zipkin-server-2.12.9-exec.jar
第3步:通过浏览器访问 http://localhost:9411访问
ZipKin客户端和Sleuth的集成非常简单,只需要在微服务中添加其依赖和配置即可
(1)这里可以直接在父工程中添加依赖 或者在每个微服务中都添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
第2步:在每个微服务中添加配置
application.properties中的
#指定zipkin服务端缩再的地址
spring.zipkin.base-url=http://localhost:9411
#不注册到服务中心
spring.zipkin.discovery-client-enabled=false
#设置zipkin对sleuth链路日志的采样比例 企业为3%
spring.sleuth.sampler.probability=1.0
第3步: 访问微服务
第4步: 访问zipkin的UI界面,观察效果
第5步:点击其中一条记录,可观察一次访问的详细线路
Zipkin Server默认会将追踪数据信息保存到内存,但这种方式不适合生产环境。
Zipkin支持将追踪数据持久化到mysql数据库或elasticsearch中。
第1步: 创建mysql数据环境
CREATE TABLE IF NOT EXISTS zipkin_spans ( `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit', `trace_id` BIGINT NOT NULL, `id` BIGINT NOT NULL, `name` VARCHAR(255) NOT NULL, `parent_id` BIGINT, `debug` BIT(1), `start_ts` BIGINT COMMENT 'Span.timestamp(): epoch micros used for endTs query and to implement TTL', `duration` BIGINT COMMENT 'Span.duration(): micros used for minDuration and maxDuration query' ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci; ALTER TABLE zipkin_spans ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `id`) COMMENT 'ignore insert on duplicate'; ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`, `id`) COMMENT 'for joining with zipkin_annotations'; ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTracesByIds'; ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames'; ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering and range'; CREATE TABLE IF NOT EXISTS zipkin_annotations ( `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit', `trace_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.trace_id', `span_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.id', `a_key` VARCHAR(255) NOT NULL COMMENT 'BinaryAnnotation.key or Annotation.value if type == -1', `a_value` BLOB COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB', `a_type` INT NOT NULL COMMENT 'BinaryAnnotation.type() or -1 if Annotation', `a_timestamp` BIGINT COMMENT 'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp', `endpoint_ipv4` INT COMMENT 'Null when Binary/Annotation.endpoint is null', `endpoint_ipv6` BINARY(16) COMMENT 'Null when Binary/Annotation.endpoint is null, or no IPv6 address', `endpoint_port` SMALLINT COMMENT 'Null when Binary/Annotation.endpoint is null', `endpoint_service_name` VARCHAR(255) COMMENT 'Null when Binary/Annotation.endpoint is null' ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci; ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `span_id`, `a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate'; ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`, `span_id`) COMMENT 'for joining with zipkin_spans'; ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTraces/ByIds'; ALTER TABLE zipkin_annotations ADD INDEX(`endpoint_service_name`) COMMENT 'for getTraces and getServiceNames'; ALTER TABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT 'for getTraces'; ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces'; ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`, `a_key`) COMMENT 'for dependencies job'; CREATE TABLE IF NOT EXISTS zipkin_dependencies ( `day` DATE NOT NULL, `parent` VARCHAR(255) NOT NULL, `child` VARCHAR(255) NOT NULL, `call_count` BIGINT ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci; ALTER TABLE zipkin_dependencies ADD UNIQUE KEY(`day`, `parent`, `child`);
第2步: 在启动ZipKin Server的时候,指定数据保存的mysql的信息
java -jar zipkin-server-2.12.9-exec.jar --STORAGE_TYPE=mysql --
MYSQL_HOST=127.0.0.1 --MYSQL_TCP_PORT=3306 --MYSQL_DB=zipkin --MYSQL_USER=root --MYSQL_PASS=root
这里注意 要粘贴到一个文本中 让其在一行粘贴到命令窗口中,注意自己的数据库账号和密码 此处记得让自己的数据库能远程连接 否则会报错
之后我们在打开图形化界面可以发现几分钟前的依旧能显示,或者在数据库的表中也能显示
Skywalking是一个国产的开源框架,
2015年有吴晟个人开源,2017年加入Apache孵化器,国人开源的产品,主要开发人员来自于华为,
2019年4月17日Apache董事会批准SkyWalking成为顶级项目,
支持Java、.Net、NodeJs等探针,数据存储支持Mysql、Elasticsearch等,
跟Pinpoint一样采用字节码注入的方式实现代码的**无侵入**,
探针采集数据粒度粗,但性能表现优秀,且对云原生支持,目前增长势头强劲,社区活跃。
Skywalking是分布式系统的应用程序性能监视工具,专为微服务,
云原生架构和基于容器(Docker,K8S,Mesos)架构而设计,
它是一款优秀的APM(Application Performance Management)工具,
包括了分布式追踪,性能指标分析和服务依赖分析等。
1. Zipkin是Twitter开源的调用链路分析工具,目前基于Spingcloud sleuth得到了广泛的应用, 特点是轻量,部署简单。 2. Pinpoint一个韩国团队开源的产品,运用了字节码增强技术,只需要在启动时添加启动参数即可, 对代码无侵入。 目前支持Java和PHP语言,底层采用HBase来存储数据,探针收集的数据粒度非常细, 但性能损耗大,因其出现的时间较长,完成度也很高,应用的公司较多 3. Skywalking是本土开源的基于字节码注入的调用链路分析以及应用监控分析工具, 特点是支持多种插件,UI功能较强,接入端无代码侵入。 4. CAT是由国内美团点评开源的,基于Java语言开发, 目前提供Java、C/C++、Node.js、Python、Go等语言的客户端, 监控数据会全量统计,国内很多公司在用, 例如美团点评、携程、拼多多等,CAT跟下边要介绍的Zipkin都需要在应用程序中埋点, 对代码侵入性强。
SkyWalking 逻辑上分为四部分: 探针, 平台后端, 存储和用户界面。
Skywalking agent和业务端绑定在一起,负责收集各种监控数据
Skywalking oapservice是负责处理监控数据,接受agent的数据并存储在数据库中,
接受来自UI的请求,查询监控数据。
Skywalking UI提供给用户,展现各种监控数据和告警。
Skywalking目录结构
下载地址
https://skywalking.apache.org/
下载完之后解压 并启动
开发环境IDEA中使用探针配置即可集成使用agent,我们把 apache-skywalking-apm-bin
放到本地D:/Softwares
目录下,此时我们使用探针配置为3个项目分别配置agent:
# 客户端需要的jar所在位置
-javaagent:D:/softInstall/apache-skywalking-apm-bin/agent/skywalking-agent.jar
# 客户端服务名称
-Dskywalking.agent.service_name=springcloud-order
# skywalking地址
-Dskywalking.collector.backend_service=localhost:11800
三个微服务配置完成后并启动,并随意访问一个微服务,之后访问 localhost:8080/
效果如下:
持久化的效果就是可以在数据库中保存所有访问的微服务,如果不持久化,只要一启动skywalking就会像格式化一样 之前访问的微服务都没了
1.在config/application.yml 文件中修改
2.找一个数据库的jar包 和你的数据库版本适配
3.重启skywalking,并随意访问一个微服务 即可发现 swtest中有很多表 即持久化成功
1 Rocketbot-仪表盘
作用:查看被监控服务的运行状态。
监控面板:
1.1 APM
APM: 应用性能管理,通过各种探针采集数据,收集关键指标,
同时搭配数据呈现以实现对应用程序性能管理和故障管理的系统化解决方案。
1.1.1 Global全局维度
板块描述:
services load :服务每分钟请求数s1ow services:慢响应服务,单位ms
Un-Health services (Apdex) :Apdex性能指标,1为满分。
show Endpoints:慢响应端点,单面ms
G1obal Response Latency:百分比响应延时,不同百分比的延时时间,单位ms
G1obal Heatmap:服务响应时间热力分布图,根据时间段内不同响应时间的数量显示颜色深度
底部栏:展示数据的时间区间,点击可以调整
1.1.2 Service服务维度
属性描述:
service Apdex(数字):当前服务的评分
service Apdex(折线图):不同时间的Apdex评分
service Avg Response Times:平均响应延时,单位ms
G1obal Response Time Percentile:百分比响应延时
successfu1 Rate(数字):请求成功率
successfu1 Rate(折线图):不同时间的请求成功率
servce Load(数字):每分钟请求数
servce Load(折线图):不同时间的每分钟请求数
servce Instances Load:每个服务实例的每分钟请求数
show service Instance:每个服务实例的最大延时
service Instance successfu1 Rate:每个服务实例的请求成功率
1.1.3 Instance实例维度
service Instance Load:当前实例的每分钟请求数
service Instance successfu1 Rate:当前实例的请求成功率
service Instance Latency:当前实例的响应延时
vM CPu: jvm占用CPU的百分比
JVM Memory : VM内存占用大小,单位m
JVM GC Time : VM垃圾回收时间,包含YGC和oGC
JVM Gc count: JVM垃圾回收次数,包含YGC和oGC
JVM Thread count (ava service) : VM创建线程数量
CLR xx:类似VM虚拟机(可以直接理解成虚拟机)
1.1.4 Endpoint端点(API)维度
Endpoint Load in current service:每个端点的每分钟请求数
s1ow Endpoints in current service:每个端点的最慢请求时间,单位ms
successful Rate in current service:每个端点的请求成功率
Endpoint Load:当前端点每个时间段的请求数据
Endpoint Avg Response Time:当前端点每个时间段的请求行响应时间
Endpoint Response Time Percentile:当前端点每个时间段的响应时间占比
Endpoint successfu1 Rate:当前端点每个时间段的请求成功率
2 拓扑图
3 追踪
左侧:api接口列表,红色-异常请求,蓝色-正常请求
右侧:api追踪列表,api请求连接各端点的先后顺序和时间
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。