Skip to content

Commit

Permalink
feat(sse): sse samples
Browse files Browse the repository at this point in the history
  • Loading branch information
ekoz committed Dec 18, 2024
1 parent 45e57a4 commit 3a9c47e
Show file tree
Hide file tree
Showing 8 changed files with 403 additions and 18 deletions.
30 changes: 24 additions & 6 deletions kbase-stack-sse/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,36 @@

<artifactId>kbase-stack-sse</artifactId>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>

<dependency>
<groupId>com.ibothub.heap</groupId>
<artifactId>kbase-stack-base</artifactId>
<version>${revision}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>

<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,83 @@
*/
package com.ibothub.heap.sse.controller;

import java.time.Duration;
import java.time.LocalDateTime;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.google.common.collect.Lists;
import com.ibothub.heap.base.model.vo.ResponseEntity;
import com.ibothub.heap.sse.model.vo.req.MessageReq;
import com.ibothub.heap.sse.model.vo.resp.MessageResp;
import jakarta.annotation.PostConstruct;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.io.FileUtils;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Sinks;

/**
* @author <a href="mailto:[email protected]">eko.zhan</a>
* @version v1.0
* @since 2024/12/11 14:08
* @since 2024/12/17 14:52
*/
@RestController
@RequestMapping("/api/v1/sse")
public class HelloController {

@GetMapping("/events")
public Flux<ServerSentEvent<String>> getEvents() {
return Flux.interval(Duration.ofSeconds(1))
.map(sequence -> ServerSentEvent.<String>builder()
.id(String.valueOf(sequence))
.event("message")
.data("Event #" + sequence + " at " + LocalDateTime.now())
List<String> famousList = Lists.newArrayList();

private final ConcurrentHashMap<String, Sinks.Many<MessageResp>> sinks = new ConcurrentHashMap<>();

@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<MessageResp>> stream(@RequestParam("token") String token) {
Sinks.Many<MessageResp> sink = Sinks.many().multicast().directBestEffort();
sinks.put(token, sink);

return sink.asFlux().map(data ->
ServerSentEvent.<MessageResp>builder()
.data(data)
.build());
}

@PostMapping("/send")
public ResponseEntity<String> send(@RequestBody MessageReq req, @RequestHeader("Authorization") String token) {
Sinks.Many<MessageResp> sink = sinks.get(token);
if (sink != null) {
MessageResp messageResp = new MessageResp();
messageResp.setMessage(req.getMessage() + "," + getFamous());
sink.tryEmitNext(messageResp);
return ResponseEntity.ok("");
} else {
return ResponseEntity.failure("");
}
}

@PostConstruct
public void init() throws IOException {
String txt = FileUtils.readFileToString(ResourceUtils.getFile("classpath:data.json"),
StandardCharsets.UTF_8);
JSONObject jsonObject = JSON.parseObject(txt);
JSONArray famousArr = jsonObject.getJSONArray("famous");
famousArr.forEach(famous -> famousList.add(famous.toString()));
}

private String getFamous(){
Random random = new Random();
int num = random.nextInt(famousList.size());
return famousList.get(num);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* powered by https://ekozhan.com
*/
package com.ibothub.heap.sse.model.vo.req;

import java.io.Serializable;
import lombok.Data;

/**
* @author <a href="mailto:[email protected]">eko.zhan</a>
* @version v1.0
* @since 2024/12/18 15:12
*/
@Data
public class MessageReq implements Serializable {

private String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* powered by https://ekozhan.com
*/
package com.ibothub.heap.sse.model.vo.resp;

import java.io.Serializable;
import lombok.Data;

/**
* @author <a href="mailto:[email protected]">eko.zhan</a>
* @version v1.0
* @since 2024/12/18 15:12
*/
@Data
public class MessageResp implements Serializable {

private String message;
}
111 changes: 111 additions & 0 deletions kbase-stack-sse/src/main/resources/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
{
"famous":[
"爱迪生说过,天才是百分之一的勤奋加百分之九十九的汗水。",
"查尔斯·史说过,一个人几乎可以在任何他怀有无限热忱的事情上成功。",
"培根说过,深窥自己的心,而后发觉一切的奇迹在你自己。",
"歌德曾经说过,流水在碰到底处时才会释放活力。",
"莎士比亚说过,那脑袋里的智慧,就像打火石里的火花一样,不去打它是不肯出来的。",
"戴尔·卡耐基说过,多数人都拥有自己不了解的能力和机会,都有可能做到未曾梦想的事情。",
"白哲特说过,坚强的信念能赢得强者的心,并使他们变得更坚强。",
"伏尔泰说过,不经巨大的困难,不会有伟大的事业。",
"富勒曾经说过,苦难磨炼一些人,也毁灭另一些人。",
"文森特·皮尔说过,改变你的想法,你就改变了自己的世界。",
"拿破仑·希尔说过,不要等待,时机永远不会恰到好处。",
"塞涅卡说过,生命如同寓言,其价值不在与长短,而在与内容。",
"奥普拉·温弗瑞说过,你相信什么,你就成为什么样的人。",
"吕凯特说过,生命不可能有两次,但许多人连一次也不善于度过。",
"莎士比亚说过,人的一生是短的,但如果卑劣地过这一生,就太长了。",
"笛卡儿说过,我的努力求学没有得到别的好处,只不过是愈来愈发觉自己的无知。",
"左拉说过,生活的道路一旦选定,就要勇敢地走到底,决不回头。",
"米歇潘说过,生命是一条艰险的峡谷,只有勇敢的人才能通过。",
"吉姆·罗恩说过,要么你主宰生活,要么你被生活主宰。",
"日本谚语说过,不幸可能成为通向幸福的桥梁。",
"海贝尔说过,人生就是学校。在那里,与其说好的教师是幸福,不如说好的教师是不幸。",
"杰纳勒尔·乔治·S·巴顿说过,接受挑战,就可以享受胜利的喜悦。",
"德谟克利特说过,节制使快乐增加并使享受加强。",
"裴斯泰洛齐说过,今天应做的事没有做,明天再早也是耽误了。",
"歌德说过,决定一个人的一生,以及整个命运的,只是一瞬之间。",
"卡耐基说过,一个不注意小事情的人,永远不会成就大事业。",
"卢梭说过,浪费时间是一桩大罪过。",
"康德说过,既然我已经踏上这条道路,那么,任何东西都不应妨碍我沿着这条路走下去。",
"克劳斯·莫瑟爵士说过,教育需要花费钱,而无知也是一样。",
"伏尔泰说过,坚持意志伟大的事业需要始终不渝的精神。",
"亚伯拉罕·林肯说过,你活了多少岁不算什么,重要的是你是如何度过这些岁月的。",
"韩非说过,内外相应,言行相称。",
"富兰克林说过,你热爱生命吗?那么别浪费时间,因为时间是组成生命的材料。",
"马尔顿说过,坚强的信心,能使平凡的人做出惊人的事业。",
"笛卡儿说过,读一切好书,就是和许多高尚的人谈话。",
"塞涅卡说过,真正的人生,只有在经过艰难卓绝的斗争之后才能实现。",
"易卜生说过,伟大的事业,需要决心,能力,组织和责任感。",
"歌德说过,没有人事先了解自己到底有多大的力量,直到他试过以后才知道。",
"达尔文说过,敢于浪费哪怕一个钟头时间的人,说明他还不懂得珍惜生命的全部价值。",
"佚名说过,感激每一个新的挑战,因为它会锻造你的意志和品格。",
"奥斯特洛夫斯基说过,共同的事业,共同的斗争,可以使人们产生忍受一切的力量。 b",
"苏轼说过,古之立大事者,不惟有超世之才,亦必有坚忍不拔之志。",
"王阳明说过,故立志者,为学之心也;为学者,立志之事也。",
"歌德说过,读一本好书,就如同和一个高尚的人在交谈。",
"乌申斯基说过,学习是劳动,是充满思想的劳动。",
"别林斯基说过,好的书籍是最贵重的珍宝。",
"富兰克林说过,读书是易事,思索是难事,但两者缺一,便全无用处。",
"鲁巴金说过,读书是在别人思想的帮助下,建立起自己的思想。",
"培根说过,合理安排时间,就等于节约时间。",
"屠格涅夫说过,你想成为幸福的人吗?但愿你首先学会吃得起苦。",
"莎士比亚说过,抛弃时间的人,时间也抛弃他。",
"叔本华说过,普通人只想到如何度过时间,有才能的人设法利用时间。",
"博说过,一次失败,只是证明我们成功的决心还够坚强。 维b",
"拉罗什夫科说过,取得成就时坚持不懈,要比遭到失败时顽强不屈更重要。",
"莎士比亚说过,人的一生是短的,但如果卑劣地过这一生,就太长了。",
"俾斯麦说过,失败是坚忍的最后考验。",
"池田大作说过,不要回避苦恼和困难,挺起身来向它挑战,进而克服它。",
"莎士比亚说过,那脑袋里的智慧,就像打火石里的火花一样,不去打它是不肯出来的。",
"希腊说过,最困难的事情就是认识自己。",
"黑塞说过,有勇气承担命运这才是英雄好汉。",
"非洲说过,最灵繁的人也看不见自己的背脊。",
"培根说过,阅读使人充实,会谈使人敏捷,写作使人精确。",
"斯宾诺莎说过,最大的骄傲于最大的自卑都表示心灵的最软弱无力。",
"西班牙说过,自知之明是最难得的知识。",
"塞内加说过,勇气通往天堂,怯懦通往地狱。",
"赫尔普斯说过,有时候读书是一种巧妙地避开思考的方法。",
"笛卡儿说过,阅读一切好书如同和过去最杰出的人谈话。",
"邓拓说过,越是没有本领的就越加自命不凡。",
"爱尔兰说过,越是无能的人,越喜欢挑剔别人的错儿。",
"老子说过,知人者智,自知者明。胜人者有力,自胜者强。",
"歌德说过,意志坚强的人能把世界放在手中像泥块一样任意揉捏。",
"迈克尔·F·斯特利说过,最具挑战性的挑战莫过于提升自我。",
"爱迪生说过,失败也是我需要的,它和成功对我一样有价值。",
"罗素·贝克说过,一个人即使已登上顶峰,也仍要自强不息。",
"马云说过,最大的挑战和突破在于用人,而用人最大的突破在于信任人。",
"雷锋说过,自己活着,就是为了使别人过得更美好。",
"布尔沃说过,要掌握书,莫被书掌握;要为生而读,莫为读而生。",
"培根说过,要知道对好事的称颂过于夸大,也会招来人们的反感轻蔑和嫉妒。",
"莫扎特说过,谁和我一样用功,谁就会和我一样成功。",
"马克思说过,一切节省,归根到底都归结为时间的节省。",
"莎士比亚说过,意志命运往往背道而驰,决心到最后会全部推倒。",
"卡莱尔说过,过去一切时代的精华尽在书中。",
"培根说过,深窥自己的心,而后发觉一切的奇迹在你自己。",
"罗曼·罗兰说过,只有把抱怨环境的心情,化为上进的力量,才是成功的保证。",
"孔子说过,知之者不如好之者,好之者不如乐之者。",
"达·芬奇说过,大胆和坚定的决心能够抵得上武器的精良。",
"叔本华说过,意志是一个强壮的盲人,倚靠在明眼的跛子肩上。",
"黑格尔说过,只有永远躺在泥坑里的人,才不会再掉进坑里。",
"普列姆昌德说过,希望的灯一旦熄灭,生活刹那间变成了一片黑暗。",
"维龙说过,要成功不需要什么特别的才能,只要把你能做的小事做得好就行了。",
"郭沫若说过,形成天才的决定因素应该是勤奋。",
"洛克说过,学到很多东西的诀窍,就是一下子不要学很多。",
"西班牙说过,自己的鞋子,自己知道紧在哪里。",
"拉罗什福科说过,我们唯一不会改正的缺点是软弱。",
"亚伯拉罕·林肯说过,我这个人走得很慢,但是我从不后退。",
"美华纳说过,勿问成功的秘诀为何,且尽全力做你应该做的事吧。",
"俾斯麦说过,对于不屈不挠的人来说,没有失败这回事。",
"阿卜·日·法拉兹说过,学问是异常珍贵的东西,从任何源泉吸收都不可耻。",
"白哲特说过,坚强的信念能赢得强者的心,并使他们变得更坚强。 b",
"查尔斯·史考伯说过,一个人几乎可以在任何他怀有无限热忱的事情上成功。 b",
"贝多芬说过,卓越的人一大优点是:在不利与艰难的遭遇里百折不饶。",
"莎士比亚说过,本来无望的事,大胆尝试,往往能成功。",
"卡耐基说过,我们若已接受最坏的,就再没有什么损失。",
"德国说过,只有在人群中间,才能认识自己。",
"史美尔斯说过,书籍把我们引入最美好的社会,使我们认识各个时代的伟大智者。",
"冯学峰说过,当一个人用工作去迎接光明,光明很快就会来照耀着他。",
"吉格·金克拉说过,如果你能做梦,你就能实现它。"
]
}
65 changes: 65 additions & 0 deletions kbase-stack-sse/src/main/resources/static/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>SSE Samples</title>
</head>
<body>

<h1>Server-Sent Events (SSE) Client</h1>
<div id="messages"></div>
<input type="text" id="messageInput" placeholder="Type a message to send" />
<button onclick="sendMessage()">Send Message</button>

<script type="text/javascript">
const baseUrl = '/api/v1/sse';
// 获取用户token
const token = 'ekozhan-token';
// 创建一个新的EventSource对象,并连接到服务器上的SSE端点
// 替换为你的SSE端点URL,原生的 EventSource 对象无法设置 headers,所以通过 query param 传入,仅支持 GET 请求
const eventSource = new EventSource(baseUrl + '/stream?token=' + token);

// 当接收到新消息时,触发onmessage事件
eventSource.onmessage = function(event) {
// 将接收到的消息添加到页面上
const messages = document.getElementById('messages');
const message = document.createElement('div');
message.textContent = '' + event.data;
messages.appendChild(message);
};

// 如果连接发生错误,触发onerror事件
eventSource.onerror = function(event) {
console.error('EventSource failed:', event);
// 关闭连接
eventSource.close();
};

// 发送消息到服务器
function sendMessage() {
const input = document.getElementById('messageInput');
const message = input.value;

// 使用Fetch API发送消息到服务器
fetch(baseUrl + '/send', { // 替换为你的发送消息的服务器端点URL
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': token
},
body: JSON.stringify({ message: message }),
})
.then(response => {
if (response.ok) {
console.log('Message sent successfully');
input.value = ''; // 清空输入框
} else {
console.error('Failed to send message');
}
})
.catch(error => console.error('Error sending message:', error));
}
</script>

</body>
</html>
Loading

0 comments on commit 3a9c47e

Please sign in to comment.