springcloud微服务框架学习笔记

springcloud

day-01

1. 微服务技术栈

在这里插入图片描述

2. 初识微服务框架

2.1 微服务架构的演变

  • 单体架构
    • 将业务的所有功能集中在一个项目中开发,打成一个包部署
    • 优点:
      • 架构简单
      • 部署成本低
    • 缺点:
      • 耦合度高
      • 扩展性差
  • 分布式架构:根据业务功能对系统进行拆分,每个业务模块作为独立项目开发,称为一个服务
    • 优点:
      • 降低服务耦合
      • 有利于服务升级拓展
    • 缺点:
      • 架构复杂
      • 难度大
  • 微服务:是一种经过良好架构设计的分布式架构方案
    • 优点:
      • 拆分粒度更小
      • 服务更独立
      • 耦合度更低
    • 缺点:
      • 架构非常复杂
      • 运维、监控、部署难度提高
    • 特征:
      • 单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责,避免重复业务开发
      • 面向服务:微服务对外暴露业务接口
      • 自治:团队独立、技术独立、数据独立、部署独立
      • 隔离性强:服务调用做好隔离、容错、降级,避免出现级联问题

2.2 微服务技术对比

在这里插入图片描述


day-02

1. Eureka

提供者与消费者

  • 提供者
    • 一次业务中,被其他为服务调用的服务。(提供接口给其他微服务)
  • 消费者
    • 一次业务中,调用其他微服务的服务。(调用其他微服务提供的接口)
  • 一个服务既可以是提供者也可以是消费者
    • 比如 服务A调用服务B,服务B调用服务C

服务调用出现的问题

案例中,通过RestTemplete,发起http请求得到user数据,然后在订单模块显示出来,完成服务间的调用,但是出现一个问题,就是在地址编写的时候使用的是硬编码,即

    public Order queryOrderById(Long orderId) {
        // 1.查询订单
        Order order = orderMapper.findById(orderId);
//        2.利用restTemplate发起http请求
        String url = "http://localhost:8081/user/" + order.getUserId();
        User user = restTemplate.getForObject(url, User.class);
//      3. 封装user到Oreder
        order.setUser(user);
        // 4.返回
        return order;
    }

意味着,要对其他服务发起请求,就需要更改代码,并且不能保证那台服务的健康性

  • 服务消费者该如何获取服务提供者的地址信息?
    • 服务提供者启动时向eureka注册自己的信息
    • eureka保存这些信息
    • 消费者根据服务名称向eureka拉取提供者信息
  • 如果有多个服务提供者,消费者该如何选择?
    • 服务消费者利用负载均衡算法,从服务列表中挑选一个
  • 消费者如何得知服务提供者的健康状况?
    • 服务提供者会每隔30秒向EurekaServer发送心跳请求,报告健康状态
    • eureka会更新记录服务列表信息,信条不正常会被踢出
    • 消费者就可以拉取到最新的信息

在Eureka架构中,微服务角色有两类

  • EurekaServer:服务端(也被称为注册中心)
    • 记录服务信息
    • 心跳监控
  • EurekaClient:客户端
    • provider:服务提供者
      • 注册自己的信息到EurekaServer
      • 每隔30秒向注册中心发送心跳
    • consumer:服务消费者
      • 根据服务名称从注册中心中拉取服务列表
      • 基于服务列表做负载均衡,选中一个微服务后发起远程调用

2. 搭建Eureka

2.1 搭建环境

搭建Eureka的三大步骤

  • 引入eureka-server依赖
  • 添加@EnableEurekaServer注解
  • 在application.yml中配置Eureka地址

引入依赖

 		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

添加注解

添加到springBoot的启动类上

@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class,args);
    }
}

配置地址

server:
  port: 12345  #服务端口
spring:
  application:
    name: eurekaserver #eureka的服务名称
eureka:
  client:
    service-url: # eureka的地址信息
      defaultZone: http://127.0.0.1:12345/eureka

注:eureka注册中心,在启动时,也会对自己进行注册

在这里插入图片描述

2.2 服务注册

引入依赖

注意当前依赖为client

<!--        引入eureka的client依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

配置yml文件

配置当前服务的服务名称 配置注册中心的地址信息

spring:
  application:
    name: userservice #user服务的服务名称
