深度解锁SpringCloud主流组件,一站式解决微服务诸多难题

1.项目脚手架构建

1.1项目结构介绍和框架选择

springcloud11301

了解VO、POJO、BO对象、之前学习用的DTO、Entity、VO对象

1.2脚手架构建
  • springboot 2.1.8

  • SprinCloud Green

  • Mybatis-plus

1.3mybatis-plus使用优势
  • Mybatis-plus是基于Mybatis开发的开源框架

  • Mybatis-plus封装了分页、条件查询等内容

  • Mybatis-helper可以帮助我们更好的使用Mybatis框架

2.Eureka核心特性

2.1核心特性

Eureka的核心特性:服务注册register、服务续约renew、服务下线cancel、获取注册列表信息

1.服务注册

  • Eureka Chient在第一次心跳时向Eureka Server注册

  • 注册时会提供诸多自身元数据:主机名、端口、健康指标URL等

2.服务续约

  • Eureka Client通过发送心跳进行续约

  • 默认情况下每30秒发送一次心跳

  • 如90秒内Eureka Server未收到续约,则会进行服务剔除

3.服务下线

  • Eureka Client优雅退出时会发送cancel命令
  • Eureka Service收到cancel命令时会删除该节点

4.获取注册列表信息

  • Eureka Client会缓存由Server获取的注册表信息
  • Eureka Client会定期更新注册表信息【默认是30秒】
  • Eureka Client会处理注册表的合并等内容
2.2面试点

1.多注册中心的比较

  • 分布式基础:CAP理论
  • 常见的注册中心:Zookeeper、Eureka等
  • Eureka主要保证的是AP特性
  • Zookeeper是典型的CP特性

CAP理论

  • 一致性:Consistency,强一致性,一个节点的crud变动,其他节点随之变动;弱一致性,一段时间可能不一致,但是最终会一致
  • 可用性:Availability,
  • 分区容错性:Partition tolerance,保证系统部署在不同的分区

2.Eureka注册慢

  • 注册慢的根本原因在于Eureka的AP特性
  • Eureka Client延迟注册,默认是30秒
  • Eureka Server的响应缓存,默认是30秒
  • Eureka Server的缓存刷新,默认30秒

3.Eureka的自我保护

  • Eureka Server会自动更新续约更新阈值
  • Eureka Server续约更新频率低于阈值的时候,则进入保护模式
  • 自我保护模式下Eureka Server不会提出任何注册信息

3.Eureka实战


3.1模块介绍

cloud2

理解:影片服务去Eureka Server上去注册自己的服务地址,院线服务就会去Eureka Server获取影片服务的注册信息,然后院线服务就会通过Http的形式去调用影片服务,就完成了一次业务调用

4.负载均衡Ribbon


4.1 Ribbon架构图展示

cloud3

注意:Eureka Client和Ribbon Client 在一个客户端

4.2 Ribbon概述
  • Ribbon是客户端负载均衡器

  • Ribbon核心功能:服务发现

  • Ribbon核心功能:服务选择规则

  • Ribbon核心功能:服务监听

注意:

  • 哪些内容能被负载均衡:ServerList

  • 怎么负载均衡:负载均衡算法----1.allServerList 2.upServerList

  • IPing:探测服务存活状态

    负载均衡算法:

cloud5

配置类具体实现如下:

cloud6

如上图所示:Eureka实现负载均衡可以直接在restTemplate()方法上加 @LoadBalanced注解

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
4.3 Ribbon核心之IPing

IPing的功能是探测服务的存活状态,也是为了给ServerList提供支持

  • IPing是Ribbon保证服务可用的基石
  • 常见的实现:NIWSDiscoveryPing、PingURL

cloud4

4.4 Ribbon核心之ServerList
  • ServerList是Ribbon存储的可用服务列表
  • ServerList可以手动设置
  • ServerList常见应用是从Eureka中自动获取

5 熔断器Hystrix


