{"id":26367504,"url":"https://github.com/litongjava/aio-socket","last_synced_at":"2025-03-16T21:17:27.106Z","repository":{"id":279583105,"uuid":"939287556","full_name":"litongjava/aio-socket","owner":"litongjava","description":"aio-socket","archived":false,"fork":false,"pushed_at":"2025-02-26T09:58:35.000Z","size":0,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-26T10:39:01.747Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/litongjava.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-02-26T09:54:36.000Z","updated_at":"2025-02-26T09:58:39.000Z","dependencies_parsed_at":"2025-02-26T10:49:05.965Z","dependency_job_id":null,"html_url":"https://github.com/litongjava/aio-socket","commit_stats":null,"previous_names":["litongjava/aio-socket"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/litongjava%2Faio-socket","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/litongjava%2Faio-socket/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/litongjava%2Faio-socket/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/litongjava%2Faio-socket/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/litongjava","download_url":"https://codeload.github.com/litongjava/aio-socket/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243933454,"owners_count":20370988,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-16T21:17:26.611Z","updated_at":"2025-03-16T21:17:27.099Z","avatar_url":"https://github.com/litongjava.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# aio-socket\n## 出处\n本项目代码出自[smart-socket](https://github.com/smartboot/smart-socket/tree/master/aio-core/src/main/java/org/smartboot/socket)\n## aio-socket 简介\n\n**aio-socket** 是一款基于 Java 的异步、非阻塞高性能 IO 库，其设计理念类似于 JDK7 提供的 AIO，但在性能和资源利用上做了优化。该库采用高效的内存管理策略和自定义的线程模型，能够在 1C1G 的硬件条件下支撑上万并发连接。主要特点包括：\n\n- **异步非阻塞**：通过注册事件（如读、写、连接、接收连接）与回调处理，实现 IO 事件的异步处理，避免线程阻塞。\n- **高性能内存管理**：使用内存页池（BufferPagePool）管理物理内存，通过虚拟内存（VirtualBuffer）的方式分割和复用 ByteBuffer，从而减少内存分配和复制的开销。\n- **灵活的线程模型**：内部通过多个 Worker 线程（包括读、写、通用处理线程）以及调度任务实现高并发的 IO 处理。\n- **接口与实现分离**：通过定义接口（如 BufferPage）和抽象类（如 AbstractBufferPage），实现了弹性内存页（ElasticBufferPage）和静态内存页（StaticBufferPage）的不同策略，满足不同场景下的需求。\n\n---\n\n## 代码模块介绍\n\n下面分别介绍各个主要模块的设计与实现，并补充关键方法和参数的文档说明。\n\n### 1. 内存管理模块\n\n#### AbstractBufferPage\n\n- **作用**：作为内存页的抽象基类，定义了基本的内存清理和释放接口，同时维护一个表示内存页是否空闲的标识。\n- **关键方法**：\n  - `clean(VirtualBuffer cleanBuffer)`  \n    清理（回收）指定的虚拟内存块。子类需要根据内存类型（直接内存或堆内存）实现具体的清理操作。\n  - `tryClean()`  \n    尝试触发缓冲区的回收任务。当内存页长时间处于空闲状态时，会自动回收未使用的虚拟内存。\n  - `release()`  \n    释放内存页所占的物理内存，通常在内存页不再使用时调用。\n\n#### BufferPage\n\n- **作用**：定义了内存页对外提供的接口，主要用于申请虚拟内存。\n- **关键方法**：\n  - `allocate(int size)`  \n    申请指定大小的虚拟内存块。返回的 `VirtualBuffer` 是对真实 ByteBuffer 的一个切片映射，用于业务数据的读写。\n\n#### BufferPagePool\n\n- **作用**：管理多个内存页，采用轮训方式均衡分配内存页；同时内部调度定时任务，定期调用各内存页的 `tryClean()` 方法以实现内存回收。\n- **构造方法参数**：\n  - `pageSize`：内存页的大小。如果为 0，则使用弹性内存页（ElasticBufferPage）。\n  - `pageNum`：内存页的个数。\n  - `isDirect`：是否使用直接缓冲区（off-heap）。\n- **关键方法**：\n  - `allocateBufferPage()`  \n    从内存页池中按轮询策略返回一个可用的内存页。\n  - `release()`  \n    关闭内存池，停止调度回收任务，并释放所有内存页占用的资源。\n\n#### ElasticBufferPage\n\n- **作用**：基于弹性分配策略实现的内存页。当申请的虚拟内存大小不匹配时，会直接回收未匹配的缓冲区并创建新的虚拟内存块。\n- **内存回收策略**：\n  - 利用一个并发队列 `cleanBuffers` 保存待回收的虚拟内存，当 `tryClean()` 被触发且内存页连续处于空闲状态时，会循环回收一定数量的虚拟内存。\n  - 当使用直接内存时，通过调用 `((DirectBuffer) buffer).cleaner().clean()` 释放物理内存。\n\n#### StaticBufferPage\n\n- **作用**：基于静态内存页实现，先一次性申请一块固定大小的物理 ByteBuffer，然后通过切分（slice）来生成多个虚拟内存块（VirtualBuffer）。\n- **内存分配策略**：\n  - 内部维护一个 `availableBuffers` 列表，记录当前可分配的内存块。当申请内存时，采用“快速匹配”和“迭代申请”两种策略查找合适的内存块。\n- **内存合并**：\n  - 当虚拟内存被释放时（调用 `clean(VirtualBuffer)`），通过 `clean0()` 方法尝试与相邻的空闲块合并，以便后续大块内存的分配。\n  \n#### VirtualBuffer\n\n- **作用**：表示由物理 ByteBuffer 切分出的虚拟缓冲区，封装了内存页中某一段数据的读写边界（parentPosition 与 parentLimit）以及实际的 ByteBuffer 对象。\n- **关键字段与方法**：\n  - `getCapacity()`  \n    返回该虚拟缓冲区的容量，即 parentLimit - parentPosition。\n  - `buffer()` 与 `buffer(ByteBuffer)`  \n    分别用于获取和设置真实的 ByteBuffer 引用。\n  - `clean()`  \n    释放该虚拟缓冲区，内部调用所属内存页的 `clean()` 方法实现回收。\n\n---\n\n### 2. 异步 IO 模块\n\n#### EnhanceAsynchronousChannelGroup\n\n- **作用**：扩展了 JDK 的 `AsynchronousChannelGroup`，实现了异步 IO 事件的调度和分发。内部创建多个 Worker 线程，分别处理读、写、连接和接收等事件。\n- **关键成员**：\n  - `readExecutorService` 与 `commonExecutorService`：分别用于读和通用（写、连接、accept）任务的线程池。\n  - `readWorkers`、`writeWorker`、`commonWorker`：基于 `Worker` 内部类实现，均绑定各自的 Selector 以监听对应的 IO 事件。\n- **关键方法**：\n  - `shutdown()` 与 `shutdownNow()`  \n    停止异步通道组的所有任务，并关闭所有 Selector 及线程资源。\n  - `interestOps()` 与 `removeOps()`  \n    辅助方法用于动态修改 SelectionKey 的兴趣操作，确保线程在不同 Worker 中协同工作。\n\n#### EnhanceAsynchronousChannelProvider\n\n- **作用**：继承自 `AsynchronousChannelProvider`，提供 aio-socket 实现的异步通道。通过自定义的通道组（EnhanceAsynchronousChannelGroup）创建服务端和客户端的异步通道。\n- **关键方法**：\n  - `openAsynchronousChannelGroup(...)`  \n    根据传入的线程数或 ExecutorService 创建异步通道组。\n  - `openAsynchronousServerSocketChannel(...)` 与 `openAsynchronousSocketChannel(...)`  \n    分别创建服务端和客户端的异步通道实例。\n\n#### EnhanceAsynchronousClientChannel\n\n- **作用**：实现异步客户端通道，支持连接、读、写操作。通过调用底层 `SocketChannel` 的非阻塞操作实现异步模式。\n- **连接操作**：\n  - `connect(SocketAddress, A, CompletionHandler\u003cVoid, ? super A\u003e)`  \n    异步连接远程地址，内部先尝试直接连接，如果无法立即完成，则通过 Worker 注册 OP_CONNECT 事件，等待完成后回调。\n  - 同时支持 Future 模式的 `connect(SocketAddress)`。\n\n#### EnhanceAsynchronousServerChannel\n\n- **作用**：实现异步服务端通道，对应于每个已建立连接的 SocketChannel，支持非阻塞的读写操作。\n- **读写操作**：\n  - `read(ByteBuffer, ..., CompletionHandler\u003cInteger, ? super A\u003e)`  \n    异步读取数据。内部判断是否需要直接调用 `channel.read()` 或注册 OP_READ 事件，必要时通过 FutureCompletionHandler 处理取消和超时情况。\n  - `write(ByteBuffer, ..., CompletionHandler\u003cInteger, ? super A\u003e)`  \n    异步写入数据。写操作中采用轮询写入，如果一次写入未完成，则注册 OP_WRITE 事件等待下一次写操作。\n\n#### EnhanceAsynchronousServerSocketChannel\n\n- **作用**：实现异步服务端 Socket 通道，用于接收客户端连接请求。提供基于回调或 Future 模式的 accept 操作。\n- **接收连接**：\n  - `accept(A, CompletionHandler\u003cAsynchronousSocketChannel, ? super A\u003e)`  \n    异步接收客户端连接。当有新的连接到达时，会创建一个新的 EnhanceAsynchronousServerChannel 实例，并调用回调通知应用层。\n  - 内部通过 Selector 监听 OP_ACCEPT 事件，实现非阻塞的接收连接操作。\n\n#### FutureCompletionHandler\n\n- **作用**：实现了 `CompletionHandler` 和 `Future` 接口的组合，用于同时支持回调模式和 Future 模式。  \n- **关键特点**：\n  - 在操作完成后通过 `completed()` 或 `failed()` 设置结果或异常，并唤醒等待线程。\n  - 通过 `get()` 方法可以等待异步操作完成，并获取操作结果。\n\n---\n\n## 使用示例\n\n假设你需要构建一个高并发的服务端，可以这样使用 aio-socket：\n\n1. **创建异步通道组**  \n   使用 `EnhanceAsynchronousChannelProvider` 创建一个异步通道组：\n   ```java\n   AsynchronousChannelGroup group = new EnhanceAsynchronousChannelProvider(false)\n       .openAsynchronousChannelGroup(4, Executors.defaultThreadFactory());\n   ```\n\n2. **启动服务端 Socket**  \n   创建并绑定服务端 Socket 通道：\n   ```java\n   AsynchronousServerSocketChannel serverChannel = new EnhanceAsynchronousChannelProvider(false)\n       .openAsynchronousServerSocketChannel(group)\n       .bind(new InetSocketAddress(8080));\n   ```\n\n3. **接收连接**  \n   调用 `accept()` 方法，注册回调：\n   ```java\n   serverChannel.accept(null, new CompletionHandler\u003cAsynchronousSocketChannel, Object\u003e() {\n       @Override\n       public void completed(AsynchronousSocketChannel result, Object attachment) {\n           // 处理新连接\n           System.out.println(\"New connection accepted: \" + result);\n           // 继续接受后续连接\n           serverChannel.accept(null, this);\n       }\n       @Override\n       public void failed(Throwable exc, Object attachment) {\n           exc.printStackTrace();\n       }\n   });\n   ```\n3. **ElasticBufferPage**  \nElasticBufferPage 由 BufferPagePool 在 pageSize 为 0 时自动创建，旨在支持动态大小的内存分配，而不是预先固定大小的内存页。如果你希望使用 ElasticBufferPage，可以通过如下方式：\n\n1). **创建内存池**  \n   当你构造 BufferPagePool 时，将 pageSize 参数设置为 0，这样内部会实例化 ElasticBufferPage。例如：\n   ```java\n   // 使用 pageSize 为 0 创建内存池，pageNum 表示内存页个数，true 表示使用直接内存\n   BufferPagePool pool = new BufferPagePool(0, 10, true);\n   ```\n\n2). **申请内存页与虚拟内存**  \n   通过内存池申请一个内存页，然后调用其 allocate 方法申请所需大小的虚拟内存（VirtualBuffer）：\n   ```java\n   BufferPage bufferPage = pool.allocateBufferPage();\n   // 申请 1024 字节的虚拟内存\n   VirtualBuffer virtualBuffer = bufferPage.allocate(8192);\n   ```\n\n3). **内存回收**  \n   当你不再使用该虚拟内存时，应调用 VirtualBuffer.clean() 方法，将其归还给 ElasticBufferPage。内部会将该虚拟内存放入回收队列，等待下一次 tryClean() 进行回收：\n   ```java\n   virtualBuffer.clean();\n   ```\n\n这样，ElasticBufferPage 就会自动管理虚拟内存的分配与回收，帮助你减少频繁内存分配带来的性能开销。通常，业务代码无需直接操作 ElasticBufferPage，而是通过 BufferPagePool 获取 BufferPage，然后使用其中的 allocate/clean 接口来进行内存管理。\n---\n\n\n## 使用示例\n\n### 添加依赖\n```java\n\u003cproperties\u003e\n    \u003cproject.build.sourceEncoding\u003eUTF-8\u003c/project.build.sourceEncoding\u003e\n    \u003cjava.version\u003e1.8\u003c/java.version\u003e\n    \u003cmaven.compiler.source\u003e${java.version}\u003c/maven.compiler.source\u003e\n    \u003cmaven.compiler.target\u003e${java.version}\u003c/maven.compiler.target\u003e\n    \u003cmain.class\u003eHttpServer\u003c/main.class\u003e\n  \u003c/properties\u003e\n  \u003cdependencies\u003e\n    \u003cdependency\u003e\n      \u003cgroupId\u003ecom.litongjava\u003c/groupId\u003e\n      \u003cartifactId\u003eaio-socket\u003c/artifactId\u003e\n      \u003cversion\u003e1.0.1\u003c/version\u003e\n    \u003c/dependency\u003e\n  \u003c/dependencies\u003e\n  \u003cprofiles\u003e\n    \u003c!-- 开发环境配置 --\u003e\n    \u003cprofile\u003e\n      \u003cid\u003edevelopment\u003c/id\u003e\n      \u003cactivation\u003e\n        \u003cactiveByDefault\u003etrue\u003c/activeByDefault\u003e\n      \u003c/activation\u003e\n      \u003cbuild\u003e\n        \u003cplugins\u003e\n          \u003c!-- Spring Boot Maven 插件 --\u003e\n          \u003cplugin\u003e\n            \u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n            \u003cartifactId\u003espring-boot-maven-plugin\u003c/artifactId\u003e\n            \u003cversion\u003e2.7.4\u003c/version\u003e\n            \u003cconfiguration\u003e\n              \u003cfork\u003etrue\u003c/fork\u003e\n              \u003cmainClass\u003e${main.class}\u003c/mainClass\u003e\n              \u003cexcludeGroupIds\u003eorg.projectlombok\u003c/excludeGroupIds\u003e\n              \u003carguments\u003e\n                \u003cargument\u003e--mode=dev\u003c/argument\u003e\n              \u003c/arguments\u003e\n            \u003c/configuration\u003e\n          \u003c/plugin\u003e\n        \u003c/plugins\u003e\n      \u003c/build\u003e\n    \u003c/profile\u003e\n\n    \u003c!-- 生产环境配置 --\u003e\n    \u003cprofile\u003e\n      \u003cid\u003eproduction\u003c/id\u003e\n      \u003cbuild\u003e\n        \u003cplugins\u003e\n          \u003c!-- Spring Boot Maven 插件 --\u003e\n          \u003cplugin\u003e\n            \u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n            \u003cartifactId\u003espring-boot-maven-plugin\u003c/artifactId\u003e\n            \u003cversion\u003e2.7.4\u003c/version\u003e\n            \u003cconfiguration\u003e\n              \u003cmainClass\u003e${main.class}\u003c/mainClass\u003e\n              \u003cexcludeGroupIds\u003eorg.projectlombok\u003c/excludeGroupIds\u003e\n            \u003c/configuration\u003e\n            \u003cexecutions\u003e\n              \u003cexecution\u003e\n                \u003cgoals\u003e\n                  \u003cgoal\u003erepackage\u003c/goal\u003e\n                \u003c/goals\u003e\n              \u003c/execution\u003e\n            \u003c/executions\u003e\n          \u003c/plugin\u003e\n        \u003c/plugins\u003e\n      \u003c/build\u003e\n    \u003c/profile\u003e\n  \u003c/profiles\u003e\n```\n### 编写代码\n```java\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.AsynchronousChannelGroup;\nimport java.nio.channels.AsynchronousSocketChannel;\nimport java.nio.channels.CompletionHandler;\nimport java.util.concurrent.ThreadFactory;\n\nimport com.litongjava.enhance.buffer.BufferPage;\nimport com.litongjava.enhance.buffer.BufferPagePool;\nimport com.litongjava.enhance.buffer.VirtualBuffer;\nimport com.litongjava.enhance.channel.EnhanceAsynchronousChannelProvider;\nimport com.litongjava.enhance.channel.EnhanceAsynchronousServerSocketChannel;\n\npublic class HttpServer {\n\n  private static int cpuNum = Runtime.getRuntime().availableProcessors();\n  private static BufferPagePool pool = new BufferPagePool(0, 1024 * cpuNum, true);\n  private static BufferPage bufferPage = pool.allocateBufferPage();\n\n  public static void main(String[] args) throws Exception {\n\n    // 创建通道提供者，false 表示非低内存模式\n    EnhanceAsynchronousChannelProvider provider = new EnhanceAsynchronousChannelProvider(false);\n\n    // 创建一个异步通道组，线程数设为2（根据需求调整）\n    AsynchronousChannelGroup group = provider.openAsynchronousChannelGroup(2, new ThreadFactory() {\n      @Override\n      public Thread newThread(Runnable r) {\n        return new Thread(r, \"http-server-thread\");\n      }\n    });\n\n    // 使用提供者创建服务器通道\n    EnhanceAsynchronousServerSocketChannel server = (EnhanceAsynchronousServerSocketChannel) provider.openAsynchronousServerSocketChannel(group);\n    //\n    // 绑定端口，例如 8080，设置 backlog 为 100\n    server.bind(new InetSocketAddress(8080), 100);\n\n    System.out.println(\"HTTP Server 正在监听端口 8080 ...\");\n\n    // 异步接受连接请求\n    server.accept(null, new CompletionHandler\u003cAsynchronousSocketChannel, Object\u003e() {\n      @Override\n      public void completed(AsynchronousSocketChannel channel, Object attachment) {\n        // 接收到连接后，立即继续接受下一个连接\n        server.accept(null, this);\n        // 处理客户端连接\n        handleClient(channel);\n      }\n\n      @Override\n      public void failed(Throwable exc, Object attachment) {\n        exc.printStackTrace();\n      }\n    });\n\n    // 主线程阻塞，以保证服务器运行\n    Thread.currentThread().join();\n  }\n\n  private static void handleClient(AsynchronousSocketChannel channel) {\n    // 通过 BufferPage 池化获取一个 VirtualBuffer，分配 8192 字节空间\n    VirtualBuffer virtualBuffer = bufferPage.allocate(8192);\n    ByteBuffer buffer = virtualBuffer.buffer();\n\n    // 异步读取客户端请求，将 VirtualBuffer 作为附件传入\n    channel.read(buffer, virtualBuffer, new CompletionHandler\u003cInteger, VirtualBuffer\u003e() {\n      @Override\n      public void completed(Integer result, VirtualBuffer attachment) {\n        try {\n          if (result \u003e 0) {\n            buffer.flip();\n            byte[] bytes = new byte[buffer.remaining()];\n            buffer.get(bytes);\n            // 此处可以处理客户端请求数据 bytes\n\n            // 构造简单的 HTTP 响应\n            String httpResponse = \"HTTP/1.1 200 OK\\r\\n\" + \"Content-Length: 13\\r\\n\" + \"Content-Type: text/plain\\r\\n\" + \"\\r\\n\" + \"Hello, World!\";\n            ByteBuffer responseBuffer = ByteBuffer.wrap(httpResponse.getBytes());\n\n            // 异步写响应\n            channel.write(responseBuffer, attachment, new CompletionHandler\u003cInteger, VirtualBuffer\u003e() {\n              @Override\n              public void completed(Integer result, VirtualBuffer attachment) {\n                try {\n                  channel.close();\n                } catch (IOException e) {\n                  e.printStackTrace();\n                } finally {\n                  // 写完响应后归还虚拟缓冲区\n                  attachment.clean();\n                }\n              }\n\n              @Override\n              public void failed(Throwable exc, VirtualBuffer attachment) {\n                exc.printStackTrace();\n                try {\n                  channel.close();\n                } catch (IOException e) {\n                  e.printStackTrace();\n                } finally {\n                  // 出现写异常时也归还虚拟缓冲区\n                  attachment.clean();\n                }\n              }\n            });\n          } else {\n            // 未读到数据，则关闭连接\n            try {\n              channel.close();\n            } catch (IOException e) {\n              e.printStackTrace();\n            }\n          }\n        } finally {\n          // 不论读取成功与否，归还虚拟缓冲区\n          attachment.clean();\n        }\n      }\n\n      @Override\n      public void failed(Throwable exc, VirtualBuffer attachment) {\n        exc.printStackTrace();\n        try {\n          channel.close();\n        } catch (IOException e) {\n          e.printStackTrace();\n        } finally {\n          // 出现读异常时归还虚拟缓冲区\n          attachment.clean();\n        }\n      }\n    });\n  }\n}\n\n```\n## 并发测试\n```\n[root@ip-172-31-4-97 ~]# ab -c1000 -n1000000 http://localhost:8080/\n```\n测试报告\n```\nThis is ApacheBench, Version 2.3 \u003c$Revision: 1913912 $\u003e\nCopyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/\nLicensed to The Apache Software Foundation, http://www.apache.org/\n\nBenchmarking localhost (be patient)\nCompleted 100000 requests\nCompleted 200000 requests\nCompleted 300000 requests\nCompleted 400000 requests\nCompleted 500000 requests\nCompleted 600000 requests\nCompleted 700000 requests\nCompleted 800000 requests\nCompleted 900000 requests\nCompleted 1000000 requests\nFinished 1000000 requests\n\n\nServer Software:        \nServer Hostname:        localhost\nServer Port:            8080\n\nDocument Path:          /\nDocument Length:        13 bytes\n\nConcurrency Level:      1000\nTime taken for tests:   96.688 seconds\nComplete requests:      1000000\nFailed requests:        0\nTotal transferred:      78000000 bytes\nHTML transferred:       13000000 bytes\nRequests per second:    10342.50 [#/sec] (mean)\nTime per request:       96.688 [ms] (mean)\nTime per request:       0.097 [ms] (mean, across all concurrent requests)\nTransfer rate:          787.81 [Kbytes/sec] received\n\nConnection Times (ms)\n              min  mean[+/-sd] median   max\nConnect:        1   50  80.8     44    1143\nProcessing:     2   46   9.3     46     450\nWaiting:        0   31  10.9     31     446\nTotal:          9   97  82.3     94    1528\n\nPercentage of the requests served within a certain time (ms)\n  50%     94\n  66%     96\n  75%     97\n  80%     97\n  90%     99\n  95%    100\n  98%    108\n  99%    124\n 100%   1528 (longest request)\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flitongjava%2Faio-socket","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flitongjava%2Faio-socket","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flitongjava%2Faio-socket/lists"}