[PHP] Apache 환경의 같은 호스트 안에서 PHP와 Java(Servlet)를 동시에 기동, 운영하는 방법


Development note/PHP  2019. 10. 17. 09:00

안녕하세요. 명월입니다.

 

이 글은 Apache 환경의 같은 호스트 안에서 PHP와 Java(Servlet)를 동시에 기동, 운영하는 방법에 대한 글입니다.

 

PHP 웹 프로그램 스크립트 언어는 참 매력이 많은 언어입니다. 물론 Java의 servlet로 많은 장점이 있는 언어입니다.

제가 생각하는 PHP의 웹 프로그램 언어의 장점은 컴파일과 빌드, 디플로이(Deploy)가 필요없다는 것입니다. 이게 별거 아닌 것 같지만 실무에서는 크지요..

예를 들면 서비스 운영 중인 프로그램에서 치명적인 에러나 버그가 발생했습니다. 그럼 당연히 고쳐야지요. 그러면 어떻게 수정을 해야 하나 java라면 tomcat을 shutdown하고 디플로이하고 다시 기동해야 합니다.

그 사이에 서버는 내려갑니다. 만약 이게 한창 많은 접속 시간대라고 생각해 봅시다. 버그를 눈 앞에 보고도 수정하지 못합니다. 물론 버그의 성질에 따라 다르겠지만, 서버를 내린다는 게 더 큰 리스크를 가져갈 수도 있기 때문입니다.

하지만 PHP는 어떨까요? 그냥 소스 수정하면 됩니다. 컴파일과 디플로이가 필요없기 때문에 서버 재기동이 필요없습니다. 수정 즉시, 바로 적용입니다.

 

Java는 PHP보다 더 좋은 장점은 무엇이냐하면 스레드 관리입니다. 단순한 홈페이지나 데이터 베이스의 데이터를 가져와서 보여주는 웹 페이지라면 개인적으로 굳이 Java를 사용할까라고 하겠지만, 스레드 관리 서버라고 하면 이야기가 달라집니다.

어떤 시스템의 다른 관리 시스템이던가 웹의 request, response 관리 뿐아니라 지속적으로 시스템이 움직이는 프로그램이라면 PHP로 구현이 거의 불가능이겠지요. 불가능은 아닙니다. Safe Thread가 아닌 Non Thread PHP로 만들면 되긴 합니다만, 이게 메모리 관리가 어렵다고 합니다.(실제로 저도 사용해 본적은 없어서..)

 

그럼 먼저 apache에 php서버를 구축하겠습니다.

이건 기존 구축 과정과 동일합니다. 그래서 따로 PHP만드는 방법은 생략하겠습니다.

링크 - [PHP] PHP 개발환경 만들기와 IDE(Eclipse) 설정하기

 

깔끔하게 index.php에 phpinfo()를 호출해서 화면에 표시합니다.

이제부터 apache에 tomcat(java)을 가상 디렉토리로 연결하겠습니다. 먼저 아파치의 httpd.conf을 열고 중간에 proxy모듈의 주석을 해제합니다.

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
LoadModule proxy_http_module modules/mod_proxy_http.so

그리고 httpd.conf의 가장 하단에 proxy로 톰켓의 ajp를 연결합니다.

<VirtualHost *:80>
  # 호스트 주소
  ServerName localhost
  ProxyRequests Off
  ProxyPreserveHost On
  ## tomcat과 연결할 ajp 주소.
  ProxyPass /java  ajp://localhost:8009/CombinePHP
  ProxyPassReverse /java  ajp://localhost:8009/CombinePHP
</VirtualHost>

저는 일단 디버깅으로 확인할 생각이기 때문에 eclipse로 연결합니다만, 본 서버에서는 tomcat으로 연결하면 됩니다.

먼저 eclipse의 서버 정보를 확인해서 ajp의 포트 번호를 확인합니다.

참고로 servlet 구축은 아래를 참조해 주세요.

링크 - [Java] 38. Java에서 웹 서비스 프로젝트(JSP Servlet)를 작성하는 방법

 

그리고 CombinePHP라는 프로젝트를 생성 후에 JavaThread라는 서블릿을 생성했습니다.

