我有一个Java服务器和一个JavaScript客户端,它们已经成功地使用服务器发送事件来发送更新。最近,我们被迫在使用内置于kubernetes中的NGINX作为服务器和客户端之间的代理之间切换,使用基于kong的代理,该代理以不同的配置扩展了NGINX。新的代理会缓冲SSE破坏协议,并且由于其他应用程序正在使用新的代理,因此我无法关闭所有缓冲,这是以前的代理解决该问题的方式。作为替代方案,我一直在尝试向SSE响应中添加3个http标头,以触发NGINX为该特定端点关闭缓冲:
"Content-Type" : "text/event-stream"
"Cache-Control", "no-cache"
"X-Accel-Buffering", "no"
我已经能够相对轻松地添加“ Cache-Control”和“ X-Accel-Buffering”标头,但是我尝试了几种不同的方法来添加“ Content-Type”标头,但没有任何效果。
以下是第一次尝试,请注意,“ Content-Type”标头的设置与RestServiceImpl中的其他两个标头相同,但是从我的日志中,添加了另外两个标头,而“ Content-Type”却没有。
@Api
@Path("/service")
public interface RestService {
@GET
@Path("/sseconnect")
@Produces(SseFeature.SERVER_SENT_EVENTS)
EventOutput listenToBroadcast(@Context HttpServletResponse response);
}
@Component
public class RestServiceImpl implements RestService {
@Autowired
private Broadcaster broadcaster;
public RestServiceImpl() {
}
@Override
public EventOutput listenToBroadcast(HttpServletResponse response) {
response.addHeader("Content-Type", "text/event-stream");
response.addHeader("Cache-Control", "no-cache");
response.addHeader("X-Accel-Buffering", "no");
return broadcaster.add();
}
}
public class Broadcaster extends SseBroadcaster {
private final OutboundEvent.Builder eventBuilder =
new OutboundEvent.Builder();
public EventOutput add() {
final EventOutput eventOutput = new EventOutput();
super.add(eventOutput);
return eventOutput;
}
public void sendEvents(Events events, String name) {
final OutboundEvent outboundEvent = eventBuilder.name(name)
.mediaType(MediaType.APPLICATION_JSON_TYPE)
.data(Events.class, events).build();
super.broadcast(outboundEvent);
}
}
第二次尝试尝试修改“ Content-Type”标头的添加方式,如下所示:
@Override
public EventOutput listenToBroadcast(HttpServletResponse response) {
response.setContentType("text/event-stream");
response.addHeader("Cache-Control", "no-cache");
response.addHeader("X-Accell-Buffering", no);
return broadcaster.add();
}
具有相同的效果,添加了“ Cache-Control”和“ X-Accell-Bufferig”,但没有添加“ Content-Type”。
对于第三次尝试,我用ContainerResponse替换了HttpServletResponse
@Override
public EventOutput listenToBroadcast(ContainerResponse containerResponse)
{
final MultivaluedMap<String, Object> headers =
containerResponse.getHeaders();
headers.add("Content-Type", "text/event-stream");
headers.add("Cache-Control", "no-cache");
headers.add("X-Accel-Buffering", "no");
return broadcaster.add();
}
此解决方案破坏了端点并最终给我一个406错误,所以我不能确切地说它是行不通的。可能是不好的实现,但我什么都没看到。
对于我的第四次尝试,我尝试使用ContainerResponseFilter添加标头。
@Provider
@PreMatching
public class SseResponseFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext)
throws IOException {
final MultivaluedMap<String, Object> headers =
responseContext.getHeaders();
final List<Object> contentTypeValues = new ArrayList<Object>();
contentTypeValues.add("text/event-stream");
headers.put("Content-Type", contentTypeValues);
final List<Object> cacheControlValues = new ArrayList<Object>();
cacheControlValues.add("no-cache");
headers.put("Cache-Control", cacheControlValues);
final List<Object> xAccelBufferingValues =
new ArrayList<Object>();
xAccelBufferingValues.add("no");
headers.put("X-Accel-Buffering", xAccelBufferingValues);
}
}
此尝试将“ Cache-Control”和“ X-Accel-Buffering”标头添加到应用程序中的每个端点,但未在任何地方添加“ Content-Type”。请注意,其中一些其他api返回的响应对象允许设置“ Content-Type”,并且这些REST端点为每个特定端点正确设置了“ Content-Type”头。 SSE库返回EventOutput,不幸的是,它不允许我像Response那样设置Content-Type标头。
对于第五次尝试,我用WriterInterceptor替换了ContainerResponseFilter
@Provider
public class SseWriterInterceptor implements WriterInterceptor {
@Override
public void arroundWriteTo(WriterInterceptorContext context)
throws IOException, WebApplicationException {
final MultivaluedMap<String, Object> headers =
context.getHeaders();
final List<Object> contentTypeValues = new ArrayList<Object>();
contentTypeValues.add("text/event-stream");
headers.put("Content-Type", contentTypeValues);
final List<Object> cacheControlValues = new ArrayList<Object>();
cacheControlValues.add("no-cache");
headers.put("Cache-Control", cacheControlValues);
final List<Object> xAccelBufferingValues =
new ArrayList<Object>();
xAccelBufferingValues.add("no");
headers.put("X-Accel-Buffering", xAccelBufferingValues);
}
}
此解决方案的工作原理与以前的相同,在该解决方案中,它向应用程序的所有端点添加了“缓存控制”和“ X-加速缓存”,但再次取消了“内容类型”。
总而言之,我似乎无法弄清楚是什么原因阻止了我向我的SSE端点成功添加“ Content-Type”标头。
此问题类似于: Why does Jersey swallow my "Content-Encoding" header 但是,给定的将“ Content-Type”添加到Response对象的解决方案不起作用,因为我使用的是SSE,该方法返回的EventOutput对象没有添加“ Content-Type”标头的方法。
答案 0 :(得分:0)
事实证明,泽西岛不是未设置“ Content-Type”标头的罪魁祸首。该应用程序是作为连续部署环境的一部分而构建的,该环境针对日志记录和安全扫描之类的过滤器添加了许多过滤器。这些过滤器之一是防止使用SSE库时填充“ Content-Type”标头。
在本地计算机上运行该应用程序,然后在其基于kubernetes的开发环境中运行该应用程序,并看到本地第一个解决方案正确填充了“ Content-Type”标头之后,我能够确定这是原因。