5.1 Hystrix介绍
  • Hystrix适用于处理延迟容错的开源库
  • Hystrix主要是用于避免级联故障,提高系统弹性,也就是提高系统等可用性
  • Hystrix解决了由于扇出导致的“雪崩效应”
  • Hystrix的核心是“隔离术”和“熔断机制”

扇出示意图:

cloud7

扇出可以理解为像扇子一样展开,一个节点的崩溃,会导致上面的父节点全部崩溃,也就是雪崩效应

5.2 Hystrix主要作用
  • 提供了服务隔离和服务熔断
  • 提供了服务降级、限流和快速失败
  • 提供了请求合并和请求缓存
  • 自带单体和集群监控
5.3 Hystrix架构图

cloud8

主要流程如下:

  • 1.调用“Main”方法,Command或者Observable Command两种命令模式
  • 2.进行业务验证
    • a. 判断有没有缓存
    • b. 熔断有没有开启
    • c. 限流有没有触发
    • d. 业务执行有没有失败
    • e. 业务执行有没有超时
    • f. 所有失败方法都会触发fallBack方法
  • 3.业务成功后,直接返回
5.4 Hystrix入门

1.Hystrix两种命令模式

  • HystrixCommand和HystrixObservableCommand

  • Command会以隔离的形式完成run方法的调用

  • ObservableCommand使用当前线程进行调用

2.Hystrix配置之GroupKey

  • Hystrix中GroupKey是唯一必填项

  • GroupKey可以作为分组监控和报警的作用

6 Feign入门


6.1 Feign介绍
  • Feign是一个声明式、模版化的HTTP客户端

  • Feign很大程度上简化了HTTP的调用方式

  • Feign很好的弥补了SpringCloud的HTTP的调用的时候,重复的工作量——restTemplate也可以

6.2 Feign能干什么
  • Feign包含了多种的HTTP的调用形式
  • Feign整合了Ribbon和Hystrix
  • Feign提供了多种HTTP底层支持
6.3 Feign特性
  • Feign实现了可插拔的注解支持,包括Feign和JAX-RS注解

  • Feign支持了可插拔的HTTP编码器和解码器

  • Feign支持HTTP请求和响应的压缩

6.4 Feign配置

1.引入openFeign依赖包

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2.开启EnableFeignClients支持

@EnableFeignClients
@EnableDiscoveryClient  // DiscoverClient可以集成大部分注册中心
// @EnableEurekaClient  只对Eureka使用
@SpringBootApplication
public class BackendShowConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(BackendShowConsumerApplication.class, args);
    }

}

在启动类上添加@EnableFeignClients

3.创建业务接口

import com.mooc.meetingfilm.consumer.feign.vo.UserModel;
import com.mooc.meetingfilm.feignconf.FeignHelloConf;
import feign.Param;
import feign.RequestLine;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;

@FeignClient(name = "hello-service-provider",
        primary = true,
        path = "/provider",
        fallbackFactory = FallbackFactory.class
//        fallback = ProviderFallbackAPIImpl.class
//        configuration = FeignHelloConf.class,
//        url = "http://localhost:7101"
 )
public interface ProviderApi {

    @RequestMapping(value = "/sayhello",method = RequestMethod.GET)
    String invokerProviderController(@RequestParam("message") String message);

//    @RequestLine("GET /sayhello?message={message}")
//    String invokerProviderController(@Param("message") String message);

//    @RequestMapping(value = "/{providerId}/sayhello",method = RequestMethod.POST)
//    String providerPost(
//            @RequestHeader("author") String author,
//            @PathVariable("providerId")String providerId,
//            @RequestBody UserModel userModel);
  //@RequestBody还支持json和对象
  //@RequestHeader请求头

}
  • 添加FeignClient注解,name为自己自定义,url为注解的访问目标地址,包含协议、host、post。
  • 添加业务方法,添加URL和method方法支持,编写入参和返回值
6.5 Feign支出多种HTTP的调用形式

cloud222

6.6 Feign多组件集成

1.集成Ribbon,编写配置类

cloud6

也可以在yml里面配置最大注册数:

