-
Notifications
You must be signed in to change notification settings - Fork 828
[#4873] Support for SSE interface RPC calls #4875
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 21 commits
40d7c41
14c05e7
7eda5d9
517480b
500e8b3
5f3a593
a7135bd
58050a5
45625a0
af92830
0332c62
0cd0c47
0e3e646
9f7a9dc
7076c84
76da23e
2b32da4
255dee9
7fb5f75
cfbe610
10255be
1adfc0b
4c2c34e
63e6c1a
0812b8b
e38bb52
0da8280
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,7 +36,7 @@ jobs: | |
| - name: Set up jdk | ||
| uses: actions/setup-java@v3 | ||
| with: | ||
| java-version: '21' | ||
| java-version: '17' | ||
| distribution: 'temurin' | ||
| - name: Set up Maven | ||
| uses: stCarolas/[email protected] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one or more | ||
| * contributor license agreements. See the NOTICE file distributed with | ||
| * this work for additional information regarding copyright ownership. | ||
| * The ASF licenses this file to You under the Apache License, Version 2.0 | ||
| * (the "License"); you may not use this file except in compliance with | ||
| * the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package org.apache.servicecomb.common.rest.codec.produce; | ||
|
|
||
| import java.io.InputStream; | ||
| import java.io.OutputStream; | ||
| import java.nio.charset.StandardCharsets; | ||
| import java.util.ArrayList; | ||
| import java.util.Arrays; | ||
| import java.util.List; | ||
|
|
||
| import org.apache.commons.lang3.StringUtils; | ||
| import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; | ||
| import org.apache.servicecomb.swagger.invocation.sse.SseEventResponseEntity; | ||
|
|
||
| import com.fasterxml.jackson.databind.JavaType; | ||
|
|
||
| import jakarta.ws.rs.core.MediaType; | ||
|
|
||
| public class ProduceEventStreamProcessor implements ProduceProcessor { | ||
| public static final List<String> DEFAULT_DELIMITERS = Arrays.asList("\r\n", "\n", "\r"); | ||
|
|
||
| @Override | ||
| public String getName() { | ||
| return MediaType.SERVER_SENT_EVENTS; | ||
| } | ||
|
|
||
| @Override | ||
| public int getOrder() { | ||
| return 0; | ||
| } | ||
|
|
||
| @Override | ||
| public void doEncodeResponse(OutputStream output, Object result) throws Exception { | ||
| StringBuilder bufferBuilder = new StringBuilder(); | ||
| if (result instanceof SseEventResponseEntity<?> responseEntity) { | ||
| appendEventId(bufferBuilder, responseEntity.getEventId()); | ||
| appendEvent(bufferBuilder, responseEntity.getEvent()); | ||
| appendRetry(bufferBuilder, responseEntity.getRetry()); | ||
| appendData(bufferBuilder, responseEntity.getData()); | ||
| bufferBuilder.append("\n"); | ||
| output.write(bufferBuilder.toString().getBytes(StandardCharsets.UTF_8)); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public Object doDecodeResponse(InputStream input, JavaType type) throws Exception { | ||
| String buffer = new String(input.readAllBytes(), StandardCharsets.UTF_8); | ||
| List<String> lines = new ArrayList<>(); | ||
| splitStringByDelimiters(buffer, lines); | ||
| SseEventResponseEntity<?> responseEntity = new SseEventResponseEntity<>(); | ||
| for (String line : lines) { | ||
| if (line.startsWith("eventId:")) { | ||
| responseEntity.eventId(Integer.parseInt(line.substring("eventId:".length()).trim())); | ||
| continue; | ||
| } | ||
| if (line.startsWith("event:")) { | ||
| responseEntity.event(line.substring("event:".length()).trim()); | ||
| continue; | ||
| } | ||
| if (line.startsWith("retry:")) { | ||
| responseEntity.retry(Long.parseLong(line.substring("retry:".length()).trim())); | ||
| continue; | ||
| } | ||
| if (line.startsWith("data:")) { | ||
| responseEntity.data(RestObjectMapperFactory.getRestObjectMapper() | ||
| .readValue(line.substring("data:".length()).trim(), type)); | ||
| } | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这里要不要加个消息合法性校验, 比如之前集成测试遇到过的消息粘包导致的解析问题. 如果字段重复了, 打一个告警日志会好分析很多.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
| return responseEntity; | ||
| } | ||
|
|
||
| private void splitStringByDelimiters(String str, List<String> lines) { | ||
| boolean isContainsDelimiters = false; | ||
| for (String split : DEFAULT_DELIMITERS) { | ||
| if (str.contains(split)) { | ||
| isContainsDelimiters = true; | ||
| splitStrings(str.split(split), lines); | ||
| } | ||
| } | ||
| if (!isContainsDelimiters) { | ||
| lines.add(str); | ||
| } | ||
| } | ||
|
|
||
| private void splitStrings(String[] strings, List<String> lines) { | ||
| for (String str : strings) { | ||
| if (StringUtils.isEmpty(str)) { | ||
| continue; | ||
| } | ||
| splitStringByDelimiters(str, lines); | ||
| } | ||
| } | ||
|
|
||
| private void appendEventId(StringBuilder eventBuilder, Integer eventId) { | ||
| if (eventId == null) { | ||
| return; | ||
| } | ||
| eventBuilder.append("eventId: ").append(eventId.intValue()).append("\n"); | ||
|
||
| } | ||
|
|
||
| private void appendEvent(StringBuilder eventBuilder, String event) { | ||
| if (StringUtils.isEmpty(event)) { | ||
| return; | ||
| } | ||
| eventBuilder.append("event: ").append(event).append("\n"); | ||
| } | ||
|
|
||
| private void appendRetry(StringBuilder eventBuilder, Long retry) { | ||
| if (retry == null) { | ||
| return; | ||
| } | ||
| eventBuilder.append("retry: ").append(retry.longValue()).append("\n"); | ||
| } | ||
|
|
||
| private void appendData(StringBuilder eventBuilder, Object data) throws Exception { | ||
| if (data == null) { | ||
| throw new Exception("sse response data is null!"); | ||
| } | ||
| eventBuilder.append("data: ") | ||
| .append(RestObjectMapperFactory.getRestObjectMapper().writeValueAsString(data)) | ||
| .append("\n"); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
建议保留 else 分支, 打印告警日志 ---- 如果 result 类型不是
SseEventResponseEntity, 我们不应该悄无声息地把错误放过去了.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed