Spring Cloud学习笔记【黑马2024版】

avatar
作者
筋斗云
阅读量:11

文章目录

微服务介绍

单体架构:将所有的功能集中在一个项目中开发,打成一个包部署

优点: 	架构简单 	部署成本低 缺点: 	团队协作成本高 	系统发布效率低 	系统可用性差 

微服务架构:就是把单体服务中的功能模块拆分成多个独立项目

优点:粒度小、团队自治、服务自治 

Spring Cloud

是国内目前使用最广泛的微服务架构 

一、微服务拆分

1.拆分原则

  • 什么时候拆分?

    创业型项目:先采用单体架构,快速开发,快速试错。随着规模的扩大,主键拆分。     【例如】:我们是一个小公司,想到了一个点子,但是由于公司资金和试错能力有限,因此可以先开发一个单体项目,等到项目用户量上来了再拆分成微服务项目。      确定的大型项目:资金充足,目标明确,可以直接选择微服务架构,避免后续拆分的麻烦。    【例如】:某个大型的车企,想开发一个网上商城,因为这个车企已经有用户寄出了,所以可以直接开发微服务项目。 
  • 怎么拆?

    高内聚:每个微服务的职责要尽量单一,包含的业务相互关联度高、完整度高。 低耦合:每个微服务的功能要相对独立,尽量减少对其它微服务的依赖。 
  • 拆分方式

    纵向拆分:按照业务模块来拆分     【例如】:订单管理、菜品管理、用户管理 横向拆分:抽取公共服务,提高复用性     【例如】:记录风险操作日志的操作和发送短信的操作很多场景都需要用到,因此可以拆分出来,避免写过多的冗余代码 

2.拆分服务

2.1 微服务的两种工程结构

  • 独立Project:将每个模块拆分成一个Project,但是这些Project都放到同一个文件夹中【大型项目使用】

    以黑马商城为例,黑马商城包含以下几个模块:用户模块、商品模块、购物车模块、订单模块、支付模块。 由于模块分的很清楚,因此我们纵向拆分,按照模块将单体服务拆分成多个项目,每个项目都是一个project。如果每个项目都是一个Project,那么他们物理上就没有关联了,因此需要创建一个文件夹,将这些Project都放在同一个文件夹中,那么他们物理上就有联系了 
  • Maven聚合:大项目还是一个Project,但是它下面的功能模块变成了一个个Module,每个Module最后都是独立打包部署【小型项目使用】

2.2 拆分案例

案例:以Maven聚合的方式拆分黑马商城。

将hm-service中与商品管理相关功能拆分到一个微服务module中,命名为item-service
将hm-service中与购物车有关的功能拆分到一个微服务module中,命名为cart-service

  1. 在父工程hmall的基础上,创建一个Maven模块hm-service

  2. 给hm-service模块引入需要的依赖

  3. 创建模块的启动类

  4. 创建包结构

  5. 创建配置文件并编写

    1.启动端口不能冲突 2.设置spring.application.name名字【微服务名字,很重要】 3.修改数据库的配置 4.不需要的配置就删了 
  6. 将需要的类拷贝过来【Mapper、Service、Controller】

  7. 检查没有错误就可以启动子模块

3.出现的问题

问题说明:单体项目的时候,多个表的查询可能会用到多个service或者mapper,但是经过我们拆分之后,调用不到对应的service或者mapper,那么该怎么解决呢?

解决办法:一个服务向另一个服务发起网络请求。看下面的服务治理

二、服务治理

服务治理分为:服务注册、服务发现 

1.远程调用的问题

远程调用的代码是我们提前写好的,但是等到项目上线的时候被调用的服务可能会因为接口压力过大,集群部署,我们的代码只会调用其中的一个服务,不能实现负载均衡。

如果我们指定的服务器挂历了,怎么办?这些都是使用HttpClient等客户端存在的问题。

2.注册中心原理

