restTemplate做长连接

restTemplate是springboot自带的http请求客户端,里面包装了HttpClient,是在SpringBoot项目中进行http请求常用的方式,本篇文章主要是讲解怎么进行restTemplate的长连接

一、普通情况下的RestTemplate配置

    @Bean
    public RestTemplate AchiementTemplateLongConnection(RestTemplateBuilder builder) {
         return builder.build();
    }

二、长连接的配置

下面说下怎么进行RestTemplate的配置

    @Bean
    public RestTemplate AchiementTemplateLongConnection(RestTemplateBuilder builder) {
         return builder.requestFactory(()->httpComponentsClientHttpRequestFactory())
                .build();
    }

    @Bean
    public HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory() {
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
        factory.setHttpClient(httpClient(poolingHttpClientConnectionManager(),requestConfig()));
        return factory;
    }

    @Bean
    public RequestConfig requestConfig() {
        RequestConfig result = RequestConfig.custom()
                .setConnectionRequestTimeout(0)
                .setConnectTimeout(CONNECTION_TIMEOUT)
                .setSocketTimeout(CONNECTION_TIMEOUT)
                .build();

        return result;
    }

    @Bean
    public CloseableHttpClient httpClient(PoolingHttpClientConnectionManager poolingHttpClientConnectionManager, RequestConfig requestConfig) {
        CloseableHttpClient result = HttpClientBuilder
                .create()
                .setConnectionManager(poolingHttpClientConnectionManager)
                .setDefaultRequestConfig(requestConfig)
                .setRetryHandler(new AchivmentGetHttpRequestRetryHandler(1,true))
                .build();

        return result;
    }

    @Bean
    public PoolingHttpClientConnectionManager poolingHttpClientConnectionManager() {
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        connectionManager.setValidateAfterInactivity(2000);
        return connectionManager;
    }

下面对几个方法返回的类进行解释下:

  • httpClient:http请求的客户端
  • RequestConfig:httpClient请求的配置包括超时时间等
  • PoolingHttpClientConnectionManager:设置连接池,节省了创建连接的开销

三、长连接中需要注意的问题

试想一个问题,长连接是有时间的,不管是8小时还是2小时,当连接断开了,怎么重连是需要处理的,有两种方式可以进行处理:

  1. 连接之前先校验下连接是否通着,mysql的长连接就是通过ping命令去校验的
  2. 做重试,RestTemplate就是这么做的,捕获IOException,然后进行重试,因为之前断开的连接在第一次失败后,被销毁了,后面再一次重试即可成功了。

==注:==这里要特别说明下,httpComponents包里面并没有那么傻,自己断开的连接自己肯定可以感知的,但是当你是客户端,而服务端单方面断开连接的时候,就感知不到了。

最开始我是在检查,我代码中已经设置的检验,为何还是检验不出来呢,我设置检验的代码如下:

connectionManager.setValidateAfterInactivity(2000);

为何检验的代码没有生效呢?原因在这里:BHttpConnectionBase类的方法


    @Override
    public boolean isStale() {
        if (!isOpen()) {
            return true;
        }
        try {
            final int bytesRead = fillInputBuffer(1);
            return bytesRead < 0;
        } catch (final SocketTimeoutException ex) {
        //如果是SocketTimeoutException
            return false;
        } catch (final IOException ex) {
            return true;
        }
    }

我们看下当服务端关闭连接的时候,客户端报什么异常:

java.net.SocketInputStream.socketRead0(Native Method)抛出了timeoutException

正好是返回连接没有问题,就是说这个检验是无效的。

那这个时候只能重试了:
在RestTemplate中使用了DefaultHttpRequestRetryHandler类进行重试,里面有一个retryRequest方法就是用来判断是否重试的。里面有这么一段代码

        if (handleAsIdempotent(request)) {
            // Retry if the request is considered idempotent
            return true;
        }

这段代码是判断是否有消息体,如果有消息体则不能重试,这是为了避免如果是插入或者修改的方法,不能轻易重试,否则会有风险。但是如果你的查询请求是post,那么也不会重试,可以自己实现一个retryHandler解决此问题。

四、最后

我贴上mysql的ping逻辑,在此方法中:
com.mysql.jdbc.ConnectionImpl#execSQL(com.mysql.jdbc.StatementImpl, java.lang.String, int, com.mysql.jdbc.Buffer, int, int, boolean, java.lang.String, com.mysql.jdbc.Field[], boolean)

public ResultSetInternalMethods execSQL(StatementImpl callingStatement, String sql, int maxRows, Buffer packet, int resultSetType, int resultSetConcurrency,
            boolean streamResults, String catalog, Field[] cachedMetadata, boolean isBatch) throws SQLException {
        synchronized (getConnectionMutex()) {
            //
            // Fall-back if the master is back online if we've issued queriesBeforeRetryMaster queries since we failed over
            //

            long queryStartTime = 0;

            int endOfQueryPacketPosition = 0;

            if (packet != null) {
                endOfQueryPacketPosition = packet.getPosition();
            }

            if (getGatherPerformanceMetrics()) {
                queryStartTime = System.currentTimeMillis();
            }

            this.lastQueryFinishedTime = 0; // we're busy!

            if ((getHighAvailability()) && (this.autoCommit || getAutoReconnectForPools()) && this.needsPing && !isBatch) {
                try {
                    pingInternal(false, 0);

                    this.needsPing = false;
                } catch (Exception Ex) {
                    createNewIO(true);
                }
            }

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
要配置长连接的话,你可以使用`HttpComponentsClientHttpRequestFactory`来替代默认的`SimpleClientHttpRequestFactory`。这样可以使用Apache HttpClient作为底层的HTTP客户端,从而实现长连接的功能。 首先,你需要导入Apache HttpClient的依赖。如果你使用的是Maven,可以在`pom.xml`文件中添加以下依赖: ```xml <dependencies> ... <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency> ... </dependencies> ``` 然后,你可以创建一个`RestTemplate`实例,并设置`HttpComponentsClientHttpRequestFactory`作为其请求工厂。这样就可以启用长连接了。 ```java import org.apache.http.client.HttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; ... // 创建HttpClient实例 HttpClient httpClient = HttpClientBuilder.create() .setMaxConnTotal(100) // 设置最大连接数 .setMaxConnPerRoute(20) // 设置每个路由的最大连接数 .build(); // 创建HttpComponentsClientHttpRequestFactory实例,并设置HttpClient HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); // 创建RestTemplate实例,并设置请求工厂 RestTemplate restTemplate = new RestTemplate(requestFactory); ``` 通过以上配置,你可以使用`restTemplate`进行HTTP请求,而且会复用连接,从而提高性能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值