Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
2dafe93
init structure
YvCeung Oct 9, 2025
dd105cd
unify api
YvCeung Oct 9, 2025
9996b4e
modify http1 api
YvCeung Oct 9, 2025
10410e7
refactor clusterControllerTest
YvCeung Oct 9, 2025
70f08c4
Compatible with historical code
Oct 9, 2025
d42e3cd
unify http2 api
Oct 9, 2025
3a6bbbc
optimize code format
YvCeung Oct 9, 2025
2e880b4
optimize unit test case
YvCeung Oct 9, 2025
208a2ab
optimize http2 unit test case
YvCeung Oct 10, 2025
1625bfe
modify test case
YvCeung Oct 10, 2025
f3113a9
fix ut case
YvCeung Oct 10, 2025
0dc5686
format code
YvCeung Oct 10, 2025
ae8efe4
fix some ut case
YvCeung Oct 10, 2025
5b91c51
fix some ut case
YvCeung Oct 10, 2025
eadde63
Merge branch '2.x' into xiaoyu1009
YvCeung Oct 10, 2025
67a4ffc
optimize code
YvCeung Oct 11, 2025
f8af053
format code
YvCeung Oct 12, 2025
63d2189
fix error ut case
YvCeung Oct 12, 2025
7ade817
optimize code
YvCeung Oct 12, 2025
a54b947
optimize code
YvCeung Oct 12, 2025
8ecb132
Remove duplicate apis
YvCeung Oct 12, 2025
5b791f4
Supplement the implementation logic of the api
YvCeung Oct 12, 2025
01e44d1
Simplify the api of http2
YvCeung Oct 12, 2025
fb2efb4
format code
YvCeung Oct 12, 2025
7037727
add ut case
YvCeung Oct 12, 2025
2a39bde
add changes
YvCeung Oct 12, 2025
e15b3af
remove chinese doc
YvCeung Oct 15, 2025
6089844
Merge branch '2.x' into xiaoyu1009
YvCeung Oct 15, 2025
0777d6d
modify url build case
YvCeung Oct 15, 2025
3bb5013
Merge remote-tracking branch 'origin/xiaoyu1009' into xiaoyu1009
YvCeung Oct 15, 2025
93b77c0
Merge branch '2.x' into xiaoyu1009
YvCeung Oct 16, 2025
9ca1711
Merge branch '2.x' into xiaoyu1009
YvCeung Oct 17, 2025
568b7f0
Merge branch '2.x' into xiaoyu1009
YvCeung Oct 18, 2025
faf7c33
Merge branch '2.x' into xiaoyu1009
YvCeung Oct 19, 2025
5d94e4c
Merge remote-tracking branch 'origin/xiaoyu1009' into xiaoyu1009
YvCeung Oct 19, 2025
6ff77b4
fix compile error
YvCeung Oct 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/en-us/2.x.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ Add changes here for all PR submitted to the 2.x branch.

