第一代微服务方案标准-SpringCloudNetflix-Day02

SpringCloudNetflix

一.客户端负载均衡-OpenFeign

1.什么是Feign

1.1.引入

在day01中,我们使用Ribbon作为客户端负载均衡完成了订单服务和用户服务的通信,当我们通过RestTemplate调用其它服务时,所需要的参数须在请求的URL中进行拼接,如果参数少的话或许我们还可以忍受,一旦有多个参数的话,这时拼接请求字符串就会效率低下

而Feign的服务调用方式对于程序员来说更为友好,它基于Ribbon进行了封装,把一些负责的url和参数处理细节屏蔽起来,我们只需要简单编写Fiegn的客户端接口就可以像调用本地service去调用远程微服务。

1.2.概念

Feign是一个声明式的http客户端,使用Feign可以实现声明式REST调用,它的目的就是让Web Service调用更加简单。

Feign整合了Ribbon和SpringMvc注解,这让Feign的客户端接口看起来就像一个Controller。Feign提供了HTTP请求的模板,通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数、格式、地址等信息

而Feign则会完全代理HTTP请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。同时Feign整合了Hystrix,可以很容易的实现服务熔断和降级。

2.Feign的编码实战【重要】

2.1.创建工程

在这里插入图片描述

2.2.导入依赖

导入Feign,Eureka Client和web的依赖包,同时依赖user-common模块 ,具体的 pom.xml如下

<dependencies>
        <dependency>
            <groupId>com.huawei</groupId>
            <artifactId>springcloud-netfix-pojo-user</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--2.导入Feign的包-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>
2.3.配置类如下

主配置类增加@EnableFeignClients标签 , 其value属性可以指定Feign的客户端接口的包,当然也可以省略value属性

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
// @EnableEurekaClient默认开启,可以不加
public class PayServerApplication10040 {
    public static void main(String[] args) {
        SpringApplication.run(PayServerApplication10040.class, args);
    }

    // 随机负载均衡算法,默认为轮询
    /*@Bean
    public RandomRule randomRule(){
        return new RandomRule();
    }*/
}

yml配置如下:

#注册到EurekaServer
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:10010/eureka/
  instance:
    prefer-ip-address: true #使用ip地址进行注册
    instance-id: pay-service:10040    #实例ID
spring:
  application:
    name: pay-service
server:
  port: 10040
  # 配置UserFeignClient的日志打印级别
logging:
  level:
    com.huawei: debug

# 开启Feign的数据压缩传输来节省网络开销
feign:
  compression:
    request:
      enabled: true
      min-request-size: 1024 #最小阈值,小于这个不压缩
      mime-types: text/xml,application/xml,application/json #压缩哪些类型的数据
    response:
      enabled: true
2.4.编写Feign的客户端接口
/**
 * FeignClient("user-service"):标记该接口是feign的客户端接口,用来调用户服务的
 * user-service就是要调用目标服务(客户端)的服务名
 */
@FeignClient("user-service")
public interface UserFeignClient {
    //订单服务来调用这个方法      http://localhost:1020/user/1
    // @GetMapping(value = "/user/{id}" )
    @GetMapping("/user/{id}")
    User getById(@PathVariable Long id);
}
2.5.编写Controller使用Feign接口
@RestController
public class PayController {
    @Autowired
    private UserFeignClient userFeignClient;

    // 用户微服务:根据id查询user
    @RequestMapping("/pay/{id}")
    public User getById(@PathVariable Long id) {
        return userFeignClient.getById(id);
    }
}
2.6.测试

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

2.7.Feign的工作原理

要使用Feign,我们除了导入依赖之外,需要主配置类通过@EnableFeignClients(value="")注解开启Feign,也可以通过value属性指定了Feign的扫描包,可以不写(value=“”)
同时我们需要为Feign编写客户端接口,接口上需要注解@FeignClient标签。 当程序启动,注解了@FeignClient的接口将会被扫描到然后交给Spring管理

当请求发起,会使用jdk的动态代理方式代理接口,生成相应的RequestTemplate,Feign会为每个方法生成一个RequestTemplate同时封装好http信息,如:url,请求参数等等

最终RequestTemplate生成request请求,交给Http客户端(UrlConnection ,HttpClient,OkHttp)。然后Http客户端会交给LoadBalancerClient,使用Ribbon的负载均衡发起调用。

4.Feign的参数配置【扩展】

4.1.负载均衡配置

Feign已经集成了Ribbon,所以它的负载均衡配置基于Ribbon配置即可,这里使用xml简单配置负载均衡策略如下:

user-server:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
4.2.Feign的超时配置

如果在服务调用时出现了 “feign.RetryableException : Read timed out…”错误日志,说明Ribbon处理超时 ,我们可以配置Ribbon的超时时间:

ribbon:
	ConnectTimeout: 3000
    ReadTimeout: 6000

如果服务调用出现“com.netflix.hystrix.exception.HystrixRuntimeException:… timed - out and no fallback available” 错误日志,是因为Hystrix超时,默认Feign集成了Hystrix,但是高版本是关闭了Hystrix,我们可以配置Hystrix超时时间:

feign:
   hystrix:
       enabled: true #开启熔断支持
hystrix:
  command:
      default:
        execution:
          isolation:
            thread:
              timeoutInMilliseconds: 6000   #hystrix超时时间

5.Feign开启日志调试【扩展】

官方文档:https://cloud.spring.io/spring-cloud-openfeign/reference/html/#feign-logging

5.1.配置Feign日志打印内容

有的时候我们需要看到Feign的调用过程中的参数及相应,我们可以对Feign的日志进行配置,Feign支持如下几种日志模式来决定日志记录内容多少:

  • NONE,不记录(DEFAULT)。
  • BASIC,仅记录请求方法和URL以及响应状态代码和执行时间。
  • HEADERS,记录基本信息以及请求和响应标头。
  • FULL,记录请求和响应的标题,正文和元数据。
    创建Feign配置类
@Configuration
public class FeignConfiguration {
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;	//打印Feign的所有日志
    }
}
5.2.配置日志打印级别【扩展】

配置UserFeignClient的日志打印级别,上面的配置打印Feign的那些内容,下面这个是配置日志框架打印日志的级别,不修改可能打印不出来日志,DEBUG打印日志调试信息。

logging:
  level:
    cn.itsource.springboot.feignclient.UserFeignClient: debug

6.Feign开启GZIP【扩展】

可以通过开启Feign的数据压缩传输来节省网络开销,但是压缩数据会增加CPU的开销,所以太小的数据没必要压缩,可以通过压缩大小阈值来控制,配置如下:

feign:
  compression:
    request:
      enabled: true
      min-request-size: 1024 #最小阈值,小于这个不压缩
      mime-types: text/xml,application/xml,application/json #压缩哪些类型的数据
    response:
      enabled: true

在Feign中该配置对应

二.Hystrix熔断器

1.理解Hystrix

1.1.雪崩效应

雪崩效应:某一个服务发生故障会导致调用它的服务跟着异常,然后导致整个调用链调用的异常,甚至导致整个微服务瘫痪 。

1.2.Hystrix介绍

Hystrix其设计原则如下:

  1. 防止单个服务异常导致整个微服务故障。
  2. 快速失败,如果服务出现故障,服务的请求快速失败,线程不会等待。
  3. 服务降级,请求故障可以返回设定好的二手方案数据(兜底数据)。
  4. 熔断机制,防止故障的扩散,导致整个服务瘫痪。
  5. 服务监控,提供了Hystrix Bashboard仪表盘,实时监控熔断器状态
1.3.Hystrix的功能

资源隔离包括线程池隔离和信号量隔离,作用是限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用 ,这里可以简单的理解为资源隔离就是限制请求的数量
在这里插入图片描述
线程池和信号量对比(使用线程池比较好):
在这里插入图片描述

2.OpenFeign使用Hystrix[掌握]

2.1.导包
<dependency>
            <groupId>com.huawei</groupId>
            <artifactId>springcloud-netfix-pojo-user</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--2.导入Feign的包-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
2.2.启动类加注解

@EnableFeignClients

2.3. yml配置:

feign:

开启hystrix

hystrix:
enabled: true

2.4. Fiegn接口熔断-fallbackFactory方式(fallback方式略)
2.5. UserFeignClient接口:feign的客户端接口,用来调用户服务
/**
 * FeignClient("user-service"):标记该接口是feign的客户端接口,用来调用户服务的
 * user-service就是要调用目标服务(客户端)的服务名
 */
@FeignClient(value = "user-service",fallbackFactory = UserFeignClientFallBackFactory.class)
public interface UserFeignClient {
    //订单服务来调用这个方法      http://localhost:1020/user/1
    // @GetMapping(value = "/user/{id}" )
    @GetMapping("/user/{id}")
    User getById(@PathVariable Long id);
}
2.6.写一个内去实现该接口(托底实现类)
// 要交给spring去管理,要不然找不到类
@Component
public class UserFeignClientFallBackFactory implements FallbackFactory<UserFeignClient> {
    // 因为接口不能new对象,因此使用匿名内部类
    @Override
    public UserFeignClient create(Throwable throwable) {
        /*return new UserFeignClient() {
            @Override
            public User getById(Long id) {
                throwable.printStackTrace();
                return new User(-1L,"错误","用户服务不可用,请稍后再试");
            }
        };*/
        return id -> {
            throwable.printStackTrace();
            return new User(-1L, "错误", "用户服务不可用,请稍后再试");
        };
    }
}
2.7测试:略,直接看下面的服务网关

