Spring Cloud中, 服务又该如何调用 ?

各个服务以HTTP接口形式暴露 , 各个服务底层以HTTP Client的方式进行互相访问。

SpringCloud开发中,Feign是最方便,最为优雅的服务调用实现方式。

Feign 是一个声明式,模板化的HTTP客户端,可以做到用HTTP请求访问远程服务就像调用本地方法一样。简单搭建步骤如下 :

1. 首先加入pom.xml

<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-openfeign</artifactId>
</dependency>

2. 主类上面添加注解@EnableFeignClients,该注解表示当程序启动时,会进行包扫描,默认扫描所有带@FeignClient注解的类进行处理

package name.ealen;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients; /**
* Created by EalenXie on 2018/10/12 18:24.
*/
@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
public class FeignOpenClientApplication { public static void main(String[] args) {
SpringApplication.run(FeignOpenClientApplication.class,args);
}
}

3. 简单配置appliation.yml 注册到Eureka Server。

server:
port: 8090
spring:
application:
name: spring-cloud-feign-openClient
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/

4. 使用@FeignClient为本应用声明一个简单的能调用的客户端。为了方便,找个现成的开放接口,比如Github开放的api,GET /search/repositories。

GitHub接口文档 : https://developer.github.com/v3/search/#search-repositories

package name.ealen.client;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; /**
* Created by EalenXie on 2019/1/9 11:28.
*/
@FeignClient(name = "github-client", url = "https://api.github.com")
public interface GitHubApiClient { @RequestMapping(value = "/search/repositories", method = RequestMethod.GET)
String searchRepositories(@RequestParam("q") String queryStr);
}

其中,@FeignClient 即是指定客户端信息注解,务必声明在接口上面,url手动指定了客户端的接口地址。

5. 为其写一个简单Controller进行一波测试 :

package name.ealen.web;

import name.ealen.client.GitHubApiClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /**
* Created by EalenXie on 2018/10/12 18:32.
*/
@RestController
public class FeignOpenClientController { @Resource
private GitHubApiClient gitHubApiClient; @RequestMapping("/search/github/repository")
public String searchGithubRepositoryByName(@RequestParam("name") String repositoryName) {
return gitHubApiClient.searchRepositories(repositoryName);
}
}

6. 依次启动Eureka Server,和该应用。然后访问 : http://localhost:8090/search/github/repository?name=spring-cloud-dubbo

注 : 有时候在测试的时候,很容易报500 null的异常,可能是因为GitHub连接拒绝的原因,这里只是为了测试,所以可以忽略,多尝试几次即可。

关于Feign Client配置细节

1. 重点配置 @FeignClient 注解,我这里专门对源码属性做了说明 :

在上例中,我们只是简单的指定了name和url属性,如果需要专门针对该客户端进行属性按需调整,可以调整以下参数 属性值 :

package org.springframework.cloud.netflix.feign;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient { @AliasFor("name")
String value() default ""; @Deprecated
String serviceId() default ""; @AliasFor("value")
String name() default ""; String qualifier() default ""; String url() default ""; boolean decode404() default false; Class<?>[] configuration() default {}; Class<?> fallback() default void.class; Class<?> fallbackFactory() default void.class; String path() default ""; boolean primary() default true;
}
name:               指定Feign Client的名称,如果项目使用了 Ribbon,name属性会作为微服务的名称,用于服务发现。
serviceId: 用serviceId做服务发现已经被废弃,所以不推荐使用该配置。
value: 指定Feign Client的serviceId,如果项目使用了 Ribbon,将使用serviceId用于服务发现,但上面可以看到serviceId做服务发现已经被废弃,所以也不推荐使用该配置。
qualifier: 为Feign Client 新增注解@Qualifier
url: 请求地址的绝对URL,或者解析的主机名
decode404: 调用该feign client发生了常见的404错误时,是否调用decoder进行解码异常信息返回,否则抛出FeignException。
fallback: 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback 指定的类必须实现@FeignClient标记的接口。实现的法方法即对应接口的容错处理逻辑。
fallbackFactory: 工厂类,用于生成fallback 类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码。
path: 定义当前FeignClient的所有方法映射加统一前缀。
primary: 是否将此Feign代理标记为一个Primary Bean,默认为ture

例如我们要为其添加一个fallback的容错,和覆盖掉默认的configuration。

1. 首先为其添加一个fallback容错的处理类.
package name.ealen.client;

