SpringCloud

Spring Cloud 的整体架构

在这里插入图片描述
Service Provider : 暴露服务的服务提供方
Service Consumer : 调用远程服务的服务消费方
EureKa Server :服务注册中心和服务发现中心
(这里可以类比dubbo进行学习)
消费者通过服务名称寻找生产者的位置(ip+端口),dubbo是使用RPC远程调用的方式,这是使用http方式进行访问,返回个json数据

注册中心Eureka

Eureka是一个服务治理组件,基于REST的服务,它主要包括服务注册和服务发现,主要用来搭建服务注册中心。

Eureka是Netflix 公司开发的,Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务注册和发现,也就是说Spring CloudNetflix Eureka做了二次封装;

Eureka采用了C-S(客户端/服务端)的设计架构,也就是Eureka由两个组件组成:Eureka服务端和Eureka客户端。Eureka Server作为服务注册的服务端,它是服务注册中心,而系统中的其他微服务,使用Eureka的客户端连接到Eureka Server服务端,并维持心跳连接,Eureka客户端是一个Java客户端,用来简化与服务器的交互、负载均衡,服务的故障切换等;有了Eureka注册中心,系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。

Eureka与Zookeeper的比较

著名的CAP理论指出,一个分布式系统不可能同时满足C(一致性)、A(可用性)和P(分区容错性)。由于分区容错性在是分布式系统中必须要保证的,因此我们只能在AC之间进行权衡,在此Zookeeper保证的是CP, 而Eureka则是AP

Zookeeper保证CP
ZooKeeper中,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举,但是问题在于,选举leader需要一定时间, 且选举期间整个ZooKeeper集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因网络问题使得ZooKeeper集群失去master节点是大概率事件,虽然服务最终能够恢复,但是在选举时间内导致服务注册长期不可用是难以容忍的。

Eureka保证AP
Eureka优先保证可用性,Eureka各个节点是平等的,某几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册或时如果发现连接失败,则会自动切换至其它节点,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。

搭建和配置Eureka服务注册中心

  1. 创建一个SpringBoot模块(我这里起名eureka-server),并且添加SpringBoot的相关依赖;
    1. 勾选 Web --> Spring Web
    2. 勾选 Spring Cloud Discovery --> Eureka Server

也可以直接添加Eureka Server的依赖:

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

如果手动添加依赖,必须添加Maven的依赖管理器,否则eureka无法被识别

<properties>
    <spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>

<dependencyManagement>
 	<dependencies>
      	<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
 		</dependency>
 	</dependencies>
</dependencyManagement>

注意:如果通过SpringBoot的开发工具创建Web工程那么这个依赖以及依赖管理是自动添加的

  1. 在application.properties文件中配置Eureka服务注册中心信息
#内嵌定时tomcat的端口
server.port=9100

#设置该服务注册中心的hostname
eureka.instance.hostname=localhost

#由于我们目前创建的应用是一个服务注册中心,而不是普通的应用,默认情况下,这个应用会向注册中心(也是它自己)注册它自己,
#设置为false表示禁止这种自己向自己注册的默认行为
eureka.client.register-with-eureka=false

#表示不去检索其他的服务,因为服务注册中心本身的职责就是维护服务实例,它不需要去检索其他服务
eureka.client.fetch-registry=false

#指定服务注册中心的位置
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka
  1. 在Spring Boot的入口类上添加一个@EnableEurekaServer注解,用于开启Eureka注册中心服务端
@SpringBootApplication
@EnableEurekaServer
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

至此,Eureka Server 搭建完毕,接下来就进行测试:
启动Spring Boot 内嵌 Tomcat ,启动成功之后,通过在浏览器地址栏访问我们的注册中心;访问地址为:

# 两个参数均为application.properties文件中配置的值
http://${eureka.instance.hostname}:${server.port}

如果按我上面配置的参数,则访问地址为:http://localhost:9100
如果访问成功,则会显示如下的页面:
在这里插入图片描述
在这里插入图片描述

Eureka的自我保护机制

自我保护机制是当服务器与Eureka注册中心发生网络连接问题时,注册中心通过心跳检测没有发现该服务,会启动自我保护机制,将已注册的服务保留下来,等待服务连接成功;如果关闭自我保护机制,发生网络连接问题时,在注册中心找不到服务地址,Eureka注册中心会将这条服务地址移出注册中心,当网络连接问题解决时,找不到这条地址。
自我保护机制的原则是:宁可保护所有服务,也不盲目注销任何健康的微服务(宁错杀不放过)
application.properties

eureka.server.enable-self-preservation=false

false 表示关闭自我保护机制,true 表示开启自我保护机制,默认是true
开启自我保护机制页面会出现如下红色字体:
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
关闭自我保护机制页面会出现如下红色字体:
RENEWALS ARE LESSER THAN THE THRESHOLD. THE SELF PRESERVATION MODE IS TURNED OFF.THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.

Ribbon(客户端负载均衡)

Ribbon是一个客户端负载均衡的算法, 默认通过轮询的方式进行负载均衡
当两个或多个服务名相同地址不同时,需要进行负载均衡。

如何改变负载均衡方式?

在RestTemplateConfig(这个类是自定义配置类,注入restTemplate对象)中,@LoadBalance表示默认使用轮询方式进行负载均衡
将下面方法粘进去即可:

	@Bean
	public IRule iRule(){
		//表示通过随机的方式进行负载均衡,不使用轮询的方式
		//在此处使用什么类型的对象,就是用什么方式的负载均衡
		return new RandomRule();
	}

负载均衡方式有哪些?

RandomRule随机
RoundRobinRule轮询
AvailabilityFilteringRule先过滤掉由于多次访问故障的服务,以及并发连接数超过阈值的服务,然后对剩下的服务按照轮询策略进行访问;
WeightedResponseTimeRule根据平均响应时间计算所有服务的权重,响应时间越快服务权重就越大被选中的概率即越高,如果服务刚启动时统计信息不足,则使用
RoundRobinRule策略,待统计信息足够会切换到该WeightedResponseTimeRule策略;
RetryRule先按照RoundRobinRule策略分发,如果分发到的服务不能访问,则在指定时间内进行重试,分发其他可用的服务;
BestAvailableRule先过滤掉由于多次访问故障的服务,然后选择一个并发量最小的服务;
ZoneAvoidanceRule综合判断服务节点所在区域的性能和服务节点的可用性,来决定选择哪个服务;

Rest请求模板类—RestTemplate

get请求(查询)

getForEntity()—返回一个ResponseEntity类型的封装,ResponseEntity是Spring对HTTP请求响应的封装,包括了几个重要的元素,比如响应码、contentType、contentLength、响应消息体等;

//用法一:
ResponseEntity<String> entity = restTemplate.getForEntity("url",返回值类型);
String body = entity.getBody();
HttpStatus code = entity.getStatusCode(); 
int codeValue = entity.getStatusCodeValue();
HttpHeaders headers = entity.getHeaders();

//用法二:不推荐
Object [] params={1,”张无忌”};
//这里的数字0,1 表示params数组中的索引值
restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello?id={0}&name={1}", String.class, params).getBody();

//用法三:推荐
Map<String, Object> paramMap = new ConcurrentHashMap<>();
paramMap.put("id", 1);
paramMap.put("name", "张无忌");
restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello?id={id}&name={name}", String.class, paramMap).getBody();                                                                                                                                                                                                                           

getForObject—是对getForEntity()的封装,返回一个具体类型的对象,具体用法参照getForEntity();

这里推荐使用getForObject()方法

post请求(添加)

postForEntity(),postForObject()类比get请求,这里以postForObject为例(推荐使用)

//下面三种用法的第二个参数是Object request,表示请求参数列表,null表示不传递参数
//1. 第二个参数是Object类型,但不是什么类型都可以传递,只能传递LinkedMultiVlaueMap类型的对象,或者HashMap的value值是List类型的
//2. 如果第二个参数不传递,建议使用get方式的请求,因为给get方式比post方式要快
//用法一:
ResponseEntity<String> entity = restTemplate.postForObject("url",null,返回值类型);

