2012-08-26 64 views
17

Tôi nhận được yêu cầu HttpServletRequest trong Spring Servlet của mình mà tôi muốn chuyển tiếp AS-IS (tức là nội dung POST hoặc GET) đến một máy chủ khác.Chuyển tiếp HttpServletRequest đến một máy chủ khác

Cách tốt nhất để làm điều đó bằng khung công tác Spring là gì?

Tôi có cần lấy tất cả thông tin và tạo HTTPUrlConnection mới không? hoặc là có một cách dễ dàng hơn?

Trả lời

4

Rất tiếc, không có cách nào dễ dàng để thực hiện việc này. Về cơ bản bạn sẽ phải xây dựng lại theo yêu cầu, bao gồm:

  • đúng HTTP phương pháp
  • thông số yêu cầu
  • yêu cầu tiêu đề (HTTPUrlConnection không cho phép để thiết lập user agent tùy ý, "Java/1.*" luôn được nối, bạn sẽ cần HttpClient)
  • cơ thể

đó là rất nhiều công việc, chưa kể nó sẽ không mở rộng vì mỗi cuộc gọi dấu hiệu đó sẽ chiếm một thread trên m của bạn đau nhức.

Lời khuyên của tôi: sử dụng ổ cắm thô hoặc và chặn giao thức HTTP ở mức thấp nhất, chỉ thay thế một số giá trị (như tiêu đề Host) khi đang di chuyển. Bạn có thể cung cấp thêm ngữ cảnh, tại sao bạn cần điều này?

+0

Tôi có một khách hàng, một máy chủ trung gian và một vài máy chủ chính. Máy khách chỉ nói chuyện với máy chủ trung gian gửi cuộc gọi đến máy chủ. Máy chủ trả về một phản hồi cho máy chủ trung gian mà sau đó nó xử lý và sau đó trả về phản hồi cho máy khách. – user1144031

+0

không nên sao chép tiêu đề yêu cầu (3) và nội dung (4) bù đắp cho các tham số yêu cầu sao chép (2) (vì tham số bài là một phần của phần thân yêu cầu, vì chúng là một phần của url)? Nó sẽ được reduntant (trong http yêu cầu) nếu tôi thực hiện cả hai bước – mickeymoon

10

thảo luận về việc liệu bạn nên làm chuyển tiếp theo cách này sang một bên, dưới đây là cách tôi đã làm nó:

package com.example.servlets; 

import java.net.HttpURLConnection; 
import java.net.URL; 
import java.util.Enumeration; 

import javax.servlet.http.HttpServlet; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 

import com.example.servlets.GlobalConstants; 

@SuppressWarnings("serial") 
public class ForwardServlet extends HttpServlet { 

    @Override 
    public void doGet(HttpServletRequest req, HttpServletResponse resp) { 
     forwardRequest("GET", req, resp); 
    } 

    @Override 
    public void doPost(HttpServletRequest req, HttpServletResponse resp) { 
     forwardRequest("POST", req, resp); 
    } 

    private void forwardRequest(String method, HttpServletRequest req, HttpServletResponse resp) { 
     final boolean hasoutbody = (method.equals("POST")); 

     try { 
      final URL url = new URL(GlobalConstants.CLIENT_BACKEND_HTTPS // no trailing slash 
        + req.getRequestURI() 
        + (req.getQueryString() != null ? "?" + req.getQueryString() : "")); 
      HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 
      conn.setRequestMethod(method); 

      final Enumeration<String> headers = req.getHeaderNames(); 
      while (headers.hasMoreElements()) { 
       final String header = headers.nextElement(); 
       final Enumeration<String> values = req.getHeaders(header); 
       while (values.hasMoreElements()) { 
        final String value = values.nextElement(); 
        conn.addRequestProperty(header, value); 
       } 
      } 

      //conn.setFollowRedirects(false); // throws AccessDenied exception 
      conn.setUseCaches(false); 
      conn.setDoInput(true); 
      conn.setDoOutput(hasoutbody); 
      conn.connect(); 

      final byte[] buffer = new byte[16384]; 
      while (hasoutbody) { 
       final int read = req.getInputStream().read(buffer); 
       if (read <= 0) break; 
       conn.getOutputStream().write(buffer, 0, read); 
      } 

      resp.setStatus(conn.getResponseCode()); 
      for (int i = 0; ; ++i) { 
       final String header = conn.getHeaderFieldKey(i); 
       if (header == null) break; 
       final String value = conn.getHeaderField(i); 
       resp.setHeader(header, value); 
      } 

      while (true) { 
       final int read = conn.getInputStream().read(buffer); 
       if (read <= 0) break; 
       resp.getOutputStream().write(buffer, 0, read); 
      } 
     } catch (Exception e) { 
      e.printStackTrace(); 
      // pass 
     } 
    } 
} 

