【业务功能篇 146】Sping之RestTemplate http请求封装+配置类

本文介绍了如何在Springboot应用中使用MybatisPlus和ShedLock框架,通过RestTemplate进行第三方接口调用,处理通用的JSON转换,并探讨了如何优化请求配置,包括连接超时、SSL安全验证等,以确保高效和稳定的API通信。
摘要由CSDN通过智能技术生成

业务场景: 我们在开发过程中,除了后端开发接口,提供前端请求之外,有时也需要进行请求其他第三方接口数据,因为有些数据是其他服务器提供的一些公共服务,我们需要调用其他服务的第三方接口,获取数据,或者得到某个功能,这里我们在 【业务功能篇16】Springboot+mybatisplus+ShedLock框架根据一定的逻辑数据处理规则,定时任务生成告警单 这篇文章中分析的功能设计过程中,就不乏涉及需要调用第三方的接口,比如告警单需要给用户成员分别推送,这里就需要调用我们相应的推送服务功能,传递相应的参数,对应的员工信息,通过聊天社交软件进行推送, 也有需要给成员分别去生成一个 电子流待办信息,这个待办服务,也是需要调用第三方平台接口,然后通过页面展示当前用户的待办情况

【业务功能篇18】Springboot request请求第三方接口 JSONObject.parseObject 通用转换响应类-CSDN博客

@Slf4j
@Service
public class httpAdapter {

	@Resource
    RestTemplate restTemplate;
	@Value("${interface.ms-url}")
    private String baseUrl;

	/**
     * queryData
     * 可以放在某个工具类下,通用化
     * @param DataParam DataParam 请求第三方接口的参数VO
     * @return ResponseVo
     */
    public ResponseVo queryData(DataParam DataParam) {
        String queryUrl = baseUrl + "queryData";
        // 构造请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        //如果有鉴权认证根据需求可以在请求头加Authorization
        String token = "xxxxxxxx";
        headers.add("Authorization", token);
        String paramJsonString = JSON.toJSONString(DataParam);
        // 查询
        log.info("Start invoking, url = {}, query param = {}", queryUrl, paramJsonString);
        HttpEntity<String> request = new HttpEntity<>(paramJsonString, headers);
        ResponseEntity<JSONObject> response = null;
        String errorMsg = "";
        JSONObject body = new JSONObject();
        long start = System.currentTimeMillis();
        try {
            response = restTemplate.postForEntity(queryUrl, request, JSONObject.class);
            body = response.getBody();
        } catch (RestClientException exception) {
            errorMsg = StringUtils.abbreviate(exception.toString(), 2048);
            log.error(errorMsg);
        }
        log.info("After invoking , cost {} s", (System.currentTimeMillis() - start) / 1000f);
        // 解析结果
        return changeResponse(body, errorMsg);
    }


    /**
     * changeResponse
     * 
     * @param body body
     * @param errorMsg errorMsg
     * @return ResponseVo
     */
    private ResponseVo changeResponse(JSONObject body, String errorMsg) {
        if (!"".equals(errorMsg) || body == null || body.get("result") == null) {
            return ResponseUtils.errorResponse("query  error! message = " + errorMsg);
        }
		//第三方接口返回的定义,通过result获取到数据  这里返回的是一个list 所以下面解析成数组
        String jsonString = JSON.toJSONString(body.get("result"));
        String msg = String.valueOf(body.get("message"));
		//这里直接转换对应的数据封装类也可以得到这个对象集合,看整体开发封装需求决定
        //List<ResData> DataParam = JSON.parseArray(jsonString, ResData.class);
        List<JSONObject> listData = JSON.parseObject(jsonString, ArrayList.class);
        log.info("AteBrain result: code = {}, message = {}, size = {}", body.get("code"), msg, listData.size());
        return ResponseUtils.successResponse(listData, msg);
    }
}

Service层进行调用 第三方请求接口 

    //这里直接@Autowired注入bean也可以 
    private final AteBrainAdapter httpAdapter= SpringBeanUtils.getBean(AteBrainAdapter.class);  

	//上层调用 第三方接口返回数据 这里可以写在上层service,跟上面写的调用http接口解耦开
    public void getData() {
		//请求第三方接口的参数实体VO  传递查询时间段内的数据
        DataParam DataParam = DataParam.builder().startTime("2024-03-10").endTime("2024-03-15").build();
		//调用第三方接口方法,传入参数,返回数据,这个封装类是我们自定义的
        ResponseVo responseVo = httpAdapter.queryData(DataParam);
       
		//获取对应的数据,此时还是JSON对象类型的list数据
		List<JSONObject> jsonRsps = (List<JSONObject>) responseVo.getData();
		//通过stream转换  JSONObject先转换string的json字符串  不过这里应该多转了一次 第一次的map可以不用,因为第二次map里面又转换了string
		//parseObject("json字符串",对应转换的实体类),就可以吧json字符串转换成业务对应封装的实体类,
		//这里也是需要基于第三方接口返回的json数据构建,比如{"count":1,"code": "机器码"} 那么对应的class类就包含这两个属性去接收转换,名字一样
		List<ResData> DataParams = jsonRsps.stream().map(String::valueOf)
				.map(e -> JSON.parseObject(String.valueOf(e), ResData.class))
				.collect(Collectors.toList());
		//到此拿到数据,转换好了 下面就根据具体业务情况去做数据的插入或者分析的动作
        
    }

