Httpclient之http post请求 +spring-retry异常方法重试

简介

HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然在 JDK 的 java net包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。

最近我所负责的项目需要向其他项目调用接口,用到了HttpClients实现请求调用,并且对消息反馈做业务功能。下面总结一下使用方式!

HttpClient的主要功能

  • 实现了所有 HTTP 的方法(GET、POST、PUT、HEAD、DELETE、HEAD、OPTIONS 等)
  • 支持 HTTPS 协议
  • 支持代理服务器(Nginx等)等
  • 支持自动(跳转)转向…

实战

准备环境

  • Springboot + JDK1.8

引入依赖

<!-- Json工具 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.78</version>
</dependency>

<!--解析json数据工具类,如JsonPath-->
<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>2.4.0</version>
</dependency>

<!-- http工具连接依赖 -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.5</version>
</dependency>

响应体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Response implements Serializable {

    private String code;
    private String message;
    private Object content;
    
}

http服务端代码

这是一个post请求,使用@RequestBody方式接收一个String类型的参数!

注意,请求的时候Content-Type需要设置为application/json;charset=utf-8

@Slf4j
@RestController
@RequestMapping("/hzy")
public class HzyController {

    @PostMapping("/receiveData")
    public Response receiveData(@RequestBody String jsonTask) {
        SaveOrUpdateTask task = JSON.parseObject(jsonTask, SaveOrUpdateTask.class);
        System.out.println(task);

        log.info("接收项目管理数据:{}", jsonTask);
        // 这里可以处理接受到的数据
        ...
        return new Response("00000","成功", "返回成功");
    }
}

http客户端调用示例(Post请求)

客户端这里使用了@Async异步执行方法

使用方式:

  • @Async声明在需要异步执行的方法上
  • @EnableAsync声明在启动类上即可
@Async
@Override
public void pushTaskToHzy(SaveOrUpdateTask task) {
    HttpPost httpPost = new HttpPost("http://192.168.1.31:8081/hzy/receiveData");
  	// 数据转换成json字符串
    String jsonString = JSON.toJSONString(task);
    StringEntity entity = new StringEntity(jsonString, "UTF-8");
    // 把数据放入请求体中
    httpPost.setEntity(entity);
    // 由于服务端是post请求使用了@RequestBody注解,这边设置Content-Type
    httpPost.setHeader("Content-Type", "application/json;charset=utf8");

    HttpResponse response;
    try {
        // 执行post方法
        response = HttpClients.createDefault().execute(httpPost);
        // 获取响应体
        HttpEntity responseEntity = response.getEntity();
        if (responseEntity != null) {
            // 解析响应体
            String res = EntityUtils.toString(responseEntity, Charsets.UTF_8);
            log.info("响应内容为:{}", res);
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                // 使用json解析工具,获取数据
                String code = JsonPath.read(res, "$.code");
                String message = JsonPath.read(res, "$.message");
                
                // 这里做后续的业务功能处理
                ......
				return;
            }
            throw new BusinessException("响应状态失败,状态码:" + statusCode);
        }
    } catch (Exception e) {
        log.error("接收消息反馈失败了", e);
        throw new BusinessException("接收消息反馈失败了.原因:" + e.getMessage());
    }
}

一个简单的http post调用远程接口就完成了,那么假如网络不稳定,请求调用了之后无响应获取出现异常了,我们想要完成http远程调用的重试,当然httpclient自带的就有这种实现方式,不过感觉有些麻烦,还是去找了别的实现方式,就是使用spring-retry这个spring提供的重试工具。

spring-retry依赖如下

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.2.5.RELEASE</version>
</dependency>

Spring提供的声明式的重试类库spring-retry,既然是声明式,那么使用起来是很简单的了!

先来介绍两个注解

  • @Retryable:这个注解可以去声明重试的一些机制,比如重试次数、重试间隔之类
  • @Recover:用于当重试全部失败后的处理

@Retryable具体的方法参数可以参考@Retryable具体的方法参数,介绍的还是挺详细的 0.0

spring-retry的使用

实现步骤:

  1. @Retryable、@Recover定义在方法上
  2. @EnableRetry声明在启动类上

@Retryable参数

  • value = BusinessException.class,value表示当出现什么异常的时候进行重试,可以自定义一个异常,程序可能出现问题的地方直接抛出异常,然后retry组件会自动捕获到并且执行方法重试。
  • maxAttempts = 4,表示这个方法会一共执行4次(如果一直失败)

启动类

@EnableAsync
@EnableRetry
@SpringBootApplication
public class ProducerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProducerApplication.class, args);
    }
}

方法

@Async
@Override
@Retryable(value = BusinessException.class,maxAttempts = 4,backoff = @Backoff(delay = 2000,multiplier = 1.5))
public void pushTaskToHzy(SaveOrUpdateTask task) {
    HttpPost httpPost = new HttpPost("http://192.168.1.31:8081/hzy/receiveData");
  	// 数据转换成json字符串
    String jsonString = JSON.toJSONString(task);
    StringEntity entity = new StringEntity(jsonString, "UTF-8");
    // 把数据放入请求体中
    httpPost.setEntity(entity);
    // 由于服务端是post请求使用了@RequestBody注解,这边设置Content-Type
    httpPost.setHeader("Content-Type", "application/json;charset=utf8");

    HttpResponse response;
    try {
        // 执行post方法
        response = HttpClients.createDefault().execute(httpPost);
        // 获取响应体
        HttpEntity responseEntity = response.getEntity();
        if (responseEntity != null) {
            // 解析响应体
            String res = EntityUtils.toString(responseEntity, Charsets.UTF_8);
            log.info("响应内容为:{}", res);
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                // 使用json解析工具,获取数据
                String code = JsonPath.read(res, "$.code");
                String message = JsonPath.read(res, "$.message");
                
                // 这里做后续的业务功能处理
                ......
				return;
            }
            throw new BusinessException("响应状态失败,状态码:" + statusCode);
        }
    } catch (Exception e) {
        log.error("接收消息反馈失败了", e);
        throw new BusinessException("接收消息反馈失败了.原因:" + e.getMessage());
    }
}

后续的失败处理

定义一个方法,参数是上面代码抛出的异常类,和上面代码方法中的参数(重要:参数类型一定要一致,否则就直接报错了)

【注意】@Recover 的用法。它要求它注解的方法的返回值必须和@Retryable的注解的方法返回值保持一致,否则@Recover 注解声明的方法不会被调用。

要点:

  • @Recover和@Retryable声明的方法返回值要一致
  • @Recover和@Retryable声明的方法参数类型要一致(@Recover注解的方法,第一个参数是异常类,其他参数是@Retryable方法上的形参)

=注意:此方法一定要和上面的代码方法放在一个类中=

@Recover
public void failHandler(BusinessException e, SaveOrUpdateTask task) {
    log.error("重试失败3次后,处理数据中==", e);
    MProjectRefNodeEntity nodeEntity = nodeService.getByCode(task.getSequenceNumber(), null);
    nodeEntity.setTaskPushCode(ManagerCloudConstants.PUSH_FAIL);
    nodeEntity.setTaskPushMsg(message);
    nodeService.updateById(nodeEntity);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值