三.服务网关-spring cloud zuul

1.理解zuul

1.1.为什么要zuul

试想一下如果我们有很多的微服务,他们都需要登录之后才能访问,那么我需要在每个微服务都去做一套登录检查逻辑,这样是不是会存在大量重复的代码和工作量,我们希望的是把登录检查这种公共的逻辑进行统一的抽取,只需要做一套检查逻辑即可,而zuul就可以用来干这类事情,我们可以把zuul看做是微服务的大门,所有的请求都需要通过zuul将请求分发到其他微服务,根据这一特性我们就可以在zuul做统一的登录检查,下游的微服务不再处理登录检查逻辑。

1.2.什么是zuul

Zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet(filter)应用。
Zuul 在云平台上提供动态路由(请求分发),监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门,也要注册入Eureka,用一张图来理解zuul在架构中的的角色:
在这里插入图片描述

2.zuul的搭建

2.1. 依赖
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--zuul网关基础依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
2.2. 启动类加注解@EnableZuulProxy // 开启网关
  • @EnableZuulProxy : 开启zuul 可以看做是 @EnableZuulServer 的增强版 ,一般用这个
  • @EnableZuulServer : 这个标签也可以开启zuul,但是这个标签开启的Filter更少
2.3. yml配置
#注册到EurekaServer
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:10010/eureka/ #注册地址
  instance:
    #    prefer-ip-address: true #使用ip地址进行注册
    instance-id: zuul-service:10050    #实例ID
spring:
  application:
    name: zuul-service #服务名
server:
  port: 10050

zuul:
  prefix: "/servers"  #统一访问前缀
  ignoredServices: "*"  #禁用掉使用浏览器通过服务名的方式访问服务
  routes:
    pay-service: "/pay/**"   #指定pay-server这个服务使用 /pay路径来访问  - 别名
    order-service: "/order/**"   #指定order-server这个服务使用 /order路径来访问
2.4.测试zuul

没有前缀:
在这里插入图片描述
有前缀(有什么用?):
在这里插入图片描述

3.自定义zuul的Filter

3.1.zuul的工作原理
zuul的底层是通过各种Filter来实现的,zuul中的filter按照执行顺序分为了“pre”前置(”custom”自定义一般是前置),“routing”路由,“post”后置,以及“error”异常Filter组成,当各种Filter出现了异常,请求会跳转到“error filter”,然后再经过“post filter” 最后返回结果,下面是Filter的执行流程图:
在这里插入图片描述

  • 正常流程:
    • 请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。
  • 异常流程:
    • 整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕后,会将请求交给POST过滤器,最后返回给用户。
    • 如果是error过滤器自己出现异常,最终也会进入POST过滤器,而后返回。
    • 如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的时,请求不会再到达POST过滤器了。

3.2.ZuulFilter

  • filterType :是用来指定filter的类型的(类型见常量类:FilterConstants)
  • filterOrder :是filter的执行顺序,越小越先执行
  • shouldFilter :是其父接口IZuulFilter的方法,用来决定run方法是否要被执行
  • run :是其父接口IZuulFilter的方法,该方法是Filter的核心业务方法
public abstract class ZuulFilter implements IZuulFilter{
    abstract public String filterType();
    abstract public int filterOrder();
    //下面两个方法是 IZuulFilter 提供的 
    boolean shouldFilter();
    Object run() throws ZuulException;
}

3.3.自定义Filter

package com.huawei.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 不能将shouldFilter()方法和run()方法中的
 * RequestContext currentContext = RequestContext.getCurrentContext();
 * HttpServletRequest request = currentContext.getRequest();
 * 抽出来放在方法外面
 * 将RequestContext和HttpServletRequest对象抽出到方法外部可能会导致上下文混乱,
 * 线程安全问题以及对不同请求的处理干扰。
 */
@Component
public class LoginCheckZuulFilter extends ZuulFilter {


    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        // 如果是登录请求直接放行
        if (request.getRequestURI().contains("/login")) {
        // 返回false不会执行run()方法
            return false;
        }
        return true;
    }

// 登录检查
    @Override
    public Object run() throws ZuulException {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        HttpServletResponse response = currentContext.getResponse();
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=UTF-8");
        String token = request.getHeader("token");
        if (!StringUtils.hasLength(token)){
            currentContext.setSendZuulResponse(false);
            try {
                response.getWriter().println("请登录");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

3.4.测试
直接输入网址,因为没有token
在这里插入图片描述
使用postman进行测试,上面3个打钩是自动的(最新版本postman),关闭服务了,所以没有响应…自行测试
![在这里插入图片描述](https://img-blog.csdnimg.cn/6e7eccd08c6b47fd95c59644db26e43e.png

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值