ribbon:
  MaxAutoRetries : 3

hello-service-provider:
  ribbon:
    MaxAutoRetries : 3

2.集成Hystrix熔断器

2.1 配置yml

feign:
  hystrix:
    enabled: true

2.2 编写降级实现类,继承接口,重写方法

import org.springframework.stereotype.Service;

/**
 * @author : 
 * @program : com.mooc.meetingfilm.consumer.feign
 * @description : 降级实现
 **/
@Service
public class ProviderFallbackAPIImpl implements ProviderApi{

    @Override
    public String invokerProviderController(String message) {
        return "invokerProviderController fallback message="+message;
    }

}

2.3 在api接口上的FeignClient中添加fallback=ProviderFallbackAPIImpl.class

@FeignClient(name = "hello-service-provider",
        primary = true,
        path = "/provider",
//        fallbackFactory = FallbackFactory.class
        fallback = ProviderFallbackAPIImpl.class
//        configuration = FeignHelloConf.class,
//        url = "http://localhost:7101"
 )
public interface ProviderApi {

    @RequestMapping(value = "/sayhello",method = RequestMethod.GET)
    String invokerProviderController(@RequestParam("message") String message);

//    @RequestLine("GET /sayhello?message={message}")
//    String invokerProviderController(@Param("message") String message);

//    @RequestMapping(value = "/{providerId}/sayhello",method = RequestMethod.POST)
//    String providerPost(
//            @RequestHeader("author") String author,
//            @PathVariable("providerId")String providerId,
//            @RequestBody UserModel userModel);

}
6.7 Feign的降级处理
cloud333
import org.springframework.stereotype.Service;

/**
 * @author : 
 * @program : com.mooc.meetingfilm.consumer.feign
 * @description :
 **/
@Service
public class FallbackFactory implements feign.hystrix.FallbackFactory {

    @Override
    public ProviderApi create(Throwable throwable) {
        return new ProviderApi() {
            @Override
            public String invokerProviderController(String message) {
                return "invokerProviderController FallbackFactory message="+message;
            }
        };
    }

}

这里的类名重名,所以添加的是feign.hystrix.FallbackFactory

6.8 Feign面试点分析
1.Feign之HTTP性能优化
  • Feign默认使用的JDK自带的HTTP方式
  • Feign最大的优化点事更换HTTP底层的实现
  • 目前的Apache HTTPClient事一个非常好的选择
  1. 添加pom依赖

    <dependency>
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-httpclient</artifactId>
    </dependency>
    
  2. 修改配置文件yml,在之前的基础上添加

     feign:
    #开启熔断器
      hystrix:
        enabled: true
          #开启自定义的HttpClinet
      httpclient:
        enabled: true
    
2.Feign之HTTP解压缩
  • HTTP常见的优化项是数据压缩
  • Feign可以支持GZip的请求解压缩
  • 注意:解压缩是把双刃剑,一定要谨慎使用
feign:
  hystrix:
    enabled: true
  httpclient:
    enabled: true
      #请求压缩
  compression:
    request:
      enabled: true
      mime-types: text/xml,application/xml,application/json
      min-request-size: 2048
    response:
      enabled: true
