一起来学SpringCloud之

来源:互联网 发布:linux lamp一键安装 编辑:程序博客网 时间:2024/05/16 01:56

上一篇中我们讲了 断路器Hystrix(Ribbon) 本章讲解Feign+Hystrix已经Request请求传递,各种奇淫技巧….

- Hystrix

Hystrix支持回退概念:当 断路器 打开或运行错误时,执行默认的代码,给@FeignClient定义一个fallback属性,设置它实现回退的,还需要将您的实现类声明为Spring Bean。

官方文档:http://cloud.spring.io/spring-cloud-static/Dalston.SR2/#spring-cloud-feign-hystrix-fallback

- 准备工作

1.启动Consul
2.创建 battcn-providerbattcn-consumer 如果看了上一章的,可以直接copy代码复用

- battcn-provider

- pom.xml

<dependencies>    <dependency>        <groupId>org.springframework.cloud</groupId>        <artifactId>spring-cloud-starter-consul-discovery</artifactId>    </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-actuator</artifactId>    </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-test</artifactId>        <scope>test</scope>    </dependency></dependencies>

- ProviderApplication.java(有变化)

@SpringBootApplication@EnableDiscoveryClient@RestControllerpublic class ProviderApplication {    @Value("${spring.application.name}")    String applicationName;    public static void main(String[] args) {        SpringApplication.run(ProviderApplication.class, args);    }    @GetMapping("/test1")    public String test1() {        return "My Name's :" + applicationName + " Email:1837307557@qq.com";    }    @GetMapping("/test2")    public String test2() {        System.out.println(1/0);        return "hello error";    }}

- bootstrap.yml

server:  port: 8765spring:  application:    name: battcn-provider  cloud:    consul:      host: localhost      port: 8500      enabled: true      discovery:        enabled: true        prefer-ip-address: true

- battcn-consumer

- pom.xml

<dependencies>    <dependency>        <groupId>org.springframework.cloud</groupId>        <artifactId>spring-cloud-starter-feign</artifactId>    </dependency>    <dependency>        <groupId>org.springframework.cloud</groupId>        <artifactId>spring-cloud-starter-hystrix</artifactId>    </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-actuator</artifactId>    </dependency>    <dependency>        <groupId>org.springframework.cloud</groupId>        <artifactId>spring-cloud-starter-consul-discovery</artifactId>    </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-test</artifactId>        <scope>test</scope>    </dependency></dependencies>

- ConsumerApplication

@SpringCloudApplication@EnableFeignClients//开启 FeignClient支持public class ConsumerApplication {    @Bean    @LoadBalanced    public RestTemplate restTemplate() {        return new RestTemplate();    }    public static void main(String[] args) {        SpringApplication.run(ConsumerApplication.class, args);    }}

- HiClient

package com.battcn.client;import org.springframework.cloud.netflix.feign.FeignClient;import org.springframework.stereotype.Component;import org.springframework.web.bind.annotation.GetMapping;/** * @author Levin * @date 2017-08-07. */@FeignClient(value = "battcn-provider",fallback = HiClient.HiClientFallback.class)public interface HiClient {    @GetMapping("/test1")    String test1();    @GetMapping("/test2")    String test2();    @Component    class HiClientFallback implements HiClient{        @Override        public String test1() {            return "fallback....";        }        @Override        public String test2() {            return "fallback...";        }    }}

- HiController

package com.battcn.controller;import com.battcn.client.HiClient;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class HiController {    @Autowired    HiClient hiClient;    @GetMapping("/h1")    public String hi() {        return hiClient.test1();    }    @GetMapping("/h2")    public String say() {        return hiClient.test2();    }}

- bootstrap.yml

server:  port: 8766feign:  hystrix:    enabled: true   #开启Feign Hystrix 支持spring:  application:    name: battcn-consumer  cloud:    consul:      host: localhost      port: 8500      enabled: true      discovery:        enabled: true        prefer-ip-address: true

- 测试

启动:battcn-provider

启动:battcn-consumer

访问:http://localhost:8500/ 显示如下代表服务注册成功

查看注册中心

访问:http://localhost:8766/h1

    My Name's :battcn-provider Email:1837307557@qq.com  #正确情况

访问:http://localhost:8766/h2

    fallback...     #错误情况,阻断输出fallback...

- 解锁新姿势

- 异常处理

画图工具:https://www.processon.com/

异常处理

