一、微服务
1.1 微服务技术栈
- 服务集群:将单独的每一个功能,独立开发,独立部署,最后形成一个单独的服务,一旦这样的服务越来越多,就会形成一个服务集群。
注册中西:记录每个服务的ip、端口、以及每个服务的实现功能,多个单体服务之间的信息直接从注册中心进行拉取。
配置中心:当服务集群越来越多,就需要到配置中心,当需要修改某个服务时,只需要到配置中心进行修改即可,一旦配置中心更新,就会通知对应的单体服务进行热更新。
服务网关:可以对用户进行身份校验,统一的请求路由,然后在服务器上查询数据库,查到则返回给用户
分布式缓存:一种将数据存储在内存中的缓存技术,它可以存储一些用户经常需要用的数据,以提高访问速度。
分布式搜索:将搜索服务拆分成多个服务,将搜索结果集合并后返回给客户端,从而满足大量查询和高并发请求的需求。
消息队列:是分布式系统中常见的数据结构,它可以存储和转发消息,处理服务间的异步通信。因为微服务通常是独立部署的小型服务,它们需要通过消息队列来进行通信。
例如,一个服务可能需要将某些任务交给另一个服务来处理。在这种情况下,它可以将任务发送到消息队列中,并且另一个服务可以异步地从队列中消费这些任务
使用消息队列好处,包括解耦、异步和削峰。
消息队列可以用于解耦服务间的通信,使得服务能够独立进化。
通过异步通信,一个服务不需要等待另一个服务完成它的工作,从而提高了整个系统的响应速度和吞吐量。
对于请求流量的突发性增加,消息队列还可以用于削峰,使得系统能够更好地处理请求。
分布式日志服务:将分散在不同服务中产生的日志(运行日志、报错日志等)收集到一个中心化的地方进行存储和管理,以便于应用开发人员快速检索和定位问题。
系统监控链路追踪:针对分布式系统下的调用链路进行的监控。它能够跟踪系统中各个服务之间的调用,记录下每个服务的处理时间、请求状态,以及请求具体到达哪台机器上等信息。通过链路追踪,我们可以更好地了解整个服务调用的流程,快速定位问题,并对服务进行优化。链路追踪工具有很多,如Zipkin、Skywalking等。
Jenkins是一个开源的自动化服务器,它可以帮助软件开发人员自动化构建、测试和部署等与软件开发相关的流程,从而实现持续集成和持续交付。它是一个基于服务器的系统,可以运行在像Apache Tomcat这样的servlet容器中。
1.3 微服务技术栈分类
二、微服务架构
2.1 微服务架构的演变
2.1.1 单体架构
所有功能集中在一个项目种开发,打包部署
优点:
- 架构简单
- 部署成本低
缺点:
- 耦合度高,模块关联太多
2.1.2 分布式架构
根据业务功能对系统进行拆分,每个业务模块单独开发
优点:
- 降低服务耦合
- 有利于服务升级扩展
缺点:
- 服务拆分粒度如何?
- 服务集群地址如何维护?
- 服务之间如何实现远程调用?
- 服务健康状态如何感知?
2.1.3 微服务
分布式架构的优化设计方案
- 单一职责:拆分粒度更小,每一个服务对应唯一的业务能力
- 面向服务:微服务对外的业务接口
- 自治:团队独立、技术独立、数据独立、部署独立
- 隔离性强:服务调用做好隔离、容错、降级、避免出现级联问题
- 架构赋值、运维、监控、部署难度提高
2.2 微服务技术对比
2.2.1 技术选择
三、Springcloud
SpringCloud集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配
SpringCloud与Springboot版本兼容
3.1 服务拆分及远程调用
3.1.1 拆分原则
- 不同微服务,不要重复开发相同业务
- 微服务数据独立,不要访问到其他微服务的数据库
- 暴露自己的业务接口,让其他微服务调用
3.1.2 案例说明
项目结构:
将demo跑起来
3.2 拆分总结
- 微服务需要根据业务模块拆分,做到单一职责,不要重复开发相同业务
- 微服务可以将业务暴露为接口,供其它微服务使用
- 不同微服务都应该有自己独立的数据库
3.3 案例:根据订单id查询订单功能
需求:根据订单id查询订单的同时,把订单所属的用户信息一起返回
目前情况:
- 订单信息只能查询到订单数据库信息
- 用户信息只能查询到用户数据库信息
- 两者之间毫无关联
3.4 远程调用方式
使用远程调用方式查询到用户信息
可以使用RestTemplate
发送请求, RestTemplate
是发送各种HTTP请求,那么就可以使用 RestTemplate
发起远程http请求:
在order_service
模块中的启动配置类中:
/** * 在启动配置类注入 RestTemplate bean容器 * @return */ @Bean public RestTemplate restTemplate() { return new RestTemplate(); }
修改查询实现方法:
@Autowired private RestTemplate restTemplate; public Order queryOrderById(Long orderId) { // 1.查询订单 Order order = orderMapper.findById(orderId); // 2利用RestTemplate发送Http请求,查询用户 // 2.1 url的地址为user-service模块启动的端口,因为是向user-service发送请求 String url = "http://localhost:8081/user/" + order.getUserId(); // 发送请求,完成远程调用 User user = restTemplate.getForObject(url, User.class); // 封装user order.setUser(user); // 4.返回 return order; } }
3.4.1 消费者与提供者
提供者:一次业务种,被其他微服务调用的服务 (提供接口给其他微服务,接口被调用了)
消费者:一次业务种,调用其他微服务的服务(调用其他微服务提供的接口,调用别人的接口)
上面的demo中,user就是服务提供者(接口被调用),order就是服务消费者(调用了别人的接口)
思考:服务A调用服务B,服务B调用服务C,那么服务B是什么角色?主要是要看怎么调用
- 如果是服务B调用了服务C,那么服务B就是服务消费者
- 如果是服务B被服务A调用了,那么服务B就是服务提供者
3.5 总结
服务调用关系
- 服务提供者:暴露接口给其它微服务调用
- 服务消费者:调用其它微服务提供的接口
- 提供者与消费者角色其实是相对的
- 一个服务可以同时是服务提供者和服务消费者
四、Eureka
4.1 demo案例存在调用服务问题
- 服务消费者该如何获取服务提供者的地址信息?
- 如果有多个服务提供者,消费者该如何选择?
- 消费者如何得知服务提供者的健康状态?
4.2 Eureka注册中心
4.2.1 Eureka的作用
服务注册和发现:Eureka Server用于接收各个微服务实例的注册信息,并存储在自己的注册表中。Eureka Client用于将自身服务实例注册到注册表中,并从注册表中获取其他微服务实例的信息。这样,当一个微服务需要调用其他微服务时,只需要从注册表中获取对应的微服务实例的地址即可。
负载均衡:Eureka Client内置了一个轮询负载均衡器,每次从注册表中获取所有同名服务实例的地址列表,通过轮询算法选择其中一个地址进行调用。这样,就能够实现基于客户端的负载均衡。
举例说明:假设在一个电商网站中,商品微服务需要调用支付微服务进行交易。如果没有Eureka,商品微服务将无法直接找到支付微服务的实例地址。但是,如果使用了Eureka,支付微服务实例会将自己的信息注册到Eureka Server的注册表中,商品微服务在调用支付微服务时,只需要从注册表中获取支付微服务实例的地址列表,然后通过轮询算法选择其中一个进行调用,就能够顺利地完成交易操作。
以下是对上面的demo进行分析:
再来回答上面第一部分的遗留问题:
消费者该如何获取服务提供者具体信息?
服务提供者启动时向eureka注册自己的信息
eureka保存这些信息
消费者根据服务名称向eureka拉取提供者信息
如果有多个服务提供者,消费者该如何选择?
服务消费者利用负载均衡算法,从服务列表中挑选一个
消费者如何感知服务提供者健康状态?
服务提供者会每隔30秒向EurekaServer发送心跳请求,报告健康状态
eureka会更新记录服务列表信息,心跳不正常会被剔除
消费者就可以拉取到最新的信息
4.3 Euraka注册中心总结
在Eureka架构中,微服务角色有两类:
EurekaServer:服务端,注册中心
记录服务信息
心跳监控
EurekaClient:客户端
Provider:服务提供者,例如案例中的 user-service
注册自己的信息到EurekaServer
每隔30秒向EurekaServer发送心跳
consumer:服务消费者,例如案例中的 order-service
根据服务名称从EurekaServer拉取服务列表
基于服务列表做负载均衡,选中一个微服务后发起远程调用
4.4 Eureka 搭建
4.4.1 搭建Eureka注册中心
- 创建一个单独的 Eureka 项目
- 导入组件:
<!--eureka服务端--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
- 添加Eureka注解
@EnableEurekaServer
@EnableEurekaServer @SpringBootApplication public class EurekaApplication { public static void main(String[] args) { SpringApplication.run(EurekaApplication.class, args); } }
- 配置信息
server: port: 8083 # 服务端口 spring: application: name: eurekaserver # eureka的服务名称 eureka: client: service-url: # eureka的地址信息 defaultZone: http://localhost:8083/eureka
4.4.2 服务注册(user-server)
- 引入pom.xml
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
配置文件种,引入客户端配置
eureka: client: service-url: # eureka的地址信息 defaultZone: http://127.0.0.1:10086/eureka
将一个服务启动多次(如下图所示),但是端口不能一样
4.4.3 服务发现
在order-service完成服务拉取
服务拉取是基于服务名称获取服务列表,然后在对服务列表做负载均衡
- 修改OrderService的代码,修改访问的url路径,用服务名代替ip、端口:
// 2.1 url的地址为user-service模块启动的端口,因为是向user-service发送请求 String url = "http://userserver/user/" + order.getUserId();
- 在order-service项目的启动类OrderApplication中的RestTemplate添加负载均衡注解:
@Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); }
然后访问 order 服务订单:
http://localhost:8082/order/102
五、Ribbon 负载均衡
5.1 负载均衡流程
可以使用之前的demo,说明负载均衡流程:
5.2 负载均衡策略
IRule接口:
5.3 修改负载均衡规则
通过定义IRule实现可以修改负载均衡规则,有两种方式:
- 代码方式:在order-service中的OrderApplication类中,定义一个新的IRule:
@Bean public IRule randomRule(){ return new RandomRule(); }
配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改规则
userserver: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
5.4 饥饿机制
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。而饥饿加载则会在项目启动时创建,降低第一次访问的耗时。
通过下面配置开启饥饿加载:(在order-service中)
5.5 负载均衡总结
Ribbon负载均衡规则
规则接口是IRule
默认实现是ZoneAvoidanceRule,根据zone选择服务列表,然后轮询
负载均衡自定义方式
代码方式:配置灵活,但修改时需要重新打包发布
配置方式:直观,方便,无需重新打包发布,但是无法做全局配置
饥饿加载
开启饥饿加载
指定饥饿加载的微服务名称
六、Nacos注册中心
Nacos是阿里巴巴的产品,现在是SpringCloud中的一个组件。相比Eureka功能更加丰富,在国内受欢迎程度较高。
6.1 Windows11安装Nacos
下载地址:NacosGithub (不建议安装最新版本)
启动方式:
- 单机启动:
startup.cmd -m standalone
集群启动,在bin目录下启动多个
startup.cmd
运行效果如下:点击链接会跳转到对应的界面
6.2 Nacos 服务注册
- 在cloud-demo父工程中添加spring-cloud-alilbaba的管理依赖
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba</artifactId> <version>2.2.5.RELEASE</version> </dependency>
在user-service工程下导入配置文件
<!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-discovery --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>2.1.2.RELEASE</version> </dependency>
修改配置文件
spring: datasource: url: jdbc:mysql://localhost:3306/cloud_user?useSSL=false username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver application: name: userserver # user服务名称 cloud: nacos: server-addr: localhost:8848 # nacos服务地址
6.3 Nacos服务分级存储模型
将一个服务分级存储部署
6.3.1 服务跨集群调用
- 服务调用尽可能选择本地集群的服务,跨集群调用延迟较高
- 本地集群不可访问时,再去访问其它集群
6.4 服务集群配置
增加服务集群配置,name随便命名:
在nacos控制台就会看到两个集群的地方
6.4.1 Nacos服务分级总结
Nacos服务分级存储模型
一级是服务,例如userservice
二级是集群,例如GX或BJ
三级是实例,例如GX机房的某台部署了userservice的服务器
如何设置实例的集群属性
修改application.yml文件,添加spring.cloud.nacos.discovery.cluster-name属性即可
6.5 Nacos负载均衡
将以上的配置进行配置之后,就会优先选择本地集群查询,本地没有才回去访问其他的服务集群
① 优先选择同集群服务实例列表
② 本地集群找不到提供者,才去其它集群寻找,并且会报警告
③ 确定了可用实例列表后,再采用随机负载均衡挑选实例