/**
* Created by EalenXie on 2018/11/11 19:19.
*/
public class GitHubApiClientFallBack implements GitHubApiClient { @Override
public String searchRepositories(String queryStr) {
return "call github api fail";
}
}
2. 然后为其添加一个默认配置类,为了方便了解,我这里只是写了一下默认的配置。
package name.ealen.config;

import feign.Contract;
import feign.Logger;
import feign.Retryer;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; @Configuration
public class GitHubFeignConfiguration {
/**
* Feign 客户端的日志记录,默认级别为NONE
* Logger.Level 的具体级别如下:
* NONE:不记录任何信息
* BASIC:仅记录请求方法、URL以及响应状态码和执行时间
* HEADERS:除了记录 BASIC级别的信息外,还会记录请求和响应的头信息
* FULL:记录所有请求与响应的明细,包括头信息、请求体、元数据
*/
@Bean
Logger.Level gitHubFeignLoggerLevel() {
return Logger.Level.FULL;
}
}

注意 : 编码器,解码器,重试器,调用解析器请谨慎配置,一般来说默认就行。笔者对这些配置研究得很浅,所以没有写自定义的配置。

3. 此时我们修改我们的GitHubApiClient。指定上面两个类即可
package name.ealen.client;
import name.ealen.config.GitHubFeignConfiguration;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/**
* Created by EalenXie on 2019/1/9 11:28.
*/
@FeignClient(name = "github-client", url = "https://api.github.com", path = "", serviceId = "", qualifier = "", fallback = GitHubApiClientFallBack.class, decode404 = false, configuration = GitHubFeignConfiguration.class)
public interface GitHubApiClient {
@RequestMapping(value = "/search/repositories", method = RequestMethod.GET)
String searchRepositories(@RequestParam("q") String queryStr);
}
4. Feign也支持属性文件对上面属性的配置,比如下面的配置和GitHubFeignConfiguration的配置是等价的 :
feign:
client:
config:
##对名字为 github-client 的feign client做配置
github-client: # 对应GitHubApiClient类的@FeignClient的name属性值
decoder404: false # 是否解码404
loggerLevel: full # 日志记录级别

2. 重点配置 @EnableFeignClients 注解,我这里专门对源码属性做了说明 :

package org.springframework.cloud.netflix.feign;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import org.springframework.context.annotation.Import; @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients { String[] value() default {}; String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<?>[] defaultConfiguration() default {}; Class<?>[] clients() default {};
}
value:                  等价于basePackages属性,更简洁的方式
basePackages: 指定多个包名进行扫描
basePackageClasses: 指定多个类或接口的class,扫描时会在这些指定的类和接口所属的包进行扫描。
defaultConfiguration: 为所有的Feign Client设置默认配置类
clients: 指定用@FeignClient注释的类列表。如果该项配置不为空,则不会进行类路径扫描。

同样的,为所有Feign Client 也支持文件属性的配置,如下 :

feign:
client:
config:
# 默认为所有的feign client做配置(注意和上例github-client是同级的)
default:
connectTimeout: 5000 # 连接超时时间
readTimeout: 5000 # 读超时时间设置

注 : 如果通过Java代码进行了配置,又通过配置文件进行了配置,则配置文件的中的Feign配置会覆盖Java代码的配置。

但也可以设置feign.client.defalult-to-properties=false,禁用掉feign配置文件的方式让Java配置生效。

3. Feign 请求和响应开启GZIP压缩,提高通讯效率

1. 配置如下:
feign:
compression:
request:
enable: true #配置请求支持GZIP压缩,默认为false
mime-types: text/xml, application/xml, application/json #配置压缩支持的Mime Type
min-request-size: 2048 #配置压缩数据大小的上下限
reponse:
enable: true #配置响应支持GZIP压缩,默认为false

对应配置源码可以看看 :

2. 由于开启GZIP压缩之后,Feign之间的调用通过二进制协议进行传输,返回的值需要修改为ResponseEntity<byte[]>才可以正常显示,否则会导致服务之间的调用结果乱码。
3. 例如此时修改GitHubApiClient类的Feign client的配置 :
import name.ealen.config.GitHubFeignConfiguration;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/**
* Created by EalenXie on 2019/1/9 11:28.
*/
@FeignClient(name = "github-client", url = "https://api.github.com", path = "", serviceId = "", qualifier = "", decode404 = false, configuration = GitHubFeignConfiguration.class)
public interface GitHubApiClient { // @RequestMapping(value = "/search/repositories", method = RequestMethod.GET)
// Object searchRepositories(@RequestParam("q") String queryStr); @RequestMapping(value = "/search/repositories", method = RequestMethod.GET)
ResponseEntity<byte[]> searchRepositories(@RequestParam("q") String queryStr);
}