最后,需要自定义一个配置类:

import lombok.extern.slf4j.Slf4j;

import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLContext;

/**
 * restTemplate 配置类
 */
@Slf4j
@Configuration
public class RestTemplateConfig {

    private static final int CONNECT_TIMEOUT = 60000; // 连接超时 60000ms

    private static final int SOCKET_TIMEOUT = 180000; // socket超时 180000ms

    private static final int READ_TIMEOUT = 180000; // 读超时 180000ms

    private static final int REQUEST_TIMEOUT = 30000; // 请求超时 30000ms

    private static final int MAX_CONN_TOTAL = 500; // 最大连接数500

    private static final int MAX_CONN_ROUTE = 500; // 最大并发数500

    private static final int RETRY_TIMES = 2; // 重试次数2

    private static final int TIME_TO_LIVE = 30; // 长连接保持时长 30s

    /**
     * restTemplate
     *
     * @return restTemplate
     */
    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate(simpleClientHttpRequestFactory());
        restTemplate.getMessageConverters().add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
        return restTemplate;
    }

    /**
     * 自定义ClientHttpRequestFactory配置
     *
     * @return ClientHttpRequestFactory
     */
    @Bean
    public static ClientHttpRequestFactory simpleClientHttpRequestFactory() {
        // 长连接保持时长30秒
        PoolingHttpClientConnectionManager pollingConnectionManager = new PoolingHttpClientConnectionManager(
            TIME_TO_LIVE, TimeUnit.SECONDS);
        // 最大连接数
        pollingConnectionManager.setMaxTotal(MAX_CONN_TOTAL);
        // 单路由的并发数
        pollingConnectionManager.setDefaultMaxPerRoute(MAX_CONN_ROUTE);
        HttpClientBuilder httpClientBuilder = HttpClients.custom();
        httpClientBuilder.setConnectionManager(pollingConnectionManager);
        // 重试次数2次,并开启
        httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(RETRY_TIMES, true));
        // 保持长连接配置,需要在头添加Keep-Alive
        httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy());
        HttpClient httpClient = httpClientBuilder.build();
        // httpClient连接底层配置clientHttpRequestFactory
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(
            httpClient);
        clientHttpRequestFactory.setReadTimeout(READ_TIMEOUT); // ms
        clientHttpRequestFactory.setConnectTimeout(CONNECT_TIMEOUT); // ms
        return clientHttpRequestFactory;
    }

    /**
     * 通过SSLContextBuilder绕过https安全验证
     *
     * @return restTemplateSSL
     */
    @Bean
    public RestTemplate restTemplateSsl() {
        ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
        try {
            HttpClient httpClient = httpClientCrossOverSsl(getDefaultReqConfig());
            requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        } catch (Exception e) {
            log.error("init ClientHttpRequestFactory error.", e);
        }
        return new RestTemplate(requestFactory);
    }

    /**
     * Apache HttpClient 绕过ssl 验证
     *
     * @param requestConfig 请求配置
     * @return httpClientCrossOverSSL
     * @throws Exception 异常
     */
    public HttpClient httpClientCrossOverSsl(RequestConfig requestConfig) throws Exception {
        SSLContext sslContext = SSLContextBuilder.create()
            .useProtocol(SSLConnectionSocketFactory.SSL)
            .loadTrustMaterial((x, y) -> true)
            .build();
        return HttpClientBuilder.create()
            .setDefaultRequestConfig(requestConfig)
            .setSSLContext(sslContext)
            .setSSLHostnameVerifier((x, y) -> true)
            .setMaxConnTotal(MAX_CONN_TOTAL) // 最大连接数
            .setMaxConnPerRoute(MAX_CONN_ROUTE) // 每个路由设置并发数
            .build();
    }


    /**
     * getDefaultReqConfig
     * 
     * @return RequestConfig
     */
    private RequestConfig getDefaultReqConfig() {
        return RequestConfig.custom().setSocketTimeout(SOCKET_TIMEOUT) // 服务器返回数据(response)的时间,超时抛出read timeout
            .setConnectTimeout(SOCKET_TIMEOUT) // 连接上服务器(握手成功)的时间,超时抛出connect timeout
            .setConnectionRequestTimeout(REQUEST_TIMEOUT) // 从连接池中获取连接的超时时间,超时抛出ConnectionPoolTimeoutException
            .build();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值