HTTP服务组件 (HTTP Service)
API文档
https://tech.rydeen.com.cn/apidocs/http/4.6.0/index.html
组件作用
本组件是对HTTP请求访问技术的封装,对HTTP Client客户端的实现和业务代码进行了解耦,防止中间件的变更对业务代码的影响。同时,根据我们实际业务的需求,对HTTP请求的参数、请求头、请求体、响应体等进行了封装,提供了更加便捷的使用方式。目前组件内封装了OkHttp3的实现,后续会根据业务需求,增加其他HTTP客户端的实现。
组件用法
Http组件支持异步和同步两种请求方式,当使用异步请求时,需要创建一个自定义线程池来初始化Http组件,以便于异步请求的线程池管理。在这里我们推荐配合使用rydeen-component-threadpool
组件来创建动态线程池,以便更加弹性的控制线程池和接入监控。
配置文件
组件自身已经提供了默认的客户端实例创建,如果默认值不满足,请通过组件预留的配置文件接口,对Http组件的初始化进行自定义配置,具体类cn.com.rydeen.component.http.config.HttpProperties
,对应的配置文件为application.yml或bootstrap.yml,配置路径:platform.component.http.*
# 技术平台配置
platform:
# 组件配置
component:
# HTTP组件配置
http:
# 是否启用缓存
enable-cache: false
# 缓存路径
cache-path: /opt/okhttp3/cache/
# 缓存大小
cache-size: 100
# 调用超时时长
call-timeout-timeout: 10
# 调用超时时间单位(可选值:nanoseconds、microseconds、milliseconds、seconds、minutes、hours、days)
call-timeout-time-unit: seconds
# 连接超时时长
connect-timeout: 10
# 连接超时时间单位(可选值:nanoseconds、microseconds、milliseconds、seconds、minutes、hours、days)
connect-timeout-time-unit: seconds
# HTTP访问日志级别(可选值:none、basic、headers、body)
level: body
# 读超时时长
read-timeout: 30
# 读超时时间单位(可选值:nanoseconds、microseconds、milliseconds、seconds、minutes、hours、days)
read-timeout-time-unit: seconds
# 写超时时长
write-timeout: 30
# 写超时时间单位(可选值:nanoseconds、microseconds、milliseconds、seconds、minutes、hours、days)
write-timeout-time-unit: seconds
调用示例
下面仅展示部分接口的调用示例,完整的调用示例请参考Javadoc。
异步调用的示例代码如下:
```java
package cn.com.rydeen.component.template.api.controller;
import cn.com.rydeen.component.http.bean.AsyncCallback;
import cn.com.rydeen.component.http.bean.HttpResponse;
import cn.com.rydeen.component.http.client.HttpClientApi;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* API DOC 测试控制器
*
* @author richie696
* @version 1.0
* @since 2022-10-01 20:27:39
*/
@Slf4j
@Tag(name = "ApiDoc测试接口")
@RequestMapping("/api-doc")
@RestController
@RequiredArgsConstructor
public class ApiDocDemoController {
private final HttpClientApi httpClientApi;
@GetMapping("/test")
public String test(@RequestParam(value = "url", required = false) String url) throws InterruptedException {
// 公平锁
final ReentrantLock lock = new ReentrantLock(true);
// 锁的条件
final Condition condition = lock.newCondition();
// 超时定义
final int WAIT_TIMEOUT = 15;
final TimeUnit WAIT_TIMEOUT_UNIT = TimeUnit.SECONDS;
// 结果
String[] result = new String[1];
Map<String, String> headers;
Map<String, String> param;
if (url == null) {
url = "https://partner-api.stg-myteksi.com/grabfood-sandbox/partner/v1/order/prepare";
headers = Map.of("Content-Type", "application/json", "Authorization", "1233");
param = Map.of("orderID", "123", "toState", "345");
} else {
headers = Map.of();
param = Map.of();
}
// 发送异步请求
httpClientApi.doAsyncPost(String.class, url, param, headers, new AsyncCallback<>() {
@Override
public void onFailure(IOException exception) {
lock.lock();
// 获取需要返回的值
result[0] = exception.getMessage();
condition.signal();
lock.unlock();
}
@Override
public void onResponse(HttpResponse response, String data) {
lock.lock();
// 获取需要返回的值
result[0] = String.valueOf(response.getCode());
condition.signal();
lock.unlock();
}
});
// 对主线程上锁
lock.lock();
// 阻塞主线程
boolean await = condition.await(WAIT_TIMEOUT, WAIT_TIMEOUT_UNIT);
// 释放锁
lock.unlock();
// 如果返回false,则表示网络请求不通
if (!await) {
return "error";
}
// 否则返回异步请求获得的结果
return result[0];
}
}
同步调用的示例代码如下:
package cn.com.rydeen.component.template.api.controller;
import cn.com.rydeen.component.http.bean.AsyncCallback;
import cn.com.rydeen.component.http.bean.HttpResponse;
import cn.com.rydeen.component.http.client.HttpClientApi;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* API DOC 测试控制器
*
* @author richie696
* @version 1.0
* @since 2022-10-01 20:27:39
*/
@Slf4j
@Tag(name = "ApiDoc测试接口")
@RequestMapping("/api-doc")
@RestController
@RequiredArgsConstructor
public class ApiDocDemoController {
private final HttpClientApi httpClientApi;
@GetMapping("/sendMenu")
public ResultVO<?> sendMenu() {
String url = "http://10.200.0.100:8080/pos/posService";
Map<String, Object> params = Map.of(
"storeCode", "02101F116-2201",
"deviceId", "01",
// .... 省略其他参数
);
TestResponse response = httpClientApi.doPost(TestResponse.class, url, params);
return ResultVO.success(response);
}
}
SOAP1.2的调用示例代码如下:
package cn.com.rydeen.component.template.api.controller;
import cn.com.rydeen.component.http.bean.AsyncCallback;
import cn.com.rydeen.component.http.bean.HttpResponse;
import cn.com.rydeen.component.http.client.HttpClientApi;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* API DOC 测试控制器
*
* @author richie696
* @version 1.0
* @since 2022-10-01 20:27:39
*/
@Slf4j
@Tag(name = "ApiDoc测试接口")
@RequestMapping("/api-doc")
@RestController
@RequiredArgsConstructor
public class ApiDocDemoController {
private final HttpClientApi httpClientApi;
@GetMapping("/soap")
public ResultVO<?> testSOAP() {
String url = "http://shfz01.sh-fengze.com:8008/sales.asmx";
String body = """
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<postsalescreate xmlns="http://tempurl.org">
<astr_request>
<header>
<licensekey></licensekey>
<username>010201</username>
<password>010201</password>
<lang></lang>
<pagerecords>100</pagerecords>
<pageno>100</pageno>
<updatecount>100</updatecount>
<messagetype>SALESDATA</messagetype>
<messageid>332</messageid>
<version>V332M</version>
</header>
<salestotal>
<txdate_yyyymmdd>20240312</txdate_yyyymmdd>
<txtime_hhmmss>143443</txtime_hhmmss>
<storecode>02101F116-2201</storecode>
<tillid>01</tillid>
<txdocno>290508084169687040</txdocno>
<cashier>1022</cashier>
</salestotal>
<salesitems>
<salesitem>
<itemcode>02101F116-220101</itemcode>
<qty>1</qty>
<netamount>5.70</netamount>
</salesitem>
</salesitems>
<salestenders>
<salestender>
<tendercode>CH</tendercode>
<payamount>5.70</payamount>
<baseamount>5.70</baseamount>
</salestender>
</salestenders>
</astr_request>
</postsalescreate>
</soap12:Body>
</soap12:Envelope>""";
httpClientApi.doAsyncPostSOAP1_2(TestResponse.class, url, body, new AsyncCallback<>() {
@Override
public void onFailure(IOException exception) {
log.error("请求失败", exception);
}
@Override
public void onResponse(HttpResponse response, TestResponse data) {
log.info("请求成功");
}
});
return ResultVO.success();
}
}