4. Feign超时配置

feign的调用分为两层。ribbon和hystrix(默认集成),默认情况下,hystrix是关闭的,所以当ribbon发生超时异常时,可以如下配置调整ribbon超时时间 :

#ribbon的超时时间
ribbon:
ReadTimeout: 60000 # 请求处理的超时时间
ConnectTimeout: 30000 # 请求连接的超时时间

至于为什么这个配置会生效,我们可以大概看一下源码里面相关 键值对 的描述 :

DefaultClientConfigImpl中 有许多的属性键配置 :

CommonClientConfigKey :

AbstractRibbonCommand 中的 ribbon 和 hystrix都用到的 getRibbonTimeout()方法 :

默认情况下,feign中的hystrix是关闭的。

如果开启了hystrix。此时的ribbon的超时时间和Hystrix的超时时间的结合就是Feign的超时时间,当hystrix发生了超时异常时,可以如下配置调整hystrix的超时时间 :

feign:
hystrix:
enable: true
hystrix:
shareSecurityContext: true # 设置这个值会自动配置一个Hystrix并发策略会把securityContext从主线程传输到你使用的Hystrix command
command:
default:
execution:
isolation:
thread:
timeoutInMillisecond: 10000 # hystrix超时时间调整 默认为1s
circuitBreaker:
sleepWindowInMilliseconds: 10000 # 短路多久以后开始尝试是否恢复,默认5s
forceClosed: false # 是否允许熔断器忽略错误,默认false, 不开启

关于HystrixCommandProperties类的以上配置说明,详细可以参阅 : https://www.jianshu.com/p/b9af028efebb

注 : 当开启了Ribbon之后,可能会出现首次调用失败的情况。

原因 : 因为hystrix的默认超时时间是1s,而feign首次的请求都会比较慢,如果feign的响应时间(ribbon响应时间)大于了1s,就会出现调用失败的问题。

解决方法 :

1. 将Hystrix的超时时间尽量修改得长一点。(有时候feign进行文件上传的时候,如果时间太短,可能文件还没有上传完就超时异常了,这个配置很有必要)
2. 禁用Hystirx的超时时间 : hystrix.command.default.execution.timeout.enabled=false
3. Feign直接禁用Hystrix(不推荐) : feign.hystrix.enabled=false

Feign 的HTTP请求相关

1. Feign 默认的请求 Client 替换

feign在默认情况下使用JDK原生的URLConnection 发送HTTP请求。(没有连接池,保持长连接)

1. 使用HTTP Client替换默认的Feign Client

引入pom.xml :

<!--Apache HttpClient 替换Feign原生的httpclient-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>

配置application.yml支持httpclient :

feign:
httpclient:
enable: true
2. 使用okhttp替换Feign默认的Client

引入pom.xml :

<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>

配置application.yml支持okhttp :

feign:
httpclient:
enable: false
okhttp:
enable: true

配置okhttp :

import feign.Feign;
import okhttp3.ConnectionPool;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.netflix.feign.FeignAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import java.util.concurrent.TimeUnit; @Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FeignOkHttpConfig {
@Bean
public okhttp3.OkHttpClient okHttpClient() {
return new okhttp3.OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS) //设置连接超时
.readTimeout(60, TimeUnit.SECONDS) //设置读超时
.writeTimeout(60, TimeUnit.SECONDS) //设置写超时
.retryOnConnectionFailure(true) //是否自动重连
.connectionPool(new ConnectionPool()) //构建OkHttpClient对象
.build();
}
}

2. Feign的Get多参数传递

Feign 默认不支持GET方法直接绑定POJO的,目前解决方式如下 :

1. 把POJO拆散成一个个单独的属性放在方法参数里面;
2. 把方法的参数变成Map传递;
3. GET传递@RequestBody。(此方式违反了Restful规范,而且我们一般不会这样写)