6.9 Feign实战技巧
6.9.1.Feign继承
  • 微服务的目标是大量复用,Feign会导致重复工作量
  • Feign提供了继承特性帮助我们解决这个问题
  • 接口复用最多只能有一层,切忌多继承
  1. 创建一个api模块

    1.1 在父模块下添加

    <modules>
        <module>backend_api</module>
    </modules>
    
    <dependencyManagement>
        <dependencies>
                <dependency>
                    <groupId>com.mooc.meetingfilm</groupId>
                    <artifactId>backend-api</artifactId>
                    <version>${project.version}</version>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
  2. 导入Feign依赖,修改父模块依赖

    <parent>
        <groupId>com.mooc.meetingfilm</groupId>
        <artifactId>backend-parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
  3. 客户端Film和hall开启Feign注解 @EnableFeignClients

    @ComponentScan(basePackages = {"com.mooc.meetingfilm"})
    @MapperScan(basePackages = {"com.mooc.meetingfilm.film.dao"})
    @EnableFeignClients
    @EnableDiscoveryClient
    @SpringBootApplication
    public class BackendFilmApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(BackendFilmApplication.class, args);
        }
    
    }
    
  4. 在服务film和hall添加Api依赖

    <dependencies>
        <dependency>
            <groupId>com.mooc.meetingfilm</groupId>
            <artifactId>backend-api</artifactId>
        </dependency>
    </dependencies>
    
  5. 在Api模块添加对外暴露的接口,如果需要返回的Vo对象,可以在Api模块编写,删除之前的VO,统一用API模块的

    import com.mooc.meetingfilm.apis.film.vo.DescribeFilmRespVO;
    import com.mooc.meetingfilm.utils.common.vo.BaseResponseVO;
    import com.mooc.meetingfilm.utils.exception.CommonServiceException;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    /**
     * @author : 
     * @program : com.mooc.meetingfilm.apis.film
     * @description : Film提供的公共接口服务
     **/
    public interface FilmFeignApis {
    
        /**
        * @Description: 对外暴露的接口服务
        * @Param: [filmId]
        * @return: com.mooc.meetingfilm.utils.common.vo.BaseResponseVO
        * @Author: jiangzh
        */
        @RequestMapping(value = "/films/{filmId}", method = RequestMethod.GET)
        BaseResponseVO<DescribeFilmRespVO> describeFilmById(@PathVariable("filmId") String filmId) throws CommonServiceException;
    
    }
    
    package com.mooc.meetingfilm.apis.film.vo;
    
    import lombok.Data;
    
    /**
     * @author : 
     * @program : com.mooc.meetingfilm.film.controller.vo
     * @description : 根据主键获取影片信息对象
     **/
    @Data
    public class DescribeFilmRespVO {
    
        private String filmId;
        private String filmName;
        private String filmLength;
        private String filmCats;
        private String actors;
        private String imgAddress;
        private String subAddress;
    
    
    }
    
  6. 在需要调用的模块添加Client包,这里是hall调用film查询电影信息,所以在hall模块下建一个包为client

    在client包下新建一个FilmFeignApi来继承Api模块下的FilmFeignApis

    有的公司是新建一个client去继承,也可以跳过此步,在第七步直接引入FilmFeignApis也可以

    import com.mooc.meetingfilm.apis.film.FilmFeignApis;
    import org.springframework.cloud.openfeign.FeignClient;
    
    /**
     * @author : 
     * @program : com.mooc.meetingfilm.hall.apis
     * @description : film提供的接口服务
     **/
    @FeignClient(name = "film-service")
    public interface FilmFeignApi extends FilmFeignApis {
    
    }
    
  7. 在业务逻辑层引入FilmFeignApi即可,就可以调用该方法

    @Resource
    private FilmFeignApi filmFeignApi;
    

7 zuul网关


7.1 网关介绍
  1. 由于微服务“各自为政”的特性,使微服务的使用非常麻烦
  2. 通常我们会雇佣一个“传达室大爷”,作为统一入口,这就是网关
  3. 网关的主要作用是请求转发和请求拦截
7.2 Zuul介绍
  1. Zuul是网关大军中的一员,目前市场使用的规律比较高

  2. Zuul除了实现请求转发和过滤,一般还作为鉴权和容错使用

  3. Zuul可以无缝衔接Ribbon和Hystrix