eureka:
  client:
    service-url: # eureka的地址信息
      defaultZone: http://127.0.0.1:12345/eureka

注册成功后的注册中心控制台

在这里插入图片描述

2.3 服务发现
  • 修改代码,根据服务名称来访问
  • 添加注解,实现负载均衡
//服务名称为userService
String url = "http://userservice/user/" + order.getUserId();
@Bean
    @LoadBalanced   //负载均衡
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

3. Ribbon负载均衡

流程

在这里插入图片描述

http://userservice/user并不是一个浏览器能够访问的真实地址,因此需要一个组件去进行解析,也就是Ribbon组件,当请求进入时,去注册中心去找到对应的服务名称,然后再分配到对应的服务商

通过源码可知,最后有IRule接口决定负载均衡策略

在这里插入图片描述

总结:

  • Ribbon负载均衡规则
    • 规则接口是IRule
    • 默认实现是ZoneAvoidanceRule,根据zone选择服务列表,然后轮训
      • 规则可言进行改变
  • 负载均衡自定义方式
    • 代码方式:配置灵活,但修改时需要重新打包发布
    • 配置方式: 资管,方便,无序==无需重新打包发布,但是无法做全局配置
  • 饥饿加载
    • 开启饥饿加载
    • 指定饥饿加载的微服务名称

day-03

1. NACOS注册中心

1.1 NACOS的下载和安装

gitHub地址:https://github.com/alibaba/nacos/tags

选择对应的安装包,以1.4.1为例

在这里插入图片描述

在这里插入图片描述

下载好后,解压到一个没有中文的路径下

解压好的nacos结构如下:

在这里插入图片描述

1.2 NACOS配置

三个文件夹中的内容:

  • bin
    • 启动和关闭的命令
  • conf
    • peoperties配置文件,可以配置端口,默认为8848
  • target
    • NACOS的可执行jar包
1.4 NACOS启动

在文件目录下输入cmd命令进入控制台

在这里插入图片描述

startup.cmd -m standalone
  • 其中m代表的是模式 当前启动命令表示单机启动

启动成功:

在这里插入图片描述

可以通过显示的路径访问网页,用户名、密码都是nacos

在这里插入图片描述

2. NACOS使用

2.1 服务注册到NACOSS
  • 引入父工程的管理依赖
<!--            NACOS管理依赖-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.5.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

  • 在user-service模块中引入nacos依赖
       <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
  • 修改yml文件
    • nacos的配置属于spring配置,需要把之前eurka的配置注释掉
server:
  port: 8081
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloud_user?useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
  application:
    name: userservice #user服务的服务名称
  cloud:
    nacos:
      server-addr: localhost:8848  #nacos服务地址  默认就是8848
#eureka:
#  client:
#    service-url: # eureka的地址信息
#      defaultZone: http://127.0.0.1:12345/eureka
mybatis:
  type-aliases-package: cn.itcast.user.pojo
  configuration:
    map-underscore-to-camel-case: true
logging:
  level:
    cn.itcast: debug
  pattern:
    dateformat: MM-dd HH:mm:ss:SSS
  • 启动成功,可以在nacos的服务列表中找到注册的服务

在这里插入图片描述

3. NACOS服务分级存储模型

在这里插入图片描述

  • 服务跨集群调用问题
    • 服务调用尽可能的选择本地集群的服务
      • 跨集群调用延迟较高
    • 本地集群不可访问时,再去访问其他集群

在yml文件中进行配置

#在spring下面添加
discovery:
        cluster-name: HZ  #集群名称

具体配置如下

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloud_user?useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
  application:
    name: userservice #user服务的服务名称
  cloud:
    nacos:
      server-addr: localhost:8848  #nacos服务地址
      discovery:
        cluster-name: HZ  #集群名称

然后进行启动,选中哪两个服务,启动后就变成哪个集群下

  • 当前先启动两个为杭州集群 HZ,后修改yaml文件,在启动另外一个集群为上海集群

在这里插入图片描述
在这里插入图片描述

4. 权重配置

可以在nacos控制台对不同的服务之间,配置权重,权重越大,访问到的概率就会越高

5. 环境隔离

Nacos中服务存储和数据存储的最外层都是一个名为namespace的东西,用来做最外层隔离

