是spring支持的一个请求httpt的模板对象,支持各种常用http客户端,
默认是使用JDK原生的URLConnection
@Bean
public RestTemplate rest(){
RestTemplate restTemplate = new RestTemplate();
也可以在这里设置httpfactory,如httpclient作为客户端,
spring提供了几个ClientHttpRequestFactory实现类如SimpleClientHttpRequestFactory
restTemplate.setRequestFactory(new SimpleClientHttpRequestFactory());
设置拦截器
restTemplate.setInterceptors(new ArrayList());
return restTemplate;
}
@Autowired
private RestTemplate restTemplate;
原理
RestTemplate 继承InterceptingHttpAccessor ,实现RestOperations(定义常用操作)
InterceptingHttpAccessor 继承HttpAccessor
主要方法getRequestFactory()
@Override
public ClientHttpRequestFactory getRequestFactory() {
拦截器集合
List interceptors = getInterceptors();
if (!CollectionUtils.isEmpty(interceptors)) {
ClientHttpRequestFactory factory = this.interceptingRequestFactory;
if (factory == null) {
如果存在返回带拦截器的请求工厂
factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
this.interceptingRequestFactory = factory;
}
return factory;
}
else {
return super.getRequestFactory();//默认SimpleClientHttpRequestFactory使用HttpURLConnection
}
}
然后就是发请求了
gateway内部使用netty作为服务器,我感觉也是用netty做客户端转发请求
默认没重试但是有RetryGatewayFilter重试过滤器
使用
filters:
- name: Retry
- args:
retries: 3
series: #状态码配置(分段)
- SERVER_ERROR
statuses:
- OK
methods:
- GET
- POST
exceptions:
- java.io.IOException
超时时间
spring:
cloud:
gateway:
httpclient:
connect-timeout: 1000
response-timeout: 5s
routes:
- id: route
uri: lb://rout
predicates:
- Path=/api/**
#filters:
#- name: AuthCheckFilter
metadata:
response-timeout: 200
connect-timeout: 200
LoadbalancerClientFilter:默认(已不推荐使用)
ReactiveLoadBalancerClientFilter:负载均衡拦截器,可参考RoundRobinLoadBalancer
默认使用ribbon负载均衡,可通过IRule自定义策略
@Qualifier有两个实现类时通过@Qualifier(“a”)名字指定注入类
还可以获取指定注解的类如ribbon的@LoadBalanced上有@Qualifier
ribbon可以和RestTemplate 一起使用,原理是加了一个RestTemplate 拦截器,
和RestTemplate连用时用的是RestTemplate发请求,自己只是生成url
ribbon五大核心类
IPing:客户端用于快速检查服务器当时是否处于活动状态(心跳检测)
IRule:负载均衡策略,用于确定从服务器列表返回哪个服务器
ServerList:可以响应客户端的特定服务的服务器列表
ServerListFilter:可以动态获得的具有所需特征的候选服务器列表的过滤器
ServerListUpdater:用于执行动态服务器列表更新
/**
* 单独使用ribbon
*
* @param args
*/
public static void main(String[] args) throws Exception {
// 写入服务列表
ConfigurationManager.getConfigInstance().setProperty("client.ribbon.listOfServers", "localhost:8085,localhost:8081");
// 输出服务列表
System.out.println("服务列表:" + ConfigurationManager.getConfigInstance().getProperty("client.ribbon.listOfServers"));
// 获取客户端(如果获取不到,可通过getNamedClient方法自动创建)
RestClient client = (RestClient) ClientFactory.getNamedClient("client");
// 创建request对象
HttpRequest request = HttpRequest.newBuilder().uri(new URI("/a")).build();// 写入将要访问的接口
// 多次访问测试
for (int i = 0; i < 5; i++) {
// 创建response对象
HttpResponse response = client.executeWithLoadBalancer(request);
// 接收请求结果
String json = response.getEntity(String.class);
// 打印结果
System.out.println(json);
}
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
RibbonAutoConfiguration
创建
SpringClientFactory(RibbonClientConfiguration.class, NAMESPACE=“ribbon”, “ribbon.client.name”)-》NamedContextFactory;
RibbonClientConfiguration配置了各种组件包括HttpClientRibbonConfiguration
@ConditionalOnClass(name = “org.apache.http.client.HttpClient”)
@ConditionalOnProperty(name = “ribbon.httpclient.enabled”, matchIfMissing = true)
public class HttpClientRibbonConfiguration
创建
new RibbonLoadBalancerClient(springClientFactory()) -》LoadBalancerClient;
LoadBalancerAutoConfiguration
@LoadBalanced
@Autowired(required = false)
private List restTemplates = Collections.emptyList();
ribbon:
# 最大连接数
MaxTotalConnections=500
# 每个host最大连接数
MaxConnectionsPerHost=500
OkToRetryOnAllOperations: false #对所有操作请求都进行重试,默认false
ReadTimeout: 5000 #负载均衡超时时间,默认值5000
ConnectTimeout: 3000 #ribbon请求连接的超时时间,默认值2000
MaxAutoRetries: 0 #对当前实例的重试次数,默认0
MaxAutoRetriesNextServer: 1 #对切换实例的重试次数,默认1
retryableStatusCodes:404 #可以根据接口返回的状态码判断是否重试其他服务
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RetryRule #负载均衡配置为重试策略
# 负载策略调整
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.ZoneAvoidanceRule
最简单的方法就是用Ribbon自带的重试策略,还可以通过Spring Retry进行重试
#从注册中心刷新服务器列表信息的时间间隔,默认为2000毫秒,即2秒
ServerListRefreshInterval: 2000
GET方式请求无论是连接异常还是读取异常,都会进行重试;
非GET方式请求,只有连接异常时,才会进行重试
单独使用
/**
* 单独使用feign
*
* @param args
*/
public static void main(String[] args) throws Exception {
//Feign可以设置拦截器,http客户端,重试,超时等配置
feignclient bs = Feign.builder()
.options(new Request.Options(2000, 6000))
//proxy代理生成实现类
.target(feignclient.class, "http://localhost:8085");
System.out.println(bs.a());
}
interface feignclient{
@RequestLine("GET /a")
//@Headers("Content-Type: application/json")
//@Body("%7B\"id\": \"{id}\", \"name\": \"{name}\"%7D")
String a();
}
原理
@Import(FeignClientsRegistrar.class)
设置实例化的自定义处理
-》beanDefinition.setInstanceSupplier(instanceSupplier);
public @interface EnableFeignClients
FeignAutoConfiguration,FeignClientsConfiguration,FeignRibbonClientAutoConfiguration
FeignClientFactoryBean
RXJAVA
feign 集成了 ribbon 和 hystrix(和ribbon 集成使用feign的客户端发请求,ribbon用做服务负载)
feign的重试与ribbon存在冲突,springboot默认关闭了feign自身的重试机制.
引入ribbon包后,默认会自动开启ribbon重试机制. 自己需要配置ribbon的超时时间,只有ribbon的超时时间小于hystrix的超时熔断时间时,才会进行重试.
Feign的请求:其实是Hystrix+Ribbon。Hystrix在最外层,然后再到Ribbon,最后里面的是http请求。所以说。Hystrix的熔断时间必须大于Ribbon的 ( ConnectTimeout + ReadTimeout )。而如果Ribbon开启了重试机制,还需要乘以对应的重试次数,保证在Ribbon里的请求还没结束时,Hystrix的熔断时间不会超时
Spring Cloud中使用Feign进行微服务调用分为两层:Hystrix的调用和Ribbon的调用
Feign自身的超时配置会被Ribbon的超时时间覆盖
开启feign重试机制
@Bean
public Retryer feignRetryer() {
return new Retryer.Default();
}
feign:
client:
config:
default:
#feign的配置,feign是包含ribbon的我感觉这些不用配置,直接配置ribbon的就行了
connectTimeout: 500
readTimeout: 3000
hystrix:
enabled: true
command:
#全局默认配置
default:
#线程隔离相关
execution:
timeout:
#是否给方法执行设置超时时间,默认为true。一般我们不要改。
enabled: true
isolation:
#配置请求隔离的方式,这里是默认的线程池方式。还有一种信号量的方式semaphore,使用比较少。
strategy: threadPool
thread:
#方式执行的超时时间,默认为1000毫秒,在实际场景中需要根据情况设置
timeoutInMilliseconds: 10000
#发生超时时是否中断方法的执行,默认值为true。不要改。
interruptOnTimeout: true
#是否在方法执行被取消时中断方法,默认值为false。没有实际意义,默认就好!
interruptOnCancel: false
circuitBreaker: #熔断器相关配置
enabled: true #是否启动熔断器,默认为true,false表示不要引入Hystrix。
requestVolumeThreshold: 20 #启用熔断器功能窗口时间内的最小请求数,假设我们设置的窗口时间为10秒,
sleepWindowInMilliseconds: 5000 #所以此配置的作用是指定熔断器打开后多长时间内允许一次请求尝试执行,官方默认配置为5秒。
errorThresholdPercentage: 50 #窗口时间内超过50%的请求失败后就会打开熔断器将后续请求快速失败掉,默认配置为50
feign:
okhttp:
enabled: true
httpclient: #统一的配置参数,无论是httpclient还是okhttpclient都可以
connection-timeout: 8000
max-connections: 2
/**
* feign 请求拦截器
*/
@Component
public class FeignRequestInterceptor implements RequestInterceptor
{
@Override
public void apply(RequestTemplate requestTemplate)
{
HttpServletRequest httpServletRequest = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes().getRequest();
if (StringUtils.isNotNull(httpServletRequest))
{
String userId= httpServletRequest.getHeader("userId");
// 传递用户信息请求头,防止丢失
requestTemplate.header("userId", userId);
// 配置客户端IP
requestTemplate.header("X-Forwarded-For", IpUtils.getIpAddr(ServletUtils.getRequest()));
}
}
}
package ws;
import com.netflix.hystrix.HystrixThreadPoolKey;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariable;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
import com.netflix.hystrix.strategy.properties.HystrixProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 当Feign的隔离策略为THREAD时,因为当使用该隔离策略时,是没办法拿到 ThreadLocal 中的值的,
* 须要自定义策略(重写THREAD隔离策略)
*/
//@Component
public class FeignConfig extends HystrixConcurrencyStrategy {
private static final Logger log = LoggerFactory.getLogger(FeignConfig.class);
private HystrixConcurrencyStrategy delegate;
public FeignConfig() {
try {
this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
if (this.delegate instanceof FeignConfig) {
// Welcome to singleton hell...
return;
}
HystrixCommandExecutionHook commandExecutionHook =
HystrixPlugins.getInstance().getCommandExecutionHook();
HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();
HystrixPropertiesStrategy propertiesStrategy =
HystrixPlugins.getInstance().getPropertiesStrategy();
this.logCurrentStateOfHystrixPlugins(eventNotifier, metricsPublisher, propertiesStrategy);
HystrixPlugins.reset();
HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
} catch (Exception e) {
log.error("Failed to register Sleuth Hystrix Concurrency Strategy", e);
}
}
private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier,
HystrixMetricsPublisher metricsPublisher, HystrixPropertiesStrategy propertiesStrategy) {
if (log.isDebugEnabled()) {
log.debug("Current Hystrix plugins configuration is [" + "concurrencyStrategy ["
+ this.delegate + "]," + "eventNotifier [" + eventNotifier + "]," + "metricPublisher ["
+ metricsPublisher + "]," + "propertiesStrategy [" + propertiesStrategy + "]," + "]");
log.debug("Registering Sleuth Hystrix Concurrency Strategy.");
}
}
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
return new WrappedCallable<>(callable, requestAttributes);
}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixProperty<Integer> corePoolSize, HystrixProperty<Integer> maximumPoolSize,
HystrixProperty<Integer> keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
return this.delegate.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime,
unit, workQueue);
}
@Override
public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
return this.delegate.getBlockingQueue(maxQueueSize);
}
@Override
public <T> HystrixRequestVariable<T> getRequestVariable(HystrixRequestVariableLifecycle<T> rv) {
return this.delegate.getRequestVariable(rv);
}
static class WrappedCallable<T> implements Callable<T> {
private final Callable<T> target;
private final RequestAttributes requestAttributes;
public WrappedCallable(Callable<T> target, RequestAttributes requestAttributes) {
this.target = target;
this.requestAttributes = requestAttributes;
}
@Override
public T call() throws Exception {
try {
RequestContextHolder.setRequestAttributes(requestAttributes);
return target.call();
} finally {
RequestContextHolder.resetRequestAttributes();
}
}
}
}
package ws;
import com.netflix.hystrix.exception.HystrixBadRequestException;
import feign.FeignException;
import feign.Response;
import feign.codec.ErrorDecoder;
import org.springframework.stereotype.Component;
//@Component
public class MyErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
Exception exception =null;
if (200 != response.status() ){
exception = new HystrixBadRequestException("request exception wrapper");
}else{
System.out.println("ok");
}
return exception;
}
}
nginx请求大小限制
client_max_body_size 10m; #最大接受10m文件
sendfile on; 设置为on表示启动高效传输文件的模式
keepalive_timeout 1800;保持连接的时间,默认65s
springweb模块
spring.servlet.multipart.max-file-size=5MB
spring.servlet.multipart.max-request-size=20MB
gateway也支持
servlet:
multipart:
max-request-size: 20MB
max-file-size: 5MB
HTTP1.0 基本上都是浏览器接受响应数据后关闭,服务器处理完关闭(close其实没有先后顺序)
HTTP1.1 因为基本上都带Connection: keep-alive头,所以大多数情况是不会马上关闭
@Bean
public ConfigurableServletWebServerFactory configurableServletWebServerFactory() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
connector.setPort(8090);
// 最大线程数
protocol.setMaxThreads(2);
// 最大连接数
protocol.setMaxConnections(10);
protocol.setKeepAliveTimeout(1000);
protocol.setMaxKeepAliveRequests(10);//最大数
tomcat.addAdditionalTomcatConnectors(connector);
return tomcat;
}