7.3 Zuul的配置
  1. 新增一个模块Zuul,在父模块添加依赖,不需要在业务dependencyManagement里面添加依赖

    <modules>
        <module>backend_apigw_zuul</module>
    </modules>
    
  2. 在模块zuul中pom文件添加依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
  3. 在Zuul模块的启动类添加注解,开启zuul代理@EnableZuulProxy

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
    
    @EnableZuulProxy
    @SpringBootApplication
    public class BackendApigwZuulApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(BackendApigwZuulApplication.class, args);
        }
    
    }
    
  4. 在模块的yml中配置zuul

    server:
      port: 8080
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:8761/eureka/
    zuul:
      routes:
        film-service:
          path: /filmapi/**
          
    logging:
      config: classpath:logback.xml
    

    访问http://localhost:8080/film-service/films即可访问,或者http://localhost:8080/filmapi/films

7.4 Zuul的使用
7.4.1 请求路由
  • Zuul可以通过配置完成请求的路由配置

  • Zuul服务器由默认支持的serviceId作为上下文

  • Ignored-services可以禁用serviceId

7.4.2 请求路由表达式
  • ?–>匹配任意单个字符
  • *–>匹配任意数量的字符
  • ** -->匹配任意数量的字符,支持多级目录
7.4.3 Zuul的高层架构图

zuul1

7.4.4 Zuul Filter的生命周期

zuul

7.4.5 自定义Filter
  • 继承ZuulFilter并实现相应的方法

  • 设置Filter类型、级别和是否启用

  • 开发具体的业务逻辑

  1. 在zuul模块添加filter文件夹,并在里面创建自定义的filter类,需要继承ZuulFilter

    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import com.netflix.zuul.exception.ZuulException;
    import lombok.extern.slf4j.Slf4j;
    
    import javax.servlet.http.HttpServletRequest;
    import java.util.Enumeration;
    
    /**
     * @author : 
     * @program : com.mooc.meetingfilm.apigwzuul.filters
     * @description :
     **/
    @Slf4j
    public class MyFilter extends ZuulFilter {
        /**
        * @Description: Filter类型
        * @Param: [] 
        * @return: java.lang.String
        * @Author: jiangzh
        */
        @Override
        public String filterType() {
            return "pre";
        }
    
        /**
        * @Description: filter的执行顺序
        * @Param: [] 数字越大越靠后
        * @return: int
        * @Author: jiangzh
        */
        @Override
        public int filterOrder() {
            return 0;
        }
    
        /**
        * @Description: 是否要拦截
        * @Param: []
        * @return: boolean
        * @Author: jiangzh
        */
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        /**
        * @Description: Filter的具体业务逻辑
        * @Param: []
        * @return: java.lang.Object
        * @Author: jiangzh
        */
        @Override
        public Object run() throws ZuulException {
            // ThreadLocal
            RequestContext requestContext = RequestContext.getCurrentContext();
    
            HttpServletRequest request = requestContext.getRequest();
    
            Enumeration<String> headerNames = request.getHeaderNames();
            while (headerNames.hasMoreElements()){
                String headName = headerNames.nextElement();
                log.info("headName:{}, headValue:{}", headName, request.getHeader(headName));
            }
    
            return null;
        }
    }
    
  2. 在zuul模块下新建一个config的文件夹,编写ZuulConfig配置类,设置返回值类型为自定义Filter类

    import com.mooc.meetingfilm.apigwzuul.filters.JWTFilter;
    import com.mooc.meetingfilm.apigwzuul.filters.MyFilter;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * @author : 
     * @program : com.mooc.meetingfilm.apigwzuul.config
     * @description :
     **/
    @Configuration
    public class ZuulConfig {
    
        @Bean
        public MyFilter initMyFilter(){
            return new MyFilter();
        }
    }
    
7.4.6 核心PreFilter

zuul21

7.4.7 核心RoutingFilter

zuul22

7.4.8 核心PostFilter

zuul23

7.5 Zuul面试点
7.5.1 Zuul和Zuul2
  • Zuul使用的是阻塞式县城完成业务调用,占用资源较多,并发量不高
  • Zuul使用的是异步线程完成业务调用
7.5.2 Zuul与Hystrix整合
  • Zuul可以使用FallbackProvider完成降级开发
  • Zuul默认是使用HystrixCommand进行包装的
  • Zuul默认情况下隔离使用的SEMAPHORE

屏蔽头信息:

  • sensitive-headers: “imooc” ## zuul默认会过滤一些头信息,需要进行过滤设置-----乐观锁

  • ignored-headers:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值