在这里插入图片描述

创建方式:

  • 控制台创建命名空间

    • 在这里插入图片描述
  • 修改yml文件

    • 在这里插入图片描述

    • 注意只能使用命名空间的ID

注意:

  • namespace用来做环境隔离
  • 每个namespace都有唯一的id
  • 不同的namespace下的服务不可见
    • 也就是说,不同的命名空间下不能进行访问

6. Nacos和Eureka的区别

在这里插入图片描述

day-04

1. http客户端Feign

1.1 Feign替代RestTemplate

RestTemplate方式调用存在的问题

发起远程调用的代码

String url = " http://userservice/user/" + order.getUserId();
User user = restTemplate.getForObject(url,User,class)
  • 存在两个问题
    • 代码可读性差
    • 多参数情况下很难去维护

Feign是一个声明式的http客户端,官方地址:https:github.com/OpenFeign/feign

其作用就是帮助我们优雅的实现实现http请求的发送,解决上面提到的问题

使用Feign的步骤如下:·

  • 引入依赖

    • <!--        添加一个feign的客户端-->
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-openfeign</artifactId>
              </dependency>
      
  • 在order-service的启动类中添加注解开启Feign的功能

    • @MapperScan("cn.itcast.order.mapper")
      @SpringBootApplication
      @EnableFeignClients
      public class OrderApplication {
      
          public static void main(String[] args) {
              SpringApplication.run(OrderApplication.class, args);
          }
      
          @Bean
          @LoadBalanced   //负载均衡
          public RestTemplate restTemplate() {
              return new RestTemplate();
          }
      }
      
  • 编写Feign客户端

    • 主要是基于SpringMVC的注解来声明远程调用的信息,比如:
      • 服务名称:userservice
      • 请求方式:GET
      • 请求路径:/user/{id}
      • 请求参数:Long id
      • 返回值类型:User
@FeignClient("userservice")
public interface UserClient {
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}
1.2 自定义配置

Feign运行自定义配置来覆盖默认配置,可以修改的配置如下:

在这里插入图片描述

一般我们需要配置的是日志级别

1.3 Feign使用优化
  • Feign底层的客户端实现:
    • URLConnection:默认实现,不支持连接池
    • Apache HttpClient:支持连接池
    • OKHttp:支持连接池
  • 因此优化Feign的性能主要包括:
    • 使用连接池代替默认的URLConnection
    • 日志级别,最好用basic或none
1.4 最佳实践
1.4.1 方式一(继承)
  • 给消费者的FeignClient和提供者的controller定义统一的父接口作为标准

    • 发送请求和接收请求的方法必须要一致才能够正常请求
    • 因此可以定义一个统一的接口来处理
    • 在这里插入图片描述
  • 但是spring不推荐这种方案,并且springMVC不支持这种方式

1.4.2 方式二(抽取)

将FeignClient抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用

  • 在这里插入图片描述

  • 实现步骤如下:

    • 首先创建一个module,命名为feign-api,然后引入feign的starter依赖

    • 将order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中

      • 在这里插入图片描述
    • 在order-service中引入feign-api的依赖

      • <!--        引入feign的api-->
                <dependency>
                    <groupId>cn.itcast.demo</groupId>
                    <artifactId>feign-api</artifactId>
                    <version>1.0</version>
                </dependency>
        
    • 修改order-service中的所有与上述三个组件有关的import部分,改成导入feign-api中的包

  • 但是单独抽取一个模块后,会导致UserClient无法注入,有两种方式解决

    • 方法一:指定FeignClient所在包

      • @EnableFeignClients(basePackages = "cn.itcast.feign.clients")
        
    • 方法二:指定FeignClient字节码

      • @EnableFeignClients(clients = {UserClient.class})
        

2. 统一网关Gateway

  • 网关功能:
    • 身份认证和权限校验
    • 服务路由、负载均衡
    • 请求限流
  • 技术实现
    • gateway(近几个版本才出现)
    • zuul(一直存在)
      • Zuul是基于Servlet的实现,属于阻塞式编程。而SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具有更好的性能
