与OKHttp共舞:网络编程的华丽篇章

2023-09-26 22:52:29 浏览数 (2)

前言

OkHttp是一个基于HTTP的用于访问网络服务的Java库。它旨在通过取消HTTP连接池以及支持连接和请求超时来提高性能。它还支持SSL和TLS,以及HTTP/2和HTTP/3。OkHttp提供了连接管理器、请求对象、缓存以及响应对象等工具,以便开发人员可以轻松地执行HTTP请求和响应。

在传统的单体应用开发中,后端常常作为接收请求的一端。但当我们需要依赖于另一个应用,比如连接工作流的时候,需要后端来发送请求。如果没有做过网络编程,这是个很令人困扰的问题。那么就有了这样一种方便使用的框架:OKHttp。

一. 慢速入门

场景:模拟一个请求发出后,client端接收并向server端发起请求。

1.1 准备两个Spring boot工程

工程结构工程结构

父工程pom文件,省略部分

代码语言:html复制
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <modules>
        <module>server</module>
        <module>client</module>
    </modules>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

client 端的pom引入OKHttp

代码语言:html复制
    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>4.10.0</version>
    </dependency>

1.2 核心代码

client 端,/controller/HelloController

代码语言:java复制
    @GetMapping("/hello")
    @ResponseBody
    public String hello() {
        OkHttpClient client = new OkHttpClient();
        // 请求参数
        Request request = new Request.Builder()
                .url("http://localhost:8080/client")
                .build();

        // 返回结果
        String result = "";
        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                result = response.body().string();
            } else {
                result = STR."请求失败: {response.message()}";
            }
        } catch (IOException e) {
            result = STR."请求失败: {e}";
        }

        return result;
    }

server端,/controller/AnswerClient

代码语言:java复制
    @GetMapping("/client")
    @ResponseBody
    public String hello(String msg) {
        return "我很好";
    }

1.3 测试

使用curl命令发起请求

代码语言:shell复制
curl http://localhost:8082/hello

结果

测试结果测试结果

二. 浅入应用

2. 1 更多参数

核心代码中构建一个request对象为参数,在使用的时候发现,有更多的属性可以加入。

代码语言:java复制
class Request internal constructor(
  @get:JvmName("url") val url: HttpUrl,
  @get:JvmName("method") val method: String,
  @get:JvmName("headers") val headers: Headers,
  @get:JvmName("body") val body: RequestBody?,
  internal val tags: Map<Class<*>, Any>

  • url: 目标服务地址
  • method:请求方法
  • head:请求头
  • body:请求参数

简单post示例

代码语言:java复制
Request request = new Request.Builder()
                .url(url)
                .header("name", "value")
                .post(requestBody)
                .build();

2.2 泛化调用

目标:我们通过“一个”URL访问不同的REST接口

代码语言:java复制
final static String WORK_FLOW_URL = "http://localhost:8080/workflow/{0}/{1}"

看起来只是少些一些代码,其更多意义是针对业务做再封装。

更多重点是另一方的统一实现接口,使得更加灵活。

2.3 序列化参数

有些时候需要的参数很多,层级关系复杂,这时候往往需要序列化成json。

fastjson举例。

代码语言:java复制
    Person person = new Person("张三", 18);
    String jsonString = JSON.toJSONString(person); // {"age":18,"name":"张三"}

2.4 请求重试

这次从OKHttp的构建出发。主要思路是加入自定义拦截器。代码如下:

代码语言:java复制
    HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
    loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

    OkHttpClient.Builder builder = new OkHttpClient.Builder()
            .addInterceptor(loggingInterceptor)
            .addInterceptor(new RetryInterceptor())
            .retryOnConnectionFailure(true);

自定义的拦截器。

代码语言:java复制
public class RetryInterceptor implements Interceptor {
    private static final int MAX_RETRIES = 3; // 设置最大重试次数
    private int retries;

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Response response = chain.proceed(request);

        if (!Objects.requireNonNull(response.body()).contentLength() > 0) {
            return response;
        }

        while (!response.isSuccessful() && retries < MAX_RETRIES) {
            retries  ;
            response = chain.proceed(request);
        }

        if (retries == MAX_RETRIES) {
            throw new IOException("请求失败,已达到最大重试次数");
        }

        return response;
    }
}

测试使用

代码语言:java复制
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (!response.isSuccessful()) {
                    throw new IOException("请求失败,代码:"   response.code());
                }

                // 处理响应
                System.out.println(response.body().string());
            }
        });

总结

这只是使用了一部分,框架还提供了:多种协议,连接池,缓存,同步异步等处理。有点初具RPC的影子。在简单的场景,是很好的RPC的替代。


我正在参与2023腾讯技术创作特训营第二期有奖征文,瓜分万元奖池和键盘手表

0 人点赞