《重新定义Spring Cloud实战》一书中介绍了一种最佳实践方式,通过Feign的拦截器的方式进行处理。实现原理是通过Feign的RequestInterceptor中的apply方法,统一拦截转换处理Feign中的GET方法多参数。处理如下 :

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils; import java.io.IOException;
import java.util.*; @Component
public class FeignRequestInterceptor implements RequestInterceptor { @Autowired
private ObjectMapper objectMapper; @Override
public void apply(RequestTemplate template) {
//feign 不支持GET方法传POJO,json body 转query
if (template.method().equals("GET") && template.body() != null) {
try {
JsonNode jsonNode = objectMapper.readTree(template.body());
template.body(null);
Map<String, Collection<String>> queries = new HashMap<>();
buildQuery(jsonNode, "", queries);
template.queries(queries);
} catch (IOException e) {
//提示:根据实践项目情况处理此处异常,这里不做扩展。
e.printStackTrace();
}
}
} private void buildQuery(JsonNode jsonNode, String path, Map<String, Collection<String>> queries) {
if (!jsonNode.isContainerNode()) { // 叶子节点
if (jsonNode.isNull()) return;
Collection<String> values = queries.computeIfAbsent(path, k -> new ArrayList<>());
values.add(jsonNode.asText());
return;
}
if (jsonNode.isArray()) { // 数组节点
Iterator<JsonNode> it = jsonNode.elements();
while (it.hasNext()) {
buildQuery(it.next(), path, queries);
}
} else {
Iterator<Map.Entry<String, JsonNode>> it = jsonNode.fields();
while (it.hasNext()) {
Map.Entry<String, JsonNode> entry = it.next();
if (StringUtils.hasText(path))
buildQuery(entry.getValue(), path + "." + entry.getKey(), queries);
else // 根节点
buildQuery(entry.getValue(), entry.getKey(), queries);
}
}
}
}

3. feign的文件上传

1. 首先我们编写一个简单文件上传服务的应用,并将其注册到Eureka Server上面

简单配置一下,application的name为feign-file-upload-application。为其写一个上传的接口 :

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import java.io.File; @RestController
public class FeignUploadController {
private static final Logger log = LoggerFactory.getLogger(FeignUploadController.class);
@PostMapping(value = "/server/uploadFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String fileUploadServer(MultipartFile file) throws Exception {
log.info("upload file name : {}", file.getName());
//上传文件放到 /usr/temp/uploadFile/ 目录下
file.transferTo(new File("/usr/temp/uploadFile/" + file.getName()));
return file.getOriginalFilename();
}
}
2. 编写一个要使用上传功能的feign 客户端 :

feign客户端应用还需要加入依赖,pom.xml :

<!-- Feign文件上传依赖-->
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form-spring</artifactId>
<version>3.0.3</version>
</dependency>

客户端指定接口信息 :

import name.ealen.config.FeignMultipartSupportConfiguration;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile; @FeignClient(value = "feign-file-upload-application", configuration = FeignMultipartSupportConfiguration.class)
public interface FileUploadFeignService {
/***
* 1.produces,consumes必填
* 2.注意区分@RequestPart和RequestParam,不要将
* : @RequestPart(value = "file") 写成@RequestParam(value = "file")
*/
@RequestMapping(method = RequestMethod.POST, value = "/uploadFile/server", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String fileUpload(@RequestPart(value = "file") MultipartFile file);
}
import feign.form.spring.SpringFormEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import feign.codec.Encoder;
/**
* Feign文件上传Configuration
*/
@Configuration
public class FeignMultipartSupportConfiguration {
@Bean
@Primary
@Scope("prototype")
public Encoder multipartFormEncoder() {
return new SpringFormEncoder();
}
}

注意 : 文件上传功能的feign client 与其他的feign client 配置要分开,因为用的是不同的Encoder和处理机制,以免互相干扰,导致请求抛Encoder不支持的异常。

4. feign的调用传递headers里面的信息内容

默认情况下,当通过Feign调用其他的服务时,Feign是不会带上当前请求的headers信息的。

如果我们需要调用其他服务进行鉴权的时候,可能会需要从headers中获取鉴权信息。则可以通过实现Feign的拦截RequestInterceptor接口,进行获取headers。然后手动配置到feign请求的headers中去。

import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration; @Component
public class FeignHeadersInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String keys = headerNames.nextElement();
String values = request.getHeader(keys);
template.header(keys, values);
}
}
}
}