JavaThread라는 클래스에는 ThreadPool를 하나 생성해서 php에서 java의 Thread를 관리하고 리턴을 받을 수 있도록 만들어 보겠습니다.

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
@WebServlet("/JavaThread")
public class JavaThread extends HttpServlet {
  private static final long serialVersionUID = 1L;
  // 생성자
  public JavaThread() { }
 
  // 싱글톤 패턴, Threadpool 변수
  private static ExecutorService service = null;
  private static int timing = 0;
  // get 형식의 요청
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    // 맴버 변수 service가 null일 경우, Thread를 생성
    if (JavaThread.service == null) {
      // 스레드 생성
      JavaThread.service = Executors.newSingleThreadExecutor();
      // 스레드 실행
      JavaThread.service.execute(() -> {
        try {
          // 1 부터 600까지 1초단위 즉, 10분 단위의 스레드 생성하였다.
          for (int i = 0; i < 600; i++) {
            // 현재 카운터의 값은 리턴하기 위해, static맴버 변수로 설정
            timing = i;
            // 스레드 대기, 1초
            Thread.sleep(1000);
          }
          // 스레드 정지
          JavaThread.service.isShutdown();
          // 스레드가 끝나면 맴버 변수를 null로 만든다.
          JavaThread.service = null;
        } catch (Throwable e) {
          e.printStackTrace();
        }
      });
    }
    // 현재 스레드 카운터를 응답한다.
    response.getWriter().append(String.valueOf(timing));
  }
  // post 형식의 요청
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    // get 형식과 같은 처리
    doGet(request, response);
  }
}

위 소스는 /java/JavaThread가 요청될 경우 스레드 생성하는 클래스입니다. 현재 스레드가 실행 중이면 생성하지 않고 실행 중인 스레드의 카운터 값을 리턴합니다.

 

다시 php화면으로 돌아와서 ajax로 위 java servlet를 요청해 보겠습니다.

<!DOCTYPE html>
<html>
<head>
  <title>ajax</title>
</head>
<body>
  <label></label>
  <button id="testBtn">Click!</button>
  <!-- jquery와 moment 라이브러리를 참조한다. -->
  <script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
  <script>
    $(function(){
      // 버튼을 클릭하면 http://localhost/java/JavaThread를 호출한다.
      $("#testBtn").on("click", function(){
        $.ajax({
          type:"GET",
          url:"java/JavaThread",
          // 스레드의 카운터를 넘겨 받는다.
          success:function(msg){
            // label 태그 취득
            $html = $("label").html();
            // 시간과 카운터를 설정
            $html += "<br />" + moment().format("H:m:s") + " : " + msg;
            // label 태그 재 설정
            $("label").html($html);
          }
        });
      });
    });
  </script>
</body>
</html>

위까지 생성이 완료되었으면 apache의 php의 페이지의 localhost/index.php에서 확인해 보겠습니다.

위 예제를 보시면 php 소스에서 ajax로 자바의 서블릿을 호출하여 값을 받아왔습니다. 여기서 중요 포인트는 php의 호스트와 java의 호스트는 같은 localhost라는 점입니다. 다른 포트에서 열린 것도 아니고 같은 포트인 80에서 php와 java가 움직이고 있는 것입니다.

 

이렇게 함으로써 이득이 무엇이냐면 PHP의 생산성을 가져가고 Java의 확장성을 동시에 구현이 가능하다는 점입니다. 또 오픈 라이브러리만 해도 PHP도 많기는 하지만 Java가 압도적으로 많기 때문에 PHP 환경의 프로그램에서 Java의 라이브러리를 간접적으로 사용할 수 있다는 것입니다.

예를 들면 POI같은 라이브러리 말입니다. PHP는 제가 알기로는 POI라이브러리가 없습니다. 즉, Excel이나 파워포인트 파일을 만들기가 어렵다는 뜻입니다.

 

믈론 이 둘을 사용함으로써 어려운 점은 두 언어를 알고 있어야 한다는 점이고, 가장 큰 문제점은 서로 다른 구조의 언어이기 때문에 세션이 공유가 안된다는 점입니다.

정확히 이야기하면 세션이 공유가 안되기 보다는 일반적인 방법이 아닌 file단위 혹은 디비 단위의 세션을 구현해야 하는 번거로움이 있겠네요..

 

여기까지 Apache환경의 같은 호스트 안에서 PHP와 Java(Servlet)를 동시에 기동, 운영하는 방법에 대한 설명이었습니다.

 

궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.