我们找工作的时候,不知道哪个公司需要招聘程序员。外包公司也不知道哪些人想要当程序员。于是我们和外包公司就将信息都报到中介公司,中介公司就会给我们推荐想要的人选。

注册中心就是上面的原理。每一个服务会将自己的信息都注册到注册信息,注册信息会有一个表维护这些信息。如果我们需要找某个服务的信息,注册中心就会将那个服务的信息返回给我们,我们在这些信息中随机选择一个(负载均衡)。

注册中心会和服务保持通讯,以保证服务是否存活,如果长时间没有通讯,会将服务从注册中心剔除。

image-20240512161253308

服务治理中的三个角色: 	服务提供者:暴露服务接口,供其它服务调用 	服务消费者:调用其它服务提供的接口 	注册中心:记录并监控微服务各实例状态,推送服务变更信息 
消费者如何知道提供者的地址? 	服务提供者会在启动时注册自己信息到注册中心,消费者可以从注册中心订阅和拉取服务信息 
消费者如何得知服务状态变更? 	服务提供者通过心跳机制向注册中心报告自己的健康状态,当心跳异常时注册中心会将异常服务剔除,并通知订阅了该服务的消费者 
当提供者有多个实例时,消费者该选择哪一个? 	消费者可以通过负载均衡算法,从多个实例中选择一个 

3.Nacos注册中心

Nacos是目前国内企业中占比最多的注册中心组件。它是阿里巴巴的产品,目前已经加入SpringCloudAlibaba中。

  1. 数据库中创建Nacos使用到的库,将他的表创建出来【有SQL脚本】

  2. 准备好Nacos的配置文件【里面指定数据库的类型、数据库地址、数据库名、数据库端口、数据库用户名、数据库密码】

    PREFER_HOST_MODE=hostname MODE=standalone SPRING_DATASOURCE_PLATFORM=mysql MYSQL_SERVICE_HOST=192.168.150.101 MYSQL_SERVICE_DB_NAME=nacos MYSQL_SERVICE_PORT=3306 MYSQL_SERVICE_USER=root MYSQL_SERVICE_PASSWORD=123 MYSQL_SERVICE_DB_PARAM=characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai 
  3. 将配置文件上传到服务器,创建Nacos的docker容器

    docker run -d \ --name nacos \ --env-file ./nacos/custom.env \ -p 8848:8848 \ -p 9848:9848 \ -p 9849:9849 \ --network hm-net \ --restart=always \ nacos/nacos-server:v2.1.0-slim 
  4. 可以通过如下访问

    ip地址:端口号/nacos 用户名/密码:nacos 

4.服务注册

服务注册:服务启动的时候,都应该将信息注册到注册中心里面

  1. 引入Nacos的依赖

    <!--nacos 服务注册发现--> <dependency>     <groupId>com.alibaba.cloud</groupId>     <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> 
  2. 配置Nacos地址

    spring:   application:     name: item-service # 服务名称   cloud:     nacos:       server-addr: 192.168.150.101:8848 # nacos地址 
  3. 启动服务即可

5.服务发现

服务发现:服务的调用者在调用服务的时候,应该在注册中心拉取服务列表

  1. 引入依赖

    <!--nacos 服务注册发现--> <dependency>     <groupId>com.alibaba.cloud</groupId>     <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> 
  2. 配置Nacos地址

    spring:   cloud:     nacos:       server-addr: 192.168.150.101:8848 
  3. 拉取服务列表

    //1.注入对象【SpringCloud的规范就是这个】 @Autowired private DiscoveryClient discoveryClient; //2.在具体的方法中根据实例的id获取服务的实例 List<ServiceInstance> instances=discoveryClient.getInstances("item-service")//nacos的实例id默认是服务的名称 //3.判断集合是否为空 if(CollUtils.isEmpyt(instances)){     return; } //4.不为空,根据负载均衡选一个实例 ServiceInstance instance=instances.get(RandomUtils.randomInt(instances.size())) //5.然后调用HttpClient等客户端发送方法【instance的getUri方法可以获取ip地址和端口号】 

