SpringcloudNetFix之Feign整合Hystrix

Feign

feign是基于Ribbon 的服务调用方式,SpringcloudNetFix之Ribbon中写了ribbn的调用方式,是注册restTemplate调用getForObject方法发送请求,需要在请求路径中拼接参数,在参数较少的情况下,这个方法还是可行的。但参数一旦增大就不可用了,所以这里了解一下Feign。

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

Feign实战

在上篇文章SpringcloudNetFix之Ribbon的基础上再添加一个子项目,取名为pay

 通用步骤
1.导入依赖
 <dependencies>
        <!--spring-cloud-starter-netflix-eureka-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!--导入Feign的包-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- user类的项目依赖 -->
        <dependency>
            <groupId>com.huawei</groupId>
            <artifactId>springboot-netfix-pojo-user</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
2.yml文件
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:10010/eureka/
  instance:
    instance-id: server-pay
spring:
  application:
    name: server-pay
server:
  port: 10040
3.启动类
@SpringBootApplication
@EnableFeignClients
public class PayApplication {
    public static void main(String[] args) {
        SpringApplication.run(PayApplication.class, args);
    }
}

注意:这里是@EnableFeignClients

FeignClient配置

创一个Client类,UserFeignClient

@FeignClient("server-user")
public interface UserFeignClient {

    @GetMapping("/user/{id}")
    User getById(@PathVariable("id")Long id);
}

注意这是一个interface

@FeignClient的参数是,userServer子项目yml文件中写的服务的名字

下面的方法和userServer中的接口方法要一样(请求方式、请求地址和参数,返回值类型),但要写成接口。

payController
    @Autowired
    private UserFeignClient userFeignClient;

    @GetMapping("/pay/{id}")
    public User getById(@PathVariable("id")Long id){
        return userFeignClient.getById(id);
    }
}

Ribbon轮询底层

轮询:就是挨个分配,他的底层就是获取全部的server的个数返回一个数组,如果长度是0,则返回null。再定义一个增长int 的 index去模运算 数组长度的结果就是得到的server的下标,再非空判断再返回。

可以自定义负载均衡的算法,在启动类子定义Bean

 //负载均衡算法
    @Bean
    public RandomRule randomRule(){
        return new RandomRule();
    }

这个是随机算法

Hystrix熔断器

说Hystrix就需要先了解几个概念

1.雪崩

一个服务故障产生的连锁反应,导致这个微服务崩溃 。举例来说,就像在路上有一辆很大的车抛锚了,其他的车都只能堵在其后边,导致整个道路堵死。

有雪崩就有针对方法

2.服务熔断

服务多次访问失败,就会被标记为‘熔断状态’,如果某个请求去访问一个熔断状态的服务,会快速失败,触发服务降级:目的-防止请求阻塞

3.降级机制

当一个业务(请求)失败/超时,就会走预先设置好的第二种方案(也就是降级逻辑)。

4.缓存

提供了请求缓存、请求合并实现 , 在高并发的场景之下,Hystrix请求缓存可以方便地开启和使用请求缓存来优化系统,达到减轻高并发时请求线程的消耗、降低请求响应时间的效果。

Hystrix应用

1.导包
<!-- Hystrix相关包 --> 
<dependency> 
    <groupId>org.springframework.cloud</groupId> 
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> 
</dependency>

我们用上面的pay项目,因为到了openFeign的依赖就不用再导入上面的依赖了

2.修改UserFeignClient
@FeignClient(value = "server-user",fallbackFactory = UserFeignClientFallbackFactory.class)
public interface UserFeignClient {

    @GetMapping("/user/{id}")
    User getById(@PathVariable("id")Long id);
}

fallbackFactory的参数是一个类,需要自己写

3.UserFeignClientFallbackFactory类
@Component
public class UserFeignClientFallbackFactory implements FallbackFactory<UserFeignClient> {

    @Override
    public UserFeignClient create(Throwable throwable) {
        return new UserFeignClient() {
            @Override
            public User getById(Long id) {
                throwable.printStackTrace();
                return new User(-1L,"服务器异常,请稍后重试",0,"");
            }
        };
    }
}

实现FallbackFactory<UserFeignClient>接口,泛型是之前的UserFeignClient接口,重写create方法,再重写UserFeignClient的getById方法,这个方法就是降级机制方法

4.测试

写完后可以关闭userServer,结果就会输出降级机制方法的值

5.其他方法

还有一个方法,不用fallbackFactory ,直接用fallback,代码如下

@FeignClient(value = "user-server",fallback = UserFeignClientFallback.class)
public interface UserFeignClient {

    @GetMapping(value = "/user/{id}" )
    User getById(@PathVariable("id")Long id);
}
//让Spring扫描到该托底类
@Component
public class UserFeignClientFallback implements UserFeignClient {

    //日志打印器
    private Logger log = LoggerFactory.getLogger(UserFeignClientFallback.class);

    @Override
    public User getById(Long id) {
        log.info("用户服务不可用");
        //托底数据
        return new User(-1l,"无此用户","用户服务不可用");
    }
}

服务网关-spring cloud zuul

什么是zuul

Zuul是Netflix的开源项目,Spring Cloud将其收纳成为自己的一个子组件。zuul用的是多线程阻塞模型,它本质上就是一个同步 Servlet,这样的模型比较简单,他的问题是多线程之间上下文切换是有开销的,线程越多开销就越大。线程池数量固定意味着能力接受的请求数固定,当后台请求变慢,面对大量的请求,线程池中的线程容易被耗尽,后续的请求会被拒绝。

在Zuul 2.0中它采用了 Netty 实现异步非阻塞编程模型,异步非阻塞模式对线程的消耗比较少,对线程上线文切换的消耗也比较小,并且可以接受更多的请求。它的问题就是线程模型比较复杂,要求深究底层原理需要花一些功夫。

Zuul 在云平台上提供动态路由(请求分发),监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门,也要注册入Eureka,

zuul搭建
1.导依赖

    <dependency>
       <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
     </dependency>
     <dependency>
        <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
     </dependency>
     <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
2.启动类
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class,args);
    }
}
3.yml文件
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:10010/eureka/
  instance:
    instance-id: server-zuul
spring:
  application:
    name: server-zuul
server:
  port: 10050

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

配置完yml,网页的访问路径就改为:localhost:10050/servers/order/order/1

servers为前缀,第一个order为orderServer服务名的别名,不想给访问的人看,就可以配置别名,第二个order为orderController的mapping路径

zuul自定义Filter
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过滤器了。

ZuulFilter

自定义一个filter类去继承ZuulFilter重写四个方法,我这里以登录拦截请求为例

// 需要交给spring容器管理
@Component
public class LoginCheckFilter extends ZuulFilter {
    @Override
    public String filterType() {
    // 返回filter的类型
        return "pre";
    }

    @Override
    public int filterOrder() {
    // 在有很多的prefilter时,想当于filter的优先级,越小的越先执行
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        //  RequestContext是他提供的工具
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        // 是否包含"/login" 如果包含返回 false,就不执行下面的run方法,true执行
        return !request.getRequestURI().contains("/login");
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        HttpServletResponse response = currentContext.getResponse();
        // 设置字符集,否者会乱码
        response.setContentType("application/json;charset=utf-8");
        if(request.getHeader("token") == null){
            // 是否走下个filter是看currentContext.setSendZuulResponse的值;false就不走,默认是true
            // 和下面的return null 无关
            currentContext.setSendZuulResponse(false);
            try {
                // 提示信息
                response.getWriter().println("快去请如来佛祖");
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return null;
    }
}

结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值