Rõ ràng điều này có thể sử dụng một chút công việc liên quan với lỗi xử lý và những thứ tương tự nhưng nó là chức năng . Tôi đã ngừng sử dụng nó, tuy nhiên, bởi vì nó dễ dàng hơn trong trường hợp của tôi để thực hiện cuộc gọi trực tiếp đến CLIENT_BACKEND hơn để đối phó với cookie, auth, vv trên hai tên miền riêng biệt.

2

Tôi cũng cần làm như vậy và sau một số không tối ưu với bộ điều khiển Spring và RestTemplate, tôi đã tìm thấy giải pháp tốt hơn: Smiley's HTTP Proxy Servlet. Lợi ích là, nó thực sự thực hiện AS-IS proxy, giống như Apache mod_proxy, và nó thực hiện nó theo một cách trực tuyến, mà không cần lưu trữ toàn bộ yêu cầu/đáp ứng trong bộ nhớ.

Đơn giản, bạn đăng ký một servlet mới vào đường dẫn bạn muốn proxy đến một máy chủ khác, và cung cấp cho servlet này máy chủ đích làm tham số init. Nếu bạn đang sử dụng một ứng dụng web truyền thống với một web.xml, bạn có thể cấu hình nó như sau:

<servlet> 
    <servlet-name>proxy</servlet-name> 
    <servlet-class>org.mitre.dsmiley.httpproxy.ProxyServlet</servlet-class> 
    <init-param> 
     <param-name>targetUri</param-name> 
     <param-value>http://target.uri/target.path</param-value> 
    </init-param> 
</servlet> 
<servlet-mapping> 
    <servlet-name>proxy</servlet-name> 
    <url-pattern>/mapping-path/*</url-pattern> 
</servlet-mapping> 

hoặc, tất nhiên, bạn có thể đi với annotation config.

Nếu bạn đang sử dụng Spring Boot, nó thậm chí còn dễ dàng hơn: Bạn chỉ cần tạo một bean loại ServletRegistrationBean, với cấu hình yêu cầu:

@Bean 
public ServletRegistrationBean proxyServletRegistrationBean() { 
    ServletRegistrationBean bean = new ServletRegistrationBean(
      new ProxyServlet(), "/mapping-path/*"); 
    bean.addInitParameter("targetUri", "http://target.uri/target.path"); 
    return bean; 
} 

Bằng cách này, bạn cũng có thể sử dụng các tính chất mùa xuân mà có sẵn trong môi trường.

Bạn thậm chí có thể mở rộng lớp ProxyServlet và ghi đè phương pháp của nó để tùy chỉnh yêu cầu/tiêu đề phản hồi, v.v. trong trường hợp bạn cần.

Cập nhật: Sau khi sử dụng servlet proxy của Smiley một thời gian, chúng tôi gặp phải một số sự cố hết thời gian, nó không hoạt động đáng tin cậy. Đã chuyển sang Zuul từ Netflix, không gặp bất kỳ sự cố nào sau đó. Một hướng dẫn về cấu hình nó với Spring Boot có thể được tìm thấy trên this link.

Các vấn đề liên quan