项目的通信

在开发的过程中,我们平时会面临两种通信,一种是项目之间的通信,一种是项目以外的通信。对于前者,我们通常会使用openFeign来解决。而项目以外的通信我们则会有restTemplate、okHttp以及Retrofit等众多通信框架。我之前也是一直使用restTemplate,但最近接触到okHttp发现,用着比restTemplate更省心更方便一点,并且支持异步通信。这次就针对okHttp来简单做一下文章。

为什么选择okHttp

从性能方面考虑,其支持HTTP2并且使用链接池复用资源;在实际使用方面,只需要进行一次封装,后续即可直接调用,省心省力;最后就是支持异步通信功能;这些对于日常开发完全够用了,下面直接在项目中进行演示

项目实践

引入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--okhttp-->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.11.0</version>
</dependency>

<!--jackson 序列化对象使用-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.0</version>
</dependency>

<!--引入hutool工具类包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>

okHttp工具类

这个工具类是我自己封装的一个,提供了get、post的请求,并在其基础上分别实现了异步的通信调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
/**
* @Classname OkHttpUtils
* @Description http请求工具类
* @Date 2023/09/05 13:26
* @Author by Sora33
*/
public class OkHttpUtils {

private static final Logger logger = LoggerFactory.getLogger(OkHttpUtils.class);

private static final String METHOD_GET = "get";
private static final String METHOD_POST = "post";

private static ObjectMapper objectMapperService = SpringUtil.getBean(ObjectMapper.class);

private static OkHttpClient okHttpClient;

static {
// 设置默认连接和超时时间都是5s
OkHttpUtils.okHttpClient = new OkHttpClient.Builder()
.connectTimeout(5L, TimeUnit.SECONDS)
.readTimeout(5L, TimeUnit.SECONDS)
.build();
}


/**
* 发起get请求调用
* @param URL 请求地址
* @param paramMap 参数map
* @param headersMap 请求头map
* @return
*/
public static String get(String URL, HashMap<String, Object> paramMap, Map<String, String> headersMap) {
// 获取headers
Headers headers = getHeaders(headersMap);
// 获取参数
String param = getParam(paramMap);
// 拼接URL
URL = URL + param;

logger.info("本次访问请求地址:[{}],请求方式:[{}],正在发起请求……", URL, OkHttpUtils.METHOD_GET);

Request request = getRequest(URL, headers, OkHttpUtils.METHOD_GET);

return newCall(request);
}


/**
* 以异步方式发起get请求调用
* @param URL 请求地址
* @param paramMap 参数map
* @param headersMap 请求头map
* @return
*/
public static void getAsync(String URL, HashMap<String, Object> paramMap, Map<String, String> headersMap) {
// 获取headers
Headers headers = getHeaders(headersMap);
// 获取参数
String param = getParam(paramMap);
// 拼接URL
URL = URL + param;

logger.info("本次访问请求地址:[{}],请求方式:[{}],正在发起请求……", URL, OkHttpUtils.METHOD_GET);

Request request = getRequest(URL, headers, OkHttpUtils.METHOD_GET);

// 发起异步调用
newCallAsync(request);
}


/**
* 发起get请求调用
* @param URL 请求地址
* @param paramMap 参数map
* @return
*/
public static String get(String URL, HashMap<String, Object> paramMap) {
// 获取headers
Headers headers = getHeaders(new HashMap<>());
// 获取参数
String param = getParam(paramMap);
// 拼接URL
URL = URL + param;

logger.info("本次访问请求地址:[{}],请求方式:[{}],正在发起请求……", URL, OkHttpUtils.METHOD_GET);

Request request = getRequest(URL, headers, OkHttpUtils.METHOD_GET);

return newCall(request);
}


/**
* 以异步方式发起get请求调用
* @param URL 请求地址
* @param paramMap 参数map
* @return
*/
public static void getAsync(String URL, HashMap<String, Object> paramMap) {
// 获取headers
Headers headers = getHeaders(new HashMap<>());
// 获取参数
String param = getParam(paramMap);
// 拼接URL
URL = URL + param;

logger.info("本次访问请求地址:[{}],请求方式:[{}],正在发起请求……", URL, OkHttpUtils.METHOD_GET);

Request request = getRequest(URL, headers, OkHttpUtils.METHOD_GET);

// 发起异步调用
newCallAsync(request);
}


/**
* 发起post请求调用
* @param URL 请求地址
* @param paramMap 参数map
* @param headersMap 请求头map
* @return
*/
public static String post(String URL, HashMap<String, Object> paramMap, Map<String, String> headersMap) {
// 获取headers
Headers headers = getHeaders(headersMap);
// 创建请求body
RequestBody body = getRequestBody(paramMap);

logger.info("本次访问请求地址:[{}],请求方式:[{}],正在发起请求……", URL, OkHttpUtils.METHOD_POST);

Request request = getRequest(URL, headers, OkHttpUtils.METHOD_POST,body);
return newCall(request);
}


/**
* 以异步方式发起post请求调用
* @param URL 请求地址
* @param paramMap 参数map
* @param headersMap 请求头map
* @return
*/
public static void postAsync(String URL, HashMap<String, Object> paramMap, Map<String, String> headersMap) {
// 获取headers
Headers headers = getHeaders(headersMap);
// 创建请求body
RequestBody body = getRequestBody(paramMap);

logger.info("本次访问请求地址:[{}],请求方式:[{}],正在发起请求……", URL, OkHttpUtils.METHOD_POST);

Request request = getRequest(URL, headers, OkHttpUtils.METHOD_POST,body);

// 发起异步调用
newCallAsync(request);
}


/**
* 发起post请求调用
* @param URL 请求地址
* @param paramMap 参数map
* @return
*/
public static String post(String URL, HashMap<String, Object> paramMap) {
// 获取headers
Headers headers = getHeaders(new HashMap<>());
// 创建请求body
RequestBody body = getRequestBody(paramMap);

logger.info("本次访问请求地址:[{}],请求方式:[{}],正在发起请求……", URL, OkHttpUtils.METHOD_POST);

Request request = getRequest(URL, headers, OkHttpUtils.METHOD_POST,body);
return newCall(request);
}


/**
* 以异步方式发起post请求调用
* @param URL 请求地址
* @param paramMap 参数map
* @return
*/
public static void postAsync(String URL, HashMap<String, Object> paramMap) {
// 获取headers
Headers headers = getHeaders(new HashMap<>());
// 创建请求body
RequestBody body = getRequestBody(paramMap);

logger.info("本次访问请求地址:[{}],请求方式:[{}],正在发起请求……", URL, OkHttpUtils.METHOD_POST);

Request request = getRequest(URL, headers, OkHttpUtils.METHOD_POST,body);

// 发起异步调用
newCall(request);
}


/**
* 获取requestBody对象
* @param paramMap
* @return
*/
private static RequestBody getRequestBody(HashMap<String, Object> paramMap) {
try {
String value = objectMapperService.writeValueAsString(paramMap);
return RequestBody.create(MediaType.parse("application/json"), value);
} catch (JsonProcessingException e) {
logger.error("json序列化出错!", e);
}
return RequestBody.create(MediaType.parse("application/json"),"{}");
}


/**
* 获取request请求
* @param URL
* @param headers
* @param method
* @param requestBody
* @return
*/
private static Request getRequest(String URL, Headers headers, String method, RequestBody... requestBody) {
if (requestBody.length > 1) {
throw new RuntimeException("requestBody最多只允许一个!");
}
if (OkHttpUtils.METHOD_GET.equalsIgnoreCase(method)) {
return new Request.Builder()
.get()
.url(URL)
.headers(headers)
.build();
} else if (OkHttpUtils.METHOD_POST.equalsIgnoreCase(method)) {
return new Request.Builder()
.post(requestBody[0])
.url(URL)
.headers(headers)
.build();
}
return new Request.Builder().build();
}


/**
* 发起请求
* @param request
* @return
*/
private static String newCall(Request request) {
try(Response execute = okHttpClient.newCall(request).execute()) {
if (execute.isSuccessful()) {
ResponseBody body = execute.body();
String result = body == null ? null : body.string();
logger.info("接口请求访问成功,返回值:[{}]", result);
return result;
}
logger.error("接口请求失败!错误码:[{}]", execute.code());
} catch (IOException e) {
logger.error("接口请求失败!失败原因:", e);
}
return null;
}


/**
* 发起异步请求
* @param request
* @return
*/
private static void newCallAsync(Request request) {
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
logger.error("接口请求失败!", e);
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
if (response.isSuccessful()) {
ResponseBody body = response.body();
String result = body == null ? null : body.string();
logger.info("接口请求访问成功,返回值:[{}]", result);
return;
}
logger.error("接口请求失败!错误码:[{}]", response.code());
}
});
}