6.OpenFeign

问题:上面的服务发现代码太复杂了,我们单体服务只用写一行代码,到了微服务就要写这么多行代码吗?

解答:OpenFeign可以用来解决这个问题,他是一个声明式的Http客户端,可以让我们更简单的写Http请求

6.1 快速入门

  1. 引入依赖【两个】

      <!--openFeign-->   <dependency>       <groupId>org.springframework.cloud</groupId>       <artifactId>spring-cloud-starter-openfeign</artifactId>   </dependency>   <!--负载均衡器-->   <dependency>       <groupId>org.springframework.cloud</groupId>       <artifactId>spring-cloud-starter-loadbalancer</artifactId>   </dependency> 
  2. 启动类加上@EnableFeignClients注解,启动OpenFeign功能

    //如果FeignClient不在SpringBootApplication的扫描范围内可以在@EnableFeignClients中指定扫描范围 @EnableFeignClients(basePackages="come.hmall.api.clients") 

    image-20240512165808536

  3. 编写接口【就代替了原本一大堆的代码】

    //@FeignClient注解告诉java,这就是一个Feign的客户端 //value属性就说明以后拉取的是对应id的服务 //里面默认使用了负载均衡算法 @FeignClient("item-service") public interface ItemClient {      //这个请求路径【请求的端口号和ip在负载均衡那里已经自动获取了】     @GetMapping("/items")     //请求返回的类型会根据反射自动装配到我们的返回值中     //@RequestParam是请求的参数     List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids); } 
  4. 在需要发送请求的地方调用接口即可

    //注入对象 @AutoWired private ItemClient itemClient;  //需要的地方调用方法 List<ItemDTO> items=itemClient.queryItemByIds(); 

6.2 连接池

OpenFeign允许指定连接方式,但是默认方式由于使用jdk的远程Client,所以不支持连接池。【HttpClient和OkHttp都支持连接池】

  1. 引入feign对OKHttp的支持的依赖

    <!--OK http 的依赖 --> <dependency>   <groupId>io.github.openfeign</groupId>   <artifactId>feign-okhttp</artifactId> </dependency> 
  2. 配置文件中开启连接池

    feign:   okhttp:     enabled: true # 开启OKHttp功能 

6.3 日志输出【多数情况不需要开启】

OpenFeign需要输出日志需要符合两个条件:

  1. FeignClient所在的包日志级别为debug
  2. Feign的日志级别在NONE以上
Feign的日志级别: 	NONE:不记录任何日志信息,这是默认值。 	BASIC:仅记录请求的方法,URL以及响应状态码和执行时间 	HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息 	FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。 
  1. 定义一个类定义Feign的日志级别

    public class DefaultFeignConfig {     @Bean     public Logger.Level feignLogLevel(){         return Logger.Level.FULL;//日志级别     } } 
  2. 如果需要局部配置,则在对应的FeignClient中配置configuration属性

    @FeignClient(value = "item-service", configuration = DefaultFeignConfig.class) 
  3. 如果需要全局配置,则在启动类的EnableFeignClients,添加defaultConfiguration属性

    @EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class) 

6.4 最佳实践【两个方案】

我们刚才只是在一个服务中使用OpenFeign发送了请求。但是这个请求在很多服务中都能使用到,如果每次都写一边,会很麻烦。我们能否把这个请求抽象成一个公共的呢?

  • 方案一:模块拆分

    image-20240512172937058

    1.将一个模块抽象成三个子模块,一个模块专门存放Feign的API 2.其他的模块需要发送Feign请求的时候直接引入对应模块的依赖 
  • 方案二:再写一个模块

    image-20240512173408646

    1.创建一个模块,和其他模块同级别,这个模块只存放FeignAPI 2.其他接口需要调用的时候直接引入模块API 

三、远程的调用

四、请求路由

身份认证

配置管理

服务保护

分布式事务

异步通信

消息可靠性

延迟消息

分布式搜索

倒排索引

数据聚合

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!