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