//用法二:不推荐
Object [] params={1,”张无忌”};
//这里的数字0,1 表示params数组中的索引值
restTemplate.postForObject("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello?id={0}&name={1}",null, String.class, params).getBody();

//用法三:推荐
Map<String, Object> paramMap = new ConcurrentHashMap<>();
paramMap.put("id", 1);
paramMap.put("name", "张无忌");
restTemplate.postForObject("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello?id={id}&name={name}",null, String.class, paramMap).getBody(); 

restTemplate.postForLocation()

put请求(修改)

put()是void类型,无返回值,是以post方式发出请求,用于访问后台提供者的putMapping请求,因为无返回值,前台接收不到具体的值,后台可以接收到。
put(“url”,Object request)
put(“url”,Object request,可变长参数)
put(“url”,Object request,Map<String,?> map)

//1. put请求不能获取服务提供者的响应数据,实际工作不建议使用,可以使用get或者post对应的方法替代
//2. 这个方法针对后台服务提供者的PutMapping请求,适用于数据修改
LinkedMultiValueMap params = new LinkedMultiValueMap();
params.add("name" ,"chenqi");
params.add("age" , "27"):
restTemplate.put("http://localhost:8080/put",params);

delete请求(删除)

只支持get请求,用于访问后台服务提供者的DeleteMapping 请求。
由于delete无法获取服务提供者响应数据,建议使用getForEntity或getForObject进行替换
delete(“url”)
delete(“url”,Map<String,?> map)
delete(“url”,可变长参数)

//1. 第一个参数是请求路径,第二个参数是请求参数,可以是map对象,也可以是可变长参数
//2. 这个方法针对后台服务提供者的PutMapping请求,适用于数据修改
Map params = new HashMap();
params.add("name" ,"chenqi");
params.add("age" , "27"):
restTemplate.delete("http://localhost:8080/put?name={name}&age={age}",params);

服务熔断

什么是服务熔断?

当提供者对消费者的响应产生堆积时,不能及时处理,或者消费者在一定时间不能及时获取到提供者的响应内容,此时,消费者就采用自己的响应内容返回给客户端,这就是服务熔断机制。

服务熔断的使用者是谁?

消费者采取服务熔断机制。

如何使用服务熔断?

第一步:添加依赖包,此处建议使用自动化工程生成的依赖包,保证版本一致

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.4.4.RELEASE</version>
</dependency>

第二步:在入口处添加注解,启动服务熔断机制

//这里也可以使用@SpringCloudApplication注解,它包括以下三个注解的功能
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class Application {
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

第三步:在需要进行熔断的位置(方法)添加注解

//@HystrixCommand 用于标志当前方法是熔断机制,只能用在消费者一端
//fallbackMethod后面是回调方法名,@HystrixProperty指定hystrix的某个属性
@HystrixCommand(fallbackMethod="error",commandProperties={@HystrixProperties(name="execution.isolation.thread.timeoutInMilliseconds", value="5000")})
@RequestMapping("/test")
public String test(){
	return "来自消费者的Hystrix测试";
}
public String error(){
	return "服务器繁忙请稍后重试";
}

服务降级与异常处理

什么是服务降级?

当服务提供者或中服务消费者发生异常后或者超时,会触发熔断机制中fallbackMethod中指定的方法,这就是服务降级。

如何进行异常处理?

  1. 通过服务降级的方式
  2. 通过自定义熔断器类

当在服务生产者发生异常,如何在服务消费者一端了解异常内容?

在熔断机制中fallbackMethod中指定的方法,对其添加Throwable类型的参数,通过System.out.println(Throwable类型的参数)输出到控制台即可。

自定义Hystrix请求的服务异常熔断处理

MyHystrixCommand.java

public class MyHystrixCommand extends HystrixCommand {

	//手动添加两个属性
    private String url;
    private RestTemplate restTemplate;
    
    //在重写的构造方法中添加两个属性URL、restTemplate
    protected MyHystrixCommand(Setter setter,RestTemplate restTemplate ,String url) {
        super(setter);
        this.restTemplate = restTemplate;
        this.url = url;
    }
    //这个方法不能进行手动调用,手动调用不能进行服务熔断
    protected Object run() throws Exception {
        return restTemplate.getForEntity(url, String.class).getBody();
    }
    //返回熔断后的响应数据来替代服务提供者的响应信息
    protected Object getFallback() {
        //服务因异常而熔断则用于获取错误的异常信息对象
        Throwable throwable = super.getExecutionException();
        return "服务被熔断了";
    }
}

TestController.java

@RequestMapping("/test02")
    public Object test01() {
        String url = "http://localhost:8081/test";
        MyHystrixCommand command = new MyHystrixCommand(
                com.netflix.hystrix.HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("")),
                restTemplate,url);
        Object result = command.execute();
        return result;
    }