Spring Cloud Feign 总结的更多相关文章

  1. 笔记:Spring Cloud Feign Ribbon 配置

    由于 Spring Cloud Feign 的客户端负载均衡是通过 Spring Cloud Ribbon 实现的,所以我们可以直接通过配置 Ribbon 的客户端的方式来自定义各个服务客户端调用的参 ...

  2. 笔记:Spring Cloud Feign Hystrix 配置

    在 Spring Cloud Feign 中,除了引入了用户客户端负载均衡的 Spring Cloud Ribbon 之外,还引入了服务保护与容错的工具 Hystrix,默认情况下,Spring Cl ...

  3. 笔记:Spring Cloud Feign 其他配置

    请求压缩 Spring Cloud Feign 支持对请求与响应进行GZIP压缩,以减少通信过程中的性能损耗,我们只需要通过下面二个参数设置,就能开启请求与响应的压缩功能,yml配置格式如下: fei ...

  4. 笔记:Spring Cloud Feign 声明式服务调用

    在实际开发中,对于服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以我们通常会针对各个微服务自行封装一些客户端类来包装这些依赖服务的调用,Spring Cloud Feign 在此基础上做了进 ...

  5. 第六章:声明式服务调用:Spring Cloud Feign

    Spring Cloud Feign 是基于 Netflix Feign 实现的,整合了 Spring Cloud Ribbon 和 Spring Cloud Hystrix,除了提供这两者的强大功能 ...

  6. Spring Cloud Feign Ribbon 配置

    由于 Spring Cloud Feign 的客户端负载均衡是通过 Spring Cloud Ribbon 实现的,所以我们可以直接通过配置 Ribbon 的客户端的方式来自定义各个服务客户端调用的参 ...

  7. Spring Cloud feign

    Spring Cloud feign使用 前言 环境准备 应用模块 应用程序 应用启动 feign特性 综上 1. 前言 我们在前一篇文章中讲了一些我使用过的一些http的框架 服务间通信之Http框 ...

  8. 微服务架构之spring cloud feign

    在spring cloud ribbon中我们用RestTemplate实现了服务调用,可以看到我们还是需要配置服务名称,调用的方法 等等,其实spring cloud提供了更优雅的服务调用方式,就是 ...

  9. Spring Cloud Feign 在调用接口类上,配置熔断 fallback后,输出异常

    Spring Cloud Feign 在调用接口类上,配置熔断 fallback后,出现请求异常时,会进入熔断处理,但是不会抛出异常信息. 经过以下配置,可以抛出异常: 将原有ErrorEncoder ...

  10. RestTemplate OR Spring Cloud Feign 上传文件

    SpringBoot,通过RestTemplate 或者 Spring Cloud Feign,上传文件(支持多文件上传),服务端接口是MultipartFile接收. 将文件的字节流,放入ByteA ...

随机推荐

  1. C11简洁之道:循环的改善

    1.  for循环的新用法 在C++98/03中,通过for循环对一个容器进行遍历,一般有两种方法,常规的for循环,或者使用<algorithm>中的for_each方法. for循环遍 ...

  2. Proxmap Sort

    这个排序是桶排序和基数排序的改进,理解了前两者,这个排序很容易理解 先回忆下桶排序是怎么回事,它与桶的区别在于入桶规则,桶排序里是1入1号桶,2入2号桶 这个排序把数字分区了,然后给出一个所谓的键,例 ...

  3. How to write educational schema.

    Sometimes, writing such educational schemas could be of much use, and creating such docs can be bene ...

  4. Java面试通关要点汇总集(山东数漫江湖)

    这里,笔者结合自己过往的面试经验,整理了一些核心的知识清单,帮助读者更好地回顾与复习 Java 服务端核心技术.本文会以引出问题为主,后面有时间的话,笔者陆续会抽些重要的知识点进行详细的剖析与解答.敬 ...

  5. jQuery mobile 滑动打开面板

    一.首先在<head></head>里面引入jQuery库.jQuery mobile库以及jQuery mobile样式 <link rel="stylesh ...

  6. Python3.3.3 安装(Linux系统)

    1.wget http://www.python.org/ftp/python/3.3.3/Python-3.3.3.tgz //检查http://www.python.org/ftp/python网 ...

  7. 在电脑中配置adb

    在环境变量的系统变量path中添加SDK中platform_tools和tools的路径 如果出现version说明配置成功

  8. 火狐浏览器下点击a标签时出现虚线的解决方案

    1.兼容性问题 火狐浏览器下点击a标签时出现虚线 2.解决方案 a:focus { outline: none;}

  9. Vue组件-动态组件

    动态组件 通过使用保留的 <component> 元素,动态地绑定到它的 is 特性,可以让多个组件使用同一个挂载点,并动态切换: <div id="app6"& ...

  10. c#上传文件时,当选择的文件为0kb,会验证不通过

    FileUpload1.HasFile 当FileUpload1控件选择的文件为0KB时,FileUpload1.HasFile返回false