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

微服务介绍

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

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

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

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

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
    

三、远程的调用

四、请求路由

身份认证

配置管理

服务保护

分布式事务

异步通信

消息可靠性

延迟消息

分布式搜索

倒排索引

数据聚合

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值