声明式服务消费Feign

什么是Feign?

Feign是SpringCloud中对负载均衡Ribbon和服务熔断Hystrix的整合,是对它们的封装,简化了开发工作

Feign的使用对象是谁?

服务消费者

Feign的使用步骤有哪些?(以下仅描述了消费者)

  1. 在SpringBoot工程中添加如下依赖
<!-- web起步依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka客户端起步依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- feign起步依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Hystrix负载均衡起步依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
  1. 在项目入口类中添加@EnableFeignClients注解,开启Feign功能
  2. 声明服务,定义一个service接口
//@FeignClient 注解作用是标记当前接口是Feign的一个客户端接口
//属性name 用于指定服务提供者的服务名称
//@FeignClient(name ="11-EUREKA-CLIENT-FEIGN-PROVIDER",fallback= MyFallBack.class)
@FeignClient(name ="11-EUREKA-CLIENT-FEIGN-PROVIDER",fallbackFactory = MyFallbackFactory.class)
public interface TestService {
    //标记当前方法用于请求远程服务提供者
    //属性 /test 用于指定需要访问的服务的具体请求名称
    //注意:当前方法的返回值最好和服务提供者的请求返回值类型相同
    //      方法名任意但建议与服务提供者的名字相同
    @RequestMapping("/test")
     String test();
}
  1. 自定义Hystrix负载均衡类MyFallBack 实现TestService接口(两种方式)
//第一种方式
@Component
public class MyFallBack implements TestService {
    @Override
    public String test() {
        return "test请求不可用请稍后再试";
    }
    public String test2() {
        return "test2请求不可用请稍后再试-------";
    }
}
//第二种方式:可以获取异常信息
@Component
public class MyFallbackFactory implements FallbackFactory<HelloService> {
    @Override
    public HelloService create(Throwable throwable) {
        return new HelloService() {
            @Override
            public String hello() {
                return throwable.getMessage();
            }
        };
    }
}
  1. controller测试
@RestController
public class TestController {
    @Resource
    private TestService testService;
    
    @RequestMapping("/test")
    public String test() {
        String result = testService.test();
        return "来自消费者的test============"+result;
    }
}

API网关Zuul

什么是网关?

网关是服务消费者对服务提供者进行访问请求时,对这些请求进行管理调度的服务程序。它就像一个安检站一样,所有外部的请求都需要经过它的调度与过滤。

网关管理的是什么?

它管理的是外部请求,是消费者对服务提供者的请求

网关的作用有哪些?

API网关实现请求路由、负载均衡、权限验证等功能

使用Zuul构建API网关步骤

首先要有eureka服务模块、服务提供者模块,服务消费者模块

  1. 新建api网关模块12-eureka-client-api-gateway ,添加依赖包
<!--添加spring cloud的zuul的起步依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<!--添加spring cloud的eureka的客户端依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
  1. 在入口类上添加注解@EnableZuulProxy,开启Zuul的 API网关服务功能
  2. 在application.properties文件中配置路由规则
