Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Request by restTemplate to Netty Server hangs the thread

RestTemplate example is below.

public class SimpleClient {

    private final String URL;
    private AsyncRestTemplate rest = new AsyncRestTemplate(new Netty4ClientHttpRequestFactory());
    private RestTemplate restTemplate = new RestTemplate(new Netty4ClientHttpRequestFactory());

    public SimpleClient(String url) {
        this.URL = url;
        Netty4ClientHttpRequestFactory nettyFactory = new Netty4ClientHttpRequestFactory();
        try {
                    nettyFactory.setSslContext(SslContextBuilder.forClient().build());
        } catch (SSLException e) {
            e.printStackTrace();
        }
        rest = new AsyncRestTemplate(nettyFactory);
    }

    @Override
    public ResponseEntity<ResponseData> doSendByPOST(RequestData data,Class<ResponseData> clazz) {

        List<HttpMessageConverter<?>> messageConvertors = new ArrayList<>();
        messageConvertors.add(new MappingJackson2HttpMessageConverter());

        rest.setMessageConverters(messageConvertors);
        restTemplate.setMessageConverters(messageConvertors);

        HttpHeaders headers = new HttpHeaders();
        ObjectMapper objectMapper = new ObjectMapper();
        StringWriter writer = new StringWriter();
        try {
            objectMapper.writeValue(writer, data);
        } catch (IOException e) {
            e.printStackTrace();
        }
        headers.set(HttpHeaders.CONTENT_LENGTH,String.valueOf(writer.toString().getBytes(Charset.forName("UTF-8")).length));
        headers.set(HttpHeaders.CONTENT_TYPE,"application/json");
        HttpEntity<ResponseData> request = new HttpEntity<ResponseData>(headers);

        MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
        try {
            parts.add("requestData", objectMapper.writeValueAsString(data));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

//      return restTemplate.exchange(this.URL,HttpMethod.POST ,request, clazz, parts);

        ListenableFuture<ResponseEntity<ResponseData>> entity =  rest.exchange(this.URL,HttpMethod.POST ,request, clazz, parts);
        return extractResponseEntity(entity);
    }
    // ...
}

Netty read data from request channelRead method

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

    if (msg instanceof HttpRequest) {
        DefaultHttpRequest defaultHttpRequest = (DefaultHttpRequest) msg;
        if (EmptyHttpHeaders.is100ContinueExpected(defaultHttpRequest)) {
            ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.CONTINUE));
        }

        boolean keepAlive = EmptyHttpHeaders.isKeepAlive(defaultHttpRequest);


        handle = frontController.dispatchRequest(defaultHttpRequest);

    }
    if (msg instanceof HttpContent) {
        HttpContent httpContent = (HttpContent) msg;
        ByteArrayOutputStream body = new ByteArrayOutputStream(64);
        ByteBuf content = httpContent.content();
        if (content.isReadable()) {
            //body.write(content.array());
            content.readBytes(body,content.readableBytes());
            //body.append(content.toString(CharsetUtil.UTF_8));
            FullHttpResponse response = handle.handle(body);
            if(response == null){
                response = prepareDefaultResponse();
            }

            response.headers().set("content-type", "application/json");
            response.headers().set("content-length", response.content().readableBytes());
            response.headers().set("connection", HttpHeaderValues.KEEP_ALIVE);

        }

        if (msg instanceof LastHttpContent) {
            //process request
            ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
        }
    }

The code below is working fine but I guess there is a problem with blocking io and nonblocking io. When the request is dispatched, I can not reach the HttpContent I only get HttpRequest as a msg parameter. Spring resttemplate waits for a response but Netty does not care :)

 if (msg instanceof HttpRequest) {
     DefaultHttpRequest defaultHttpRequest = (DefaultHttpRequest) msg;
     if (EmptyHttpHeaders.is100ContinueExpected(defaultHttpRequest)) {
         ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.CONTINUE));
     }

     boolean keepAlive = EmptyHttpHeaders.isKeepAlive(defaultHttpRequest);


     handle = frontController.dispatchRequest(defaultHttpRequest);

 }

My problem is how to get response from netty server by rest template. I have tried many ways to accomplish full req/resp. When restTemplate request to Netty server it hangs the thread so I can not move on the distributed in memory cache implementation.

Hanging in RestTemplate.java Line : 681

Method waits forever when using Netty4ClientHttpRequestFactory.

response = request.execute();
like image 548
Gurkan İlleez Avatar asked Jan 01 '18 09:01

Gurkan İlleez


1 Answers

From my understanding, you read HTTP post request that from Rest Client as HttpRequest Object lets call it first case so that means you don't even branch on the if (msg instanceof HttpContent) {} case (second one) your HTTP server just writes the default response without any content or header that you're setting in the second case. If this is the cause for the blocking on the client side you have to fill that default response just like on the second case an see what client do.

I think netty API provides this https://netty.io/4.1/api/io/netty/handler/codec/http/DefaultFullHttpResponse.html

Also this example could give you an idea of what could be wrong server side. http://www.seepingmatter.com/2016/03/30/a-simple-standalone-http-server-with-netty.html

like image 107
Alican Beydemir Avatar answered Oct 18 '22 03:10

Alican Beydemir