如果我们FeignClient服务都是内部的,在客户端抛出异常直接往最外层抛出,就不需要在消费者通过硬编码处理了,关键代码(完整代码看GIT)…

battcn-provider 中异常处理

@ExceptionHandler(value = Exception.class)@ResponseBodypublic ErrorResponseEntity jsonErrorHandler(Exception e, HttpServletResponse rep) throws Exception {    if (e instanceof BattcnException) {        BattcnException exception = (BattcnException) e;        return exception.toErrorResponseEntity();    }    logger.error("服务器未知异常", e);    rep.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());    return new ErrorResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), "服务器未知异常");}

battcn-consumer 中异常处理

@ExceptionHandler(value = Exception.class)@ResponseBodypublic ErrorResponseEntity jsonErrorHandler(Exception e, HttpServletResponse rep) throws Exception {    if (e instanceof HystrixBadRequestException) {        HystrixBadRequestException exception = (HystrixBadRequestException) e;        rep.setStatus(HttpStatus.BAD_REQUEST.value());        logger.info("[HystrixBadRequestException] - [" + exception.getMessage() + "]");        JSONObject obj = JSON.parseObject(exception.getMessage());        return new ErrorResponseEntity(obj.getInteger("customCode"), obj.getString("message"));    }    logger.error("服务器未知异常", e);    rep.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());    return new ErrorResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), "服务器未知异常");}

Hystrix中只有,HystrixBadRequestException 是不会被计数,也不会进入阻断器,所以我们定义一个自己的错误解码器

- FeignServiceErrorDecoder

package com.battcn.config;import com.netflix.hystrix.exception.HystrixBadRequestException;import feign.Response;import feign.Util;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Component;import java.io.IOException;@Componentpublic class FeignServiceErrorDecoder implements feign.codec.ErrorDecoder {    static Logger LOGGER = LoggerFactory.getLogger(FeignServiceErrorDecoder.class);    @Override    public Exception decode(String methodKey, Response response) {        try {            if (response.status() >= 400 && response.status() <= 499) {                String error = Util.toString(response.body().asReader());                return new HystrixBadRequestException(error);            }        } catch (IOException e) {            LOGGER.error("[Feign解析异常] - [{}]", e);        }        return feign.FeignException.errorStatus(methodKey, response);    }}

- 测试

访问:http://localhost:8766/h1

    My Name's :battcn-provider Email:1837307557@qq.com  #正确情况

访问:http://localhost:8766/h2

    {"customCode":400,"message":"请求错误"}     #抛出异常,而不是进入阻断器

关闭battcn-provider:http://localhost:8766/h1

    fallback...     #服务down机,阻断输出fallback...

- Request 参数传递

在开发中难免会有 服务之间 请求头传递比如Token,ID,因为我们使用的是FeignClient 的方式,那么我们无法获得HttpServletRequest的上下文,这个时候怎么办呢?通过硬编码是比较low的一种,接下来为各位看官带来简单粗暴的(也就知道这种,还有其它简单的方式欢迎交流….)

参数传递

- FeignRequest(consumer)

package com.battcn.config;import feign.RequestInterceptor;import feign.RequestTemplate;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Component;import java.util.UUID;@Componentpublic class FeignRequest implements RequestInterceptor {    final Logger LOGGER = LoggerFactory.getLogger(this.getClass().getSimpleName());    @Override    public void apply(RequestTemplate requestTemplate) {        //1.模拟获取request.header参数        String token = UUID.randomUUID().toString().replace("-","").toUpperCase();        LOGGER.info("传递的Token - [{}]",token);        requestTemplate.header("token",token);//模拟将Token放入在 feign.Request对象中    }}

- HelloController(provider)

@Value("${spring.application.name}")String applicationName;@AutowiredHttpServletRequest request;@Override@GetMapping("/test1")public String test1() {    return "My Name's :" + applicationName + " Token:"+request.getHeader("token");}

- 测试

日志结果

结果:My Name's :battcn-provider Token:5588551D64C8478BA681A35892A03437 代表我们Token(HttpServletRequest)传递成功…

- 说点什么

画图工具:https://www.processon.com/

本章代码(battcn-provider/consumer):https://git.oschina.net/battcn/battcn-cloud/tree/master/battcn-cloud-hystrix-feign

如有问题请及时与我联系

  • 个人QQ:1837307557
  • Spring Cloud中国社区①:415028731
  • Spring For All 社区⑤:157525002
原创粉丝点击