Skip to content

Commit b81d565

Browse files
author
litongjava
committed
add 99 05.md
1 parent a173f75 commit b81d565

File tree

2 files changed

+372
-1
lines changed

2 files changed

+372
-1
lines changed

docs/.vuepress/config/sidebar-zh.json

+7-1
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,12 @@
227227
{
228228
"text": "99_案例",
229229
"collapsable": false,
230-
"children": ["/zh/99_案例/01.md", "/zh/99_案例/02.md", "/zh/99_案例/03.md"]
230+
"children": [
231+
"/zh/99_案例/01.md",
232+
"/zh/99_案例/02.md",
233+
"/zh/99_案例/03.md",
234+
"/zh/99_案例/04.md",
235+
"/zh/99_案例/05.md"
236+
]
231237
}
232238
]

docs/zh/99_案例/05.md

+365
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
# tio-boot handler see openai chatgpt
2+
3+
## Introudction
4+
5+
使用 tio-boot 框架的 handler 组件从 openai chatgpt 获取流程响应并以流式的方法返回给客户端
6+
使用了`tio-boot`框架,集成了与 OpenAI ChatGPT 进行交互的功能,并以服务器发送事件(Server-Sent Events, SSE)方式流式返回数据给客户端。
7+
8+
## 概念扩展
9+
10+
- **Server-Sent Events (SSE)**:一种允许服务器向浏览器客户端发送更新的技术。适用于需要实时数据更新的场景,如股票行情、新闻直播等。
11+
- **OpenAI API**:这是利用人工智能模型生成文本的 API,可用于聊天机器人、内容生成等多种应用。
12+
- **JFinal Aop**:轻量级 AOP(面向切面编程)框架,支持依赖注入和面向切面编程,使 Java 开发更简洁。
13+
14+
## 代码讲解
15+
16+
### Maven 配置 (`pom.xml`)
17+
18+
这个 `pom.xml` 文件定义了项目的构建配置和依赖管理。主要部分包括:
19+
20+
- **项目信息**:定义了项目的基本元数据,如 `groupId`, `artifactId`, 和 `version`
21+
- **属性**:定义了常用的属性,如 Java 版本和项目依赖版本。这样可以在整个项目中重复使用这些属性,便于维护和更新。
22+
- **依赖**:列出了项目所需的库,例如日志框架 `logback`, JSON 处理库 `fastjson2`, HTTP 客户端库 `okhttp3`
23+
- **构建配置**:分为开发和生产两种配置,使用了 Spring Boot 的 Maven 插件来简化打包和运行过程。
24+
25+
### 主类 (`HelloApp`)
26+
27+
该类是程序的入口点:
28+
29+
- 使用 `@AComponentScan` 注解来自动扫描和注册 JFinal AOP 容器中的组件。
30+
-`main` 方法中,记录程序启动和结束时间,使用 `TioApplication.run()` 方法启动应用。
31+
32+
### HTTP 请求处理配置 (`HttpServerRequestHandlerConfig`)
33+
34+
该类配置了 HTTP 请求的处理逻辑:
35+
36+
- `@BeforeStartConfiguration` 表明这个配置是在服务器启动之前进行的。
37+
- 使用 JFinal AOP 的 `Aop.get` 方法从 AOP 容器获取实例,便于依赖注入和管理。
38+
- 创建 `SimpleHttpRoutes` 对象并添加路由,将特定的 HTTP 请求映射到对应的处理器。
39+
40+
### 消息处理器 (`OpenaiV1ChatHandler`)
41+
42+
该类负责处理来自客户端的 HTTP 请求,并与 OpenAI ChatGPT API 交互:
43+
44+
- **SSE Header**:设置 HTTP 响应头为 SSE 格式,允许服务器推送实时数据到客户端。
45+
- **HTTP 请求处理**:接收 HTTP 请求,解析并发送请求到 OpenAI 服务器,然后将响应以 SSE 形式发送给客户端。
46+
- **API 请求构建**:构建发送到 OpenAI 的 HTTP 请求,包括设置请求头和请求体。
47+
- **流处理**:读取 OpenAI 响应,并将每一行数据以 SSE 格式实时发送给客户端。
48+
49+
### 数据模型 (`ChatgptMessage``CompletionsModel`)
50+
51+
- `ChatgptMessage` 定义了与 OpenAI 交互的基本消息结构,包括角色和内容。
52+
- `CompletionsModel` 封装了发送到 OpenAI 的请求数据,包括模型选择、消息列表和是否启用流式传输。
53+
54+
## 代码内容
55+
56+
### pom.xml
57+
58+
```xml
59+
pom.xml
60+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
61+
<modelVersion>4.0.0</modelVersion>
62+
<groupId>com.litongjava</groupId>
63+
<artifactId>tio-boot-sse-chatgpt-demo</artifactId>
64+
<version>1.0.0</version>
65+
<properties>
66+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
67+
<java.version>1.8</java.version>
68+
<maven.compiler.source>${java.version}</maven.compiler.source>
69+
<maven.compiler.target>${java.version}</maven.compiler.target>
70+
<graalvm.version>23.1.1</graalvm.version>
71+
<tio-boot.version>1.4.2</tio-boot.version>
72+
<lombok-version>1.18.30</lombok-version>
73+
<hotswap-classloader.version>1.2.2</hotswap-classloader.version>
74+
<final.name>web-hello</final.name>
75+
<main.class>com.litongjava.tio.web.hello.HelloApp</main.class>
76+
</properties>
77+
<dependencies>
78+
<dependency>
79+
<groupId>ch.qos.logback</groupId>
80+
<artifactId>logback-classic</artifactId>
81+
<version>1.2.3</version>
82+
83+
</dependency>
84+
<dependency>
85+
<groupId>com.litongjava</groupId>
86+
<artifactId>tio-boot</artifactId>
87+
<version>${tio-boot.version}</version>
88+
</dependency>
89+
90+
<dependency>
91+
<groupId>org.projectlombok</groupId>
92+
<artifactId>lombok</artifactId>
93+
<version>${lombok-version}</version>
94+
<optional>true</optional>
95+
<scope>provided</scope>
96+
</dependency>
97+
98+
<dependency>
99+
<groupId>com.litongjava</groupId>
100+
<artifactId>hotswap-classloader</artifactId>
101+
<version>${hotswap-classloader.version}</version>
102+
</dependency>
103+
104+
<dependency>
105+
<groupId>com.squareup.okhttp3</groupId>
106+
<artifactId>okhttp</artifactId>
107+
<version>3.11.0</version>
108+
</dependency>
109+
110+
<dependency>
111+
<groupId>com.alibaba.fastjson2</groupId>
112+
<artifactId>fastjson2</artifactId>
113+
<version>2.0.12</version>
114+
</dependency>
115+
116+
<dependency>
117+
<groupId>junit</groupId>
118+
<artifactId>junit</artifactId>
119+
<version>4.12</version>
120+
<scope>test</scope>
121+
</dependency>
122+
123+
124+
</dependencies>
125+
<profiles>
126+
<!-- development -->
127+
<profile>
128+
<id>development</id>
129+
<activation>
130+
<activeByDefault>true</activeByDefault>
131+
</activation>
132+
<build>
133+
<plugins>
134+
<plugin>
135+
<groupId>org.springframework.boot</groupId>
136+
<artifactId>spring-boot-maven-plugin</artifactId>
137+
<version>2.7.4</version>
138+
<configuration>
139+
<fork>true</fork>
140+
<mainClass>${main.class}</mainClass>
141+
<excludeGroupIds>org.projectlombok</excludeGroupIds>
142+
<arguments>
143+
<argument>--mode=dev</argument>
144+
</arguments>
145+
</configuration>
146+
</plugin>
147+
</plugins>
148+
</build>
149+
</profile>
150+
151+
<!-- production -->
152+
<profile>
153+
<id>production</id>
154+
<build>
155+
<plugins>
156+
<plugin>
157+
<groupId>org.springframework.boot</groupId>
158+
<artifactId>spring-boot-maven-plugin</artifactId>
159+
<version>2.7.4</version>
160+
<configuration>
161+
<mainClass>${main.class}</mainClass>
162+
<excludeGroupIds>org.projectlombok</excludeGroupIds>
163+
</configuration>
164+
<executions>
165+
<execution>
166+
<goals>
167+
<goal>repackage</goal>
168+
</goals>
169+
</execution>
170+
</executions>
171+
</plugin>
172+
</plugins>
173+
</build>
174+
</profile>
175+
</profiles>
176+
</project>
177+
```
178+
179+
### HelloApp
180+
181+
```java
182+
package com.litongjava.tio.chatgpt;
183+
184+
import com.litongjava.jfinal.aop.annotation.AComponentScan;
185+
import com.litongjava.tio.boot.TioApplication;
186+
187+
@AComponentScan
188+
public class HelloApp {
189+
public static void main(String[] args) {
190+
long start = System.currentTimeMillis();
191+
TioApplication.run(HelloApp.class, args);
192+
long end = System.currentTimeMillis();
193+
System.out.println((end - start) + "ms");
194+
}
195+
}
196+
```
197+
198+
### HttpServerRequestHanlderConfig
199+
200+
```java
201+
package com.litongjava.tio.chatgpt.config;
202+
203+
import com.litongjava.jfinal.aop.Aop;
204+
import com.litongjava.jfinal.aop.annotation.AInitialization;
205+
import com.litongjava.jfinal.aop.annotation.BeforeStartConfiguration;
206+
import com.litongjava.tio.boot.server.TioBootServer;
207+
import com.litongjava.tio.chatgpt.handler.OpenaiV1ChatHandler;
208+
import com.litongjava.tio.http.server.router.SimpleHttpRoutes;
209+
210+
@BeforeStartConfiguration
211+
public class HttpServerRequestHanlderConfig {
212+
213+
@AInitialization
214+
public void httpRoutes() {
215+
216+
// 创建simpleHttpRoutes
217+
SimpleHttpRoutes simpleHttpRoutes = new SimpleHttpRoutes();
218+
// 创建controller
219+
OpenaiV1ChatHandler openaiV1ChatCompletionsHandler = Aop.get(OpenaiV1ChatHandler.class);
220+
221+
// 添加action
222+
simpleHttpRoutes.add("/openai/v1/chat/completions", openaiV1ChatCompletionsHandler::completions);
223+
224+
// 将simpleHttpRoutes添加到TioBootServer
225+
TioBootServer.me().setHttpRoutes(simpleHttpRoutes);
226+
}
227+
228+
}
229+
```
230+
231+
### ChatgptMessage
232+
233+
```java
234+
package com.litongjava.tio.chatgpt.handler;
235+
236+
import lombok.AllArgsConstructor;
237+
import lombok.Data;
238+
import lombok.NoArgsConstructor;
239+
240+
@Data
241+
@NoArgsConstructor
242+
@AllArgsConstructor
243+
public class ChatgptMessage {
244+
// system,assistant,user
245+
private String role, content;
246+
}
247+
```
248+
249+
### CompletionsModel
250+
251+
```java
252+
package com.litongjava.tio.chatgpt.handler;
253+
254+
import java.util.List;
255+
256+
import lombok.AllArgsConstructor;
257+
import lombok.Data;
258+
import lombok.NoArgsConstructor;
259+
260+
@NoArgsConstructor
261+
@AllArgsConstructor
262+
@Data
263+
public class CompletionsModel {
264+
private String model;
265+
private List<ChatgptMessage> messages;
266+
private boolean stream;
267+
}
268+
```
269+
270+
### OpenaiV1ChatHandler
271+
272+
```java
273+
package com.litongjava.tio.chatgpt.handler;
274+
275+
import java.io.BufferedReader;
276+
import java.io.IOException;
277+
import java.io.InputStreamReader;
278+
import java.util.ArrayList;
279+
import java.util.List;
280+
281+
import com.litongjava.jfinal.aop.Aop;
282+
import com.litongjava.tio.core.ChannelContext;
283+
import com.litongjava.tio.core.Tio;
284+
import com.litongjava.tio.http.common.HttpRequest;
285+
import com.litongjava.tio.http.common.HttpResponse;
286+
import com.litongjava.tio.http.server.sse.SseBytesPacket;
287+
import com.litongjava.tio.utils.environment.EnvironmentUtils;
288+
import com.litongjava.tio.utils.json.Json;
289+
290+
import okhttp3.MediaType;
291+
import okhttp3.OkHttpClient;
292+
import okhttp3.Request;
293+
import okhttp3.RequestBody;
294+
import okhttp3.Response;
295+
import okhttp3.ResponseBody;
296+
297+
public class OpenaiV1ChatHandler {
298+
299+
public HttpResponse completions(HttpRequest httpRequest) throws IOException {
300+
ChannelContext channelContext = httpRequest.getChannelContext();
301+
302+
// 设置sse请求头
303+
HttpResponse httpResponse = new HttpResponse(httpRequest).setServerSentEventsHeader();
304+
305+
// 发送http响应包,告诉客户端保持连接
306+
Tio.send(channelContext, httpResponse);
307+
308+
// 处理数据
309+
processData(channelContext);
310+
311+
// 告诉处理器不要将消息发送给客户端
312+
return new HttpResponse().setSend(false);
313+
}
314+
315+
private void processData(ChannelContext channelContext) throws IOException {
316+
317+
String apiKey = EnvironmentUtils.get("OPENAI_API_KEY");
318+
319+
OkHttpClient client = Aop.get(OkHttpClient.class);
320+
321+
List<ChatgptMessage> messages = new ArrayList<>();
322+
messages.add(new ChatgptMessage("system", "你是一名经验丰富的软件开发工程师"));
323+
messages.add(new ChatgptMessage("user", "简述浏览器发送请求到收到响应的流程"));
324+
325+
CompletionsModel completionsModel = new CompletionsModel();
326+
// completionsModel.setModel("gpt-3.5-turbo");
327+
completionsModel.setModel("gpt-4-turbo");
328+
completionsModel.setMessages(messages);
329+
completionsModel.setStream(true);
330+
331+
String url = "https://api.openai.com/v1/chat/completions";
332+
MediaType mediaType = MediaType.parse("application/json");
333+
String content = Json.getJson().toJson(completionsModel);
334+
335+
RequestBody body = RequestBody.create(mediaType, content);
336+
337+
Request request = new Request.Builder() //
338+
.url(url) //
339+
.method("POST", body) //
340+
.addHeader("Content-Type", "application/json") //
341+
.addHeader("Authorization", "Bearer " + apiKey) //
342+
.build();
343+
Response response = client.newCall(request).execute();
344+
345+
ResponseBody responseBody = response.body();
346+
if (responseBody != null) {
347+
// 转成BufferedReader是将字节转为字符
348+
try (BufferedReader reader = new BufferedReader(new InputStreamReader(responseBody.byteStream()))) {
349+
String line;
350+
while ((line = reader.readLine()) != null) {
351+
System.out.println(line);
352+
// byte[] bytes = (line + "\n\n").getBytes();
353+
// 必须添加一个回车符号
354+
byte[] bytes = (line + "\n").getBytes();
355+
SseBytesPacket ssePacket = new SseBytesPacket(bytes);
356+
// 再次向客户端发送消息
357+
Tio.send(channelContext, ssePacket);
358+
}
359+
}
360+
//
361+
}
362+
Tio.remove(channelContext, "remove sse");
363+
}
364+
}
365+
```

0 commit comments

Comments
 (0)