/**
* map转为get参数
* @param paramMap
* @return
*/
private static String getParam(Map<String, Object> paramMap) {
if (paramMap.isEmpty()) {
return "";
}
StringBuffer sb = new StringBuffer();
paramMap.forEach((key, value) -> {
sb.append("&").append(key).append("=").append(value);
});
return sb.toString().replaceFirst("&", "?");
}


/**
* 创建并装载请求头
*
* @param headersMap
* @return
*/
private static Headers getHeaders(Map<String, String> headersMap) {
Headers.Builder builder = new Headers.Builder();
headersMap.forEach(builder::add);
return builder.build();
}
}

测试代码

下面是get的测试代码,我们通过远程调用的方式来访问add方法,并将param作为id传过去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@GetMapping("/test/{param}")
public Result httpClient(@PathVariable("param") String param) {
String url = "http://127.0.0.1:9999/sora/auth/http/" + param;
HashMap<String, String> headerMap = new HashMap<>();
headerMap.put("token", "123");
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("id", param);
String result = OkHttpUtils.get(url, paramMap,headerMap);
logger.info("请求返回值:[{}]", result);
return Result.success();
}

@GetMapping("/{id}")
public Result add(@PathVariable("id") String id) {
logger.info("Get-请求参数:[{}]", id);
return Result.success();
}

通过日志打印,我们也可以了解到接口的具体地址以及返回状态,成功进入get方法。

image-20230907152124084

image-20230907152146977

下面我们直接测试post+异步的方式,我在异步请求中让程序休眠五秒,这样可以直接观测到程序的运行状态。

image-20230907152516146

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@GetMapping("/test/{param}")
public Result httpClient(@PathVariable("param") String param) {
String url = "http://127.0.0.1:9999/sora/auth/http";
HashMap<String, String> headerMap = new HashMap<>();
headerMap.put("token", "123");
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("id", param);
OkHttpUtils.postAsync(url, paramMap,headerMap);
return Result.success();
}

@PostMapping
public Result add(@RequestBody HashMap<String, Object> paramMap) {
logger.info("Post-请求参数:[{}]", paramMap);
return Result.success();
}

接口耗时29ms,明显比五秒要短,成功完成了一次异步请求

image-20230907152820818

image-20230907152813591

那么这次就到这里,毕竟是很简单的东西。感兴趣的也可以看看封装的工具类,很快就可以明白其原理。