- [[#7615](https://github.com/seata/seata/pull/7615)] Refactor DataSourceProxy
- [[#7617](https://github.com/seata/seata/pull/7617)] Refactor Alibaba Dubbo and HSF
- [[#7684](https://github.com/apache/incubator-seata/pull/7684)] Refactor and unify the apis of http1 and http2


### doc:
Expand Down
1 change: 1 addition & 0 deletions changes/zh-cn/2.x.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@

- [[#7615](https://github.com/seata/seata/pull/7615)] 重构 DataSourceProxy
- [[#7617](https://github.com/seata/seata/pull/7617)] 重构 Alibaba Dubbo 和 HSF 模块
- [[#7684](https://github.com/apache/incubator-seata/pull/7684)] 重构并统一http1和http2的API


### doc:
Expand Down
1 change: 1 addition & 0 deletions common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.seata.common.util;
package org.apache.seata.common.http;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.NameValuePair;
Expand All @@ -31,6 +31,9 @@
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.seata.common.loader.LoadLevel;
import org.apache.seata.common.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -43,9 +46,10 @@
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class HttpClientUtil {
@LoadLevel(name = "Http1", order = 1)
public class Http1HttpExecutor implements HttpExecutor {

private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientUtil.class);
private static final Logger LOGGER = LoggerFactory.getLogger(Http1HttpExecutor.class);

private static final Map<Integer /*timeout*/, CloseableHttpClient> HTTP_CLIENT_MAP = new ConcurrentHashMap<>();

Expand All @@ -54,6 +58,25 @@ public class HttpClientUtil {

private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

public static volatile Http1HttpExecutor instance;

/**
* Gets instance.
*
* @return the instance
*/
public static Http1HttpExecutor getInstance() {

if (instance == null) {
synchronized (Http1HttpExecutor.class) {
if (instance == null) {
instance = new Http1HttpExecutor();
}
}
}
return instance;
}
Comment on lines +63 to +78
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this instance need to be a singleton?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think some resources may need to be reused here, such as the connection pool here, and using a singleton can avoid the state inconsistency problems caused by multiple instances and the performance problems caused by frequent object creation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HTTP_CLIENT_MAP is already managed as a static final field.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this instance need to be a singleton?

当我们希望通过 HTTP/2 发起请求时,不仅需要检测客户端是否支持,还必须确认 Seata Server 端 是否具备 HTTP/2 能力。

如果直接使用抽象类提供的通用 API 发起调用,可能会出现这样的问题:客户端最终使用了 HTTP/2 实现类,但服务端并不支持,从而导致不可预期的异常。原因在于当前的依赖检测逻辑中,尚未包含对 Server 端协议版本 的校验。

因此,为了保证在某些场景下能够安全运行,我暂时为每个实现类单独保留了 getInstance() 方法,便于手动选择合适的实现。

待后续在依赖探测中集成 “Server 端 HTTP/2 支持判断” 能力后,这些单例获取方法就可以被移除。届时,所有 HTTP 请求都可统一通过 HttpExecutorFactory.getInstance() 进行调用。

This logic serves as a transitional solution.

When we intend to initiate requests over HTTP/2, we must verify not only the client’s capability but also whether the Seata Server supports HTTP/2.

If we directly invoke the generic API provided by the abstract class, it’s possible that the actual implementation used on the client side is based on HTTP/2 while the server does not support it. This could lead to unpredictable exceptions, since the current dependency check mechanism does not include validation for the server-side protocol version.

To ensure stability under such conditions, each implementation class temporarily provides its own getInstance() method, allowing manual selection of the proper executor.

Once the capability detection logic is enhanced to determine whether the server supports HTTP/2, these per-class singleton access methods can be safely removed. At that point, all HTTP requests can be uniformly initiated via HttpExecutorFactory.getInstance().


static {
POOLING_HTTP_CLIENT_CONNECTION_MANAGER.setMaxTotal(10);
POOLING_HTTP_CLIENT_CONNECTION_MANAGER.setDefaultMaxPerRoute(10);
Expand All @@ -69,9 +92,9 @@ public class HttpClientUtil {
})));
}

// post request
public static CloseableHttpResponse doPost(
String url, Map<String, String> params, Map<String, String> header, int timeout) throws IOException {
@Override
public HttpResult doPost(String url, Map<String, String> params, Map<String, String> header, int timeout)
throws IOException {
try {
URIBuilder builder = new URIBuilder(url);
URI uri = builder.build();
Expand All @@ -96,24 +119,27 @@ public static CloseableHttpResponse doPost(
httpPost.setEntity(stringEntity);
}
}
CloseableHttpClient client = HTTP_CLIENT_MAP.computeIfAbsent(timeout, k -> HttpClients.custom()
.setConnectionManager(POOLING_HTTP_CLIENT_CONNECTION_MANAGER)
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectionRequestTimeout(timeout)
.setSocketTimeout(timeout)
.setConnectTimeout(timeout)
.build())
.build());
return client.execute(httpPost);
CloseableHttpClient client = getHttpClient(timeout);
try (CloseableHttpResponse response = client.execute(httpPost)) {
int statusCode = response.getStatusLine() != null
? response.getStatusLine().getStatusCode()
: 0;

String responseBody = null;
if (response.getEntity() != null) {
responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
}

return new HttpResult(statusCode, responseBody, null);
}
} catch (URISyntaxException | ClientProtocolException e) {
LOGGER.error(e.getMessage(), e);
}
return null;
}

// post request
public static CloseableHttpResponse doPost(String url, String body, Map<String, String> header, int timeout)
throws IOException {
@Override
public HttpResult doPost(String url, String body, Map<String, String> header, int timeout) throws IOException {
try {
URIBuilder builder = new URIBuilder(url);
URI uri = builder.build();
Expand All @@ -129,24 +155,28 @@ public static CloseableHttpResponse doPost(String url, String body, Map<String,
httpPost.setEntity(stringEntity);
}
}
CloseableHttpClient client = HTTP_CLIENT_MAP.computeIfAbsent(timeout, k -> HttpClients.custom()
.setConnectionManager(POOLING_HTTP_CLIENT_CONNECTION_MANAGER)
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectionRequestTimeout(timeout)
.setSocketTimeout(timeout)
.setConnectTimeout(timeout)
.build())
.build());
return client.execute(httpPost);
CloseableHttpClient client = getHttpClient(timeout);
try (CloseableHttpResponse response = client.execute(httpPost)) {
int statusCode = response.getStatusLine() != null
? response.getStatusLine().getStatusCode()
: 0;

String responseBody = null;
if (response.getEntity() != null) {
responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
}

return new HttpResult(statusCode, responseBody, null);
}
} catch (URISyntaxException | ClientProtocolException e) {
LOGGER.error(e.getMessage(), e);
}
return null;
}

// get request
public static CloseableHttpResponse doGet(
String url, Map<String, String> param, Map<String, String> header, int timeout) throws IOException {
@Override
public HttpResult doGet(String url, Map<String, String> param, Map<String, String> header, int timeout)
throws IOException {
try {
URIBuilder builder = new URIBuilder(url);
if (param != null) {
Expand All @@ -159,40 +189,33 @@ public static CloseableHttpResponse doGet(
if (header != null) {
header.forEach(httpGet::addHeader);
}
CloseableHttpClient client = HTTP_CLIENT_MAP.computeIfAbsent(timeout, k -> HttpClients.custom()
.setConnectionManager(POOLING_HTTP_CLIENT_CONNECTION_MANAGER)
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectionRequestTimeout(timeout)
.setSocketTimeout(timeout)
.setConnectTimeout(timeout)
.build())
.build());
return client.execute(httpGet);
CloseableHttpClient client = getHttpClient(timeout);
try (CloseableHttpResponse response = client.execute(httpGet)) {
int statusCode = response.getStatusLine() != null
? response.getStatusLine().getStatusCode()
: 0;

String responseBody = null;
if (response.getEntity() != null) {
responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
}

return new HttpResult(statusCode, responseBody, null);
}
} catch (URISyntaxException | ClientProtocolException e) {
LOGGER.error(e.getMessage(), e);
}
return null;
}

public static CloseableHttpResponse doPostJson(
String url, String jsonBody, Map<String, String> headers, int timeout) throws IOException {
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(timeout)
.setConnectTimeout(timeout)
.build();

HttpPost post = new HttpPost(url);
post.setConfig(requestConfig);

if (headers != null) {
headers.forEach(post::addHeader);
}
post.setHeader("Content-Type", "application/json");

StringEntity entity = new StringEntity(jsonBody, StandardCharsets.UTF_8);
post.setEntity(entity);

CloseableHttpClient client = HttpClients.createDefault();
return client.execute(post);
private static CloseableHttpClient getHttpClient(int timeout) {
return HTTP_CLIENT_MAP.computeIfAbsent(timeout, k -> HttpClients.custom()
.setConnectionManager(POOLING_HTTP_CLIENT_CONNECTION_MANAGER)
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectionRequestTimeout(timeout)
.setSocketTimeout(timeout)
.setConnectTimeout(timeout)
.build())
.build());
}
}
Loading
Loading