2.1 搭建网关
  • 搭建网关服务的步骤:

    • 创建新的module,引入SpringCloudGateway的依赖和nacos的服务发现依赖

    • <!--        网关信息-->
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-gateway</artifactId>
              </dependency>
      <!--        nacos服务发现依赖-->
              <dependency>
                  <groupId>com.alibaba.cloud</groupId>
                  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
              </dependency>
      
  • 编写路由配置及nacos地址

    • server: 
        port: 10010 #网关端口
      spring:
        application:
          name: gateway #服务名称
        cloud: 
          nacos:
            server-addr: localhost:8848 #nacos地址
          gateway:
            routes: #网关路由配置
              - id: user-service #路由id,自定义,保证唯一即可
                uri: lb://userservice #路由的目标地址  lb就是负载均衡的缩写  后面跟服务名称
                predicates: #路由断言,也就是判断请求是否符合路由规则的条件
                  - Path=/user/** #按照路径匹配
      
    • 注意事项

      • 路由id:必须是唯一的,它是路由的唯一标识
      • 路由目标(url):路由的目标地址,http代表固定地址,lb代表根据服务名负载均衡
  • 编写启动类

2.2 路由断言工程Route Predicate Factory

网关路由可以配置的内容包括:

  • 路由id:路由唯一标识
  • uri:路由目的地,支持lb和http两种
  • predicates:路由断言,判断请求是否符合要求,符合则转发到路由目的地
    • 我们之前在配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理,转变为路由判断的条件
  • filters:路由过滤器,处理请求或相应

Spring提供了11种基本的Predicate工厂:

在这里插入图片描述

2.3 全局过滤器
  • 普通过滤器
    • 配置后只对当前的模块生效
  • 默认过滤器
    • 配置后对所有的模块生效

但是这两种过滤器都存在一个问题,都是由配置文件来完成的,如果想做逻辑处理,就需要使用到全局过滤器来实现

@Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter {

    /**
     * 采用的并不是像以前封装servlet的过滤器,并没有servlet的api方法
     *
     * */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//        1. 获取请求参数
        ServerHttpRequest request = exchange.getRequest();
        MultiValueMap<String, String> queryParams = request.getQueryParams();
//        2. 获取参数中的authorization参数
        String auth = queryParams.getFirst("authorization");
//        3. 判断参数值
        if ("admin".equals(auth)) {
//           3.1 满足 放行
            return chain.filter(exchange);
        }
//           3.2 不满足 拦截
//             3.2.1 设置状态码
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
//             3.2.2 拦截请求
        return exchange.getResponse().setComplete();
    }
}
  • order是用来指定执行顺序的,数值越小,优先级越高

    • 也可以通过实现接口来指定优先级

      • @Component
        public class AuthorizeFilter implements GlobalFilter, Ordered {
        
            /**
             * 采用的并不是像以前封装servlet的过滤器,并没有servlet的api方法
             *
             * */
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //        1. 获取请求参数
                ServerHttpRequest request = exchange.getRequest();
                MultiValueMap<String, String> queryParams = request.getQueryParams();
        //        2. 获取参数中的authorization参数
                String auth = queryParams.getFirst("authorization");
        //        3. 判断参数值
                if ("admin".equals(auth)) {
        //           3.1 满足 放行
                    return chain.filter(exchange);
                }
        //           3.2 不满足 拦截
        //             3.2.1 设置状态码
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        //             3.2.2 拦截请求
                return exchange.getResponse().setComplete();
            }
        
            @Override
            public int getOrder() {
                return -1;
            }
        }
        
        
  • 并且需要把过滤器注册为bean交给spring管理

2.4 过滤器链执行顺序

请求进入网关后会碰到三类过滤器:当前的路由过滤器、DefaultFilter、GlobalFilter

请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器

在这里插入图片描述

  • 每一个过滤器都必须制定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前
  • GlobalFilter通过Ordered接口,或者添加@Order注解来指定order值,可以自定义
  • 路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增
  • 当过滤器的order值一样时,会按照defaultFilter 》 路由过滤器 》 GlobalFilter的顺序执行
2.5 网关的cors跨域配置

跨域:域名不一致就是跨域,主要包括:

  • 域名不同:www.taobao.com和www.taobao.org和www.jd.com和miaosha.jd.com
  • 域名相同,端口不同:localhost:8080和localhost:8081

跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被拦截器拦截的问题

解决方案:CORS

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值