#配置服务内嵌的Tomcat端口
server.port=8083
#配置服务的名称
spring.application.name=12-eureka-client-api-gateway
#配置路由规则zuul.routes.api-zuul.path  中api-zuul 可以任意填写
#/api-zuul/** 表示请求的拦截规则类似拦截器的拦截规则以/api-zuul开头的任意目录以及子孙目录中所有请求都会被拦截
zuul.routes.api-zuul.path=/api-zuul/**
#指向服务名字 用于对这个服务下的某个写特定请求进行拦截
zuul.routes.api-zuul.serviceId=12-eureka-client-zuul-provider

#配置API网关到注册中心上,API网关也将作为一个服务注册到eureka-server上
eureka.client.service-url.defaultZone=http://localhost:9100/eureka
  1. 请求测试1
    • http://localhost:8081/test 测试结果:通过Zuul的服务提供者
    • http://localhost:8082/test 测试结果:没执行-------来自消费者Zuul的test------------通过Zuul的服务提供者
    • http://localhost:8083/api-zuul/test 测试结果:通过Zuul的服务提供者
  2. 服务消费者测试类
@RestController
public class TestController {
    @Resource
    private RestTemplate restTemplate;

    @RequestMapping("/test")
    public String test() {
        ResponseEntity<String>  result = restTemplate.getForEntity("http://12-EUREKA-CLIENT-ZUUL-PROVIDER/test", String.class);
        return "没执行-------来自消费者Zuul的test------------"+result.getBody();
    }
    @RequestMapping("/test02")
    public String test02() {
        ResponseEntity<String>  result = restTemplate.getForEntity("http://12-eureka-client-api-gateway/api-zuul/test", String.class);
        return "执行了-------来自消费者Zuul的test------------"+result.getBody();
    }
}
  1. 请求测试2
    • http://localhost:8081/test 测试结果:通过Zuul的服务提供者
    • http://localhost:8082/test 测试结果:没执行-------来自消费者Zuul的test------------通过Zuul的服务提供者
    • http://localhost:8083/api-zuul/test 测试结果:通过Zuul的服务提供者
    • http://localhost:8082/test02 测试结果:执行了-------来自消费者Zuul的test------------通过Zuul的服务提供者

使用Zuul进行请求过滤

自定义过滤器类,这个类定义在API网关模块,

//自定义一个过滤器类用于拦截用户的请求 并继承Zuul提供的过滤器类
@Component
public class AuthFilter extends ZuulFilter {
    //过滤器类型 决定式在请求之前还是请求之后指定当前过滤器
    @Override
    public String filterType() {
        //返回pre表示要在请求之前执行过滤器,其他值还有post、error、route和static,当然也可以自定义。
        return "pre";
    }
    //过滤器的排序,如果有多个过滤器那么这些过滤器将按照返回值大小直接排序执行
    @Override
    public int filterOrder() {
        return 0;
    }
    //是否启动当前过滤器如果返回true表示将要执行这个过滤器,如果返回false表示不执行这个过滤器
    @Override
    public boolean shouldFilter() {
        return true;
    }
    //如果进入当前过滤后,这个方法就是过滤器中具体的业务逻辑
    //注意:这个返回值目前版本没有什么特殊作用 因此返回 null即可
    @Override
    public Object run() throws ZuulException {
        //获取当前请求的上下文对象
        RequestContext  requestContext=RequestContext.getCurrentContext();
        //获取当前请求对象
        HttpServletRequest request=requestContext.getRequest();
        //获取请求参数中的token数据(用户的身份令牌)
        String token=request.getParameter("token");
        if(token==null){//进入if表示当前请求中没有携带token我们任务当前请求是非法请求
            //通知ZuulAPI网关当前请求非法
            requestContext.setSendZuulResponse(false);
            //设置响应编码为401 表示权限不足
            requestContext.setResponseStatusCode(401);
            //设置响应的头文件信息以html或文本响应编码格式为utf-8
            requestContext.addZuulResponseHeader("content-type","text/html;charset=utf-8");
            //设置响应的内容
            requestContext.setResponseBody("非法访问");
        }else{
            System.out.println("用户携带了身份令牌需要验证这个身份是否真的合法");
        }
        return null;
    }
}

Zuul路由规则

  1. 简化配置
#配置路由规则
zuul.routes.api-zuul.path=/api-zuul/**
zuul.routes.api-zuul.serviceId=08-springcloud-eureka-client-zuul-consumer

当访问地址符合/api-zuul/**规则的时候,会被自动定位到08-springcloud-eureka-client-zuul-consumer服务上,不过两行代码有点麻烦,还可以简化为:

#zuul.routes后面跟着的是服务名,服务名后面跟着的是路径规则,这种配置方式更简单。
zuul.routes.08-springcloud-eureka-client-zuul-consumer =/api-zuul/**
  1. 忽略掉某些接口路径,添加配置
#忽略掉某一些接口路径
zuul.ignored-patterns=/**/hello/**
  1. 配置网关路由前缀
#配置网关路由的前缀
zuul.prefix=/myapi

此时我们的访问路径就变成了http://localhost:8080/myapi/api-zuul/test

  1. 路由规则通配符的含义:
通配符含义举例说明
匹配任意单个字符/05-springcloud-service-feign/?匹配 /05-springcloud-service-feign/a,/05-springcloud-service-feign/b,/05-springcloud-service-feign/c等
*匹配任意数量的字符/05-springcloud-service-feign/*匹配 /05-springcloud-service-feign/aaa,/05-springcloud-service-feign/bbb,/05-springcloud-service-feign/ccc等,无法匹配 /05-springcloud-service-feign/a/b/c
**匹配任意数量的字符/05-springcloud-service-feign/**匹配 /05-springcloud-service-feign/aaa,/05-springcloud-service-feign/bbb,/05-springcloud-service-feign/ccc等,也可以匹配 /05-springcloud-service-feign/a/b/c
  1. 在API网关服务上做一些特殊的业务逻辑处理:让请求到达API网关后,再转发给自己本身,由API网关自己来处理
    在08-springcloud-eureka-client-zuul项目中新建如下Controller:
@RestController
public class GateWayController {
    @RequestMapping("/api/local")
    public String hello() {
        return "exec the api gateway.";
    }
}

然后在application.properties文件中配置:

zuul.routes.gateway.path=/gateway/**
zuul.routes.gateway.url=forward:/api/local

Zuul的异常处理

Zuul请求的生命周期图:
在这里插入图片描述

  1. 正常情况下所有的请求都是按照pre、route、post的顺序来执行,然后由post返回response
  2. 在pre阶段,如果有自定义的过滤器则执行自定义的过滤器
  3. pre、routing、post的任意一个阶段如果抛异常了,则执行error过滤器

我们可以有两种方式统一处理异常:
1、禁用zuul默认的异常处理SendErrorFilter过滤器,然后自定义我们自己的Errorfilter过滤器

zuul.SendErrorFilter.error.disable=true
@Component
public class ErrorFilter extends ZuulFilter {
    private static final Logger logger = LoggerFactory.getLogger(ErrorFilter.class);
    @Override
    public String filterType() {
        return "error";
    }
    @Override
    public int filterOrder() {
        return 1;
    }
    @Override
    public boolean shouldFilter() {
        return true;
    }
    @Override
    public Object run() throws ZuulException {
        try {
            RequestContext context = RequestContext.getCurrentContext();
            ZuulException exception = (ZuulException)context.getThrowable();
            logger.error("进入系统异常拦截", exception);
            HttpServletResponse response = context.getResponse();
            response.setContentType("application/json; charset=utf8");
            response.setStatus(exception.nStatusCode);
            PrintWriter writer = null;
            try {
                writer = response.getWriter();
                writer.print("{code:"+ exception.nStatusCode +",message:\""+ exception.getMessage() +"\"}");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(writer!=null){
                    writer.close();
                }
            }
        } catch (Exception var5) {
            ReflectionUtils.rethrowRuntimeException(var5);
        }
        return null;
    }
}

2、自定义全局error错误页面

@RestController
public class ErrorHandlerController implements ErrorController {
    /**
     * 出异常后进入该方法,交由下面的方法处理
     */
    @Override
    public String getErrorPath() {
        return "/error";
    }
    @RequestMapping("/error")
    public Object error(){
        RequestContext ctx = RequestContext.getCurrentContext();
        ZuulException exception = (ZuulException)ctx.getThrowable();
        return exception.nStatusCode + "--" + exception.getMessage();
    }
}

注意:全局error错误页面与自定义异常的过滤器有冲突二选一即可

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值