[필기정리]Day79-1 - Connection Pool, JNDI, Data Source 등

Web/JSP

2020. 10. 20. 17:17

# JSP 저장영역

ex)

- setApplicationAttribute.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	String name = request.getParameter("name");
	String value = request.getParameter("value");
	
	if(name != null && value != null){
		application.setAttribute(name,value); //전역변수
		//application 기본 객체에 속성을 설정. 파라미터로 전달받은 값을 속성 이름과 값으로 사용
	}
%>
<!DOCTYPE>
<html>
<head>
<title>application 속성 지정</title>
</head>
<body>
<%
	if (name != null && value != null){
%>
	application 기본 객체의 속성 설정
	<%=name %> = <%=value %>
<%
	}else{
%>
		application 기본 객체의 속성 설정 안함.
<%
	}
%>
</body>
</html>

- viewApplicationAttribute.jsp

<%@ page import="java.util.Enumeration"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE>
<html>
<head>
<title>application 기본 객체 속성보기</title>
</head>
<body>
<%
	Enumeration<String> attrEnum = application.getAttributeNames();
	//속성의 이름 목록을 구한다.
	while(attrEnum.hasMoreElements()){
		String name = (String) attrEnum.nextElement();
		Object value = application.getAttribute(name);
		//이름이 name의 속성의 값을 구한다.
%>
	application 속성 : <b><%=name %></b> = <%=value %> <br>
<%
	}
%>
</body>
</html>

- request_PageContextSave.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE>
<html>
<head>
<title>Insert title here</title>
</head>
<body>
	<h2> request와 pageContext 차이 알아보기</h2>
	
	<%
		//정보저장하기		
		request.setAttribute("id", "hong");
		pageContext.setAttribute("name","홍길동");	
	%>
	
	<h2> 저장된 정보 출력해보기 </h2>
	request : <%=request.getAttribute("id") %><br/>
	pageContext : <%=pageContext.getAttribute("name") %> <br/>
	
	<hr>
	
	<a href="request_PageContextGet.jsp"> 이동하기 </a>
	<!--  pageContext.forward("request_PageContextGet.jsp") -->
	<jsp:forward page="request_PageContextGet.jsp"/>
</body>
</html>

- request_PageContextGet.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE>
<html>
<head>
<title>Insert title here</title>
</head>
<body>
	<h2> request와 pageContext 정보 가져오기 </h2>
	request : <%=request.getAttribute("id") %><br/>
	pageContext : <%=pageContext.getAttribute("name") %> <br/>	
	<hr>
</body>
</html>

 

# Connection Pool(커넥션 풀)

   데이터베이스와 연결된 Connection 객체를 미리 생성하여 풀(Pool) 속에 저장해두고

   필요할 때마다 이 풀에 접근하여 Connection 객체를 사용하고, 작업이 끝나면 다시 반환하는 것

 

[이미지출처] https://www.kdata.or.kr/info/info_04_view.html?field=&keyword=&type=techreport&page=18&dbnum=183740&mode=detail&type=techreport

 

- Connection Pool의 필요성 및 장점

  사용자가 웹사이트에 요청을 할 때마다 매번 Connection 객체를 생성하여 연결 시

  메모리에 너무 많은 객체 생성된다는 비효율성 및 문제점이 있다.

 

  커넥션 풀을 사용하는 경우

① Pool 속에 미리 Connection이 생성 되어 있기 때문에

    Connection 생성 시 소요되는 연결 시간이 소비되지 않는다.

② 현재 다른 사용자가 사용하지 않는 Connection을 재사용할 수 있기 때문에

    사용자가 접속할 때마다 계속 새로운 Connection을 생성할 필요가 없다.

③ 한 번에 생성될 수 있는 Connection 수를 제어하기 때문에

    동시 접속자 수가 몰려도 어플리케이션이 쉽게 다운되지 않는다.

④ 커넥션을 생성하고 닫는 시간을 소모하지 않아 어플리케이션의 실행 속도가 빨라진다.    

 

- Connection Pool의 속성

속성 설명
maxActive Connection Pool이 제공할 최대 Connection 갯수
whenExhaustedAction Connection Pool에서 가져올 수 있는 Connection이 없을 때 어떻게 동작할 것인지 지정함
0 : 에러 발생
1 : maxWait 속성에서 지정한 시간만큼 Connection을 구할 때까지 기다림
2 : 일시적으로 Connection을 생성해서 사용
maxWait whenExhaustedAction 속성 값이 1일 때 사용되는 대기 시간
단위 : 1/1000초 
0보다 작을 경우 무한 대기함
maxIdle 사용되지 않고 Pool에 저장될 수 있는 최대 Connection 갯수
음수일 경우 제한 없음
minIdle 사용되지 않고  Pool에 저장될 수 있는 최소 Connection 갯수
testOnBorrow true일 경우 Connection Pool에서 Connection을 가져올 때
Connection이 유효한지 여부 검사
testOnReturn true일 경우 Connection Pool에서 Connection을 반환할 때 
Connection이 유효한지 여부 검사
timeBetweenEvctionRunsMillis 사용되지 않는 Connection을 추출하는 Thread의 실행 주기 지정
양수가 아닌 경우 실행되지 않음
단위 : 1/1000초
 numTestsPerEvictionRun 사용되지 않는 Connection을 몇 개 검사할 지 지정
minEvictableIdleTimeMillis 사용되지 않는 Connection을 추출할 때
이 속성에서 지정한 시간 이상 비활성화 상태인 Connection만 추출
양수가 아닌 경우 비활성화된 시간으로는 Pool에서 제거되지 않음
단위 : 1/1000초
testWhileIdle true일 경우 비활성화 Connection을 추출할 때
Connection이 유효한지 여부 검사하여 유효하지 않은 Connection은 Pool에서 제거함

 

+

https://linked2ev.github.io/spring/2019/08/14/Spring-3-%EC%BB%A4%EB%84%A5%EC%85%98-%ED%92%80%EC%9D%B4%EB%9E%80/

 

[Spring] 커넥션 풀(Connection pool)이란?

커넥션 풀(Connection pool)에 대해서 그래도 알고 사용하자. 쉽게 알고 갔으면 좋겠습니다.

linked2ev.github.io

 

- JNDI(Java Naming and Directory Interface)

 

Jsp&Servlet:: JNDI란? , 이클립스 톰캣 DataSource 설정방법

JNDI란? 실제 웹 어플리케이션에서 ConnectionPool 객체를 구현할 때는 Java SE에서 제공하는 javax.sql.DataSource 클래스를 이용한다. 그리고 웹 어플리케이션실행시 톰캣이 만들어 놓은 ConnectionPool 객체에.

java117.tistory.com

  명디렉터리 서비스에서 제공하는 데이터 및 객체를 발견하고 참고하기 위한 자바 API

  필요한 자원을 key/value 쌍으로 저장한 후 필요할 때 키를 이용해 값을 얻는 방법으로,

  미리 접근할 자원에 키를 지정한 후 어플리케이션이 실행 중일 때 이 키를 이용해 자원에 접근한 후 작업하는 것

 



① Naming & Directory 서비스 : 분산환경에서 자원을 연결해주는 기능
    Naming & Directory 서비스는 실제 어떤 자원을 가지고 서비스 한다는 의미가 아닌 

    어떤 서버나 애플리케이션에서 분산환경에 서비스하고자 하는 자원을 

    이 Naming & Directory 서버에 이름값과 실제 자원을 연결하여 등록하면,

    해당 자원을 이용하고자 하는 다른 애플리케이션에서 Naming & Directory 서버에 접근하여

    이름값 만을 가지고 자원을 연결하여 이용할 수 있게 하는 개념이다.


    네이밍 서비스의 대표적인 예로 DNS서버가 이런 기능을 한다.

    브라우저창에 URL을 입력하면 브라우저는 DNS서버를 통해 도메인에 해당하는 IP주소를 얻는다.

    그리고 IP 주소를 통해 실제 인터넷 서버에 접속한다.

    결국 DNS 서버는 실제 인터넷 서비스를 수행해주는 곳이 아니며,

    단지 도메인과 IP 주소만을 연결해주는 기능을 하는 것이다.

- Data Source

  Connection Pool 관리 목적으로 사용되는 객체

  서버에서 관리하는 리소스인 커넥션 풀을 사용할 수 있게 한다.

  어플리케이션에서는 이 객체를 통해 Connection을 얻어오고 반납하는 등의 작업을 수행할 수 있다.

 

  서버가 시작할 때 커넥션 풀이 서버에 준비되어 있어야 합니다.

  DB 프로그래밍을 할 때 커넥션 풀에서 Connection 객체를 얻어낼 수 있기 때문이다.

 

 

DataSource - JSP/서블릿 흝어 보기

Connection 관리 기존 JDBC 프로그램 구현으로 DBMS와 연동 작업을 할때는 웹 클라이언트로부터 요청이 있을 때마다 DB서버에 연결하기 위해 Connection 객체를 얻어내야 했습니다. 이러한 기존의 JDBC 프

opentutorials.org

 

- Pooling 기법

  디자인 패턴의 일종

  한 번 생성될 때 자원의 소모가 크며, 계속해서 생성, 소멸시킬 필요가 없다고 생각되는 객체를

  미리 여러 개 만들어 놓은 뒤, 그 것을 계속 사용하는 기법

 

  흔히 Servlet과 같은 웹프로그래밍에서는, DB Connection Pooling을 대부분 사용한다. 
  DB와의 통신이 빈번하게 이뤄지는 웹 어플리케이션의 경우,
  DB Connection 생성에서 자원의 소모가 가장 심하게 발생한다.
  수백 명의 클라이언트가 동시에 접속해서 수십번의 요청을 날리는 경우 
  그 횟수만큼 Connection 객체를 생성/소멸시키면, 서버의 부하를 초래할 수 있다.

  
  따라서 미리 연결을 시켜놓은 몇 개의 Connection 객체를 생성해놓고,

  이들을 별도의 공간에 대기시키는데 이를 Factory라고 한다.

 

 위와 같이 미리 연결되어 있는 일정 수의 Connection 객체를 돌아가면서 사용하는 식으로 DB 연결이 이뤄진다.

 정확히는 계속 DB는 연결되어있고 돌아가면서 그 연결을 통해 쿼리를 날리는 것이다. 

 

 보통 Servlet Container들에서는 JNDI와 DataSource를 이용하여 DB Connection Pooling을 쉽게 구현할 수 있다.

 

https://tomcat.apache.org/tomcat-9.0-doc/jdbc-pool.html

 

Apache Tomcat 9 (9.0.39) - The Tomcat JDBC Connection Pool

The connection pool object exposes an MBean that can be registered. In order for the connection pool object to create the MBean, the flag jmxEnabled has to be set to true. This doesn't imply that the pool will be registered with an MBean server, merely tha

tomcat.apache.org

ex)

- list.jsp

<%@page import="java.util.ArrayList"%>
<%@page import="com.superman.www.*"%>
<%@ page import="java.sql.*"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="description" content="HTML Study">
<meta name="keywords" content="HTML,CSS,XML,JavaScript">
<meta name="author" content="Bruce">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>게시판 목록</title>
</head>
<body>
<h3>목록</h3>
<%
	BoardDAO dao = new BoardDAO();
	ArrayList<BoardDTO> list = dao.selectBoard();
	for(int i=0;i<list.size();i++)
	{
		BoardDTO dto = list.get(i);
		out.print("번호 " + dto.getNo()+"<br>");
		out.print("제목 " + dto.getTitle()+"<br>");
		out.print("작성자 " + dto.getName()+"<br>");
		out.print("작성일 " + dto.getWtime()+"<br>");
		out.print("조회수 " + dto.getRcnt()+"<br><br>");
	}
%>
<a href="write.jsp">글쓰기</a>
</body>
</html>

-  BoardDAO.java

package com.superman.www;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

public class BoardDAO {
//	private String driver = "com.mysql.cj.jdbc.Driver";
//	private String url = "jdbc:mysql://localhost:3306/testdb?serverTimezone=UTC";
//	private String user = "root";
//	private String password = "1234";
	private DataSource dataSource;

	public BoardDAO() {
//		try {
//			Class.forName(driver);
//		} catch(ClassNotFoundException e) {
//			System.out.println("드라이버 로드 실패");
//		}
		try {
			Context context = new InitialContext();
			dataSource = (DataSource)context.lookup("java:comp/env/jdbc/testdb");
		} catch (Exception e) {
			e.printStackTrace();
		}		
	}
	
	public ArrayList<BoardDTO> selectBoard()
	{
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		String sql = "SELECT no, title, name, wtime, rcnt FROM board ORDER BY no DESC";
		ArrayList<BoardDTO> list = new ArrayList<BoardDTO>();
		try {
//			conn = DriverManager.getConnection(url, user, password);
			conn = dataSource.getConnection();
			pstmt = conn.prepareStatement(sql);
			rs = pstmt.executeQuery();
			while(rs.next())
			{
				BoardDTO dto = new BoardDTO();
				dto.setNo(rs.getInt("no"));
				dto.setTitle(rs.getString("title"));
				dto.setName(rs.getString("name"));
				dto.setWtime(rs.getString("wtime"));
				dto.setRcnt(rs.getInt("rcnt"));
				list.add(dto);
			}
		} catch(SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if(rs != null) rs.close();
				if(pstmt != null) pstmt.close();
				if(conn != null) conn.close();
			} catch(SQLException e) {
				e.printStackTrace();
			}
		}
		return list;		
	}	
}

커넥션 풀에 접근하려면 JNDI 서비스를 사용해야 한다. 

JNDI는 서버에서 관리하고 있는 리소스에 대한 정보를 알고 있고

특정 리소스를 찾아서 사용할 수 있도록 객체를 반환해주는 역할을 한다.

JNDI 서버역할을 하는 객체를 생성한다.

리소스가 로컬에 있을 때는 단순히 InitialContext 객체만 생성하면 된다. 

 

// Tip : context.lookup( )

   리소스를 찾은 후 리소스를 사용할 수 있도록 객체를 반환해주는 메소드

   lookup( ) 메소드의 인자값으로는 찾으려는 리소스의 등록된 이름을 지정한다. 

   

   우리가 찾으려는 리소스의 이름은 "jdbc/testdb"이고

   WAS인 톰캣에서 리소스를 관리하는 가상의 디렉터리는 "java:comp/env"입니다.

   

   그래서 lookup( ) 메소드의 최종인자 값은 "java:comp/env/jdbc/testdb"이 됩니다.

   lookup( ) 메소드가 반환하는 객체의 타입은 Object 이기 때문에 원래 리소스 타입으로 변환해줍니다.

   앞에서 server.xml 파일에 커넥션 풀을 설정할 때 리소스의 타입을 DataSource로 등록했습니다.

   즉, 원래 DataSource로 타입을 변환합니다.

 

- BoardDTO.java

package com.superman.www;

public class BoardDTO {
	private int no;
	private String title;
	private String name;
	private String password;
	private String email;
	private String contents;
	private String wtime;
	private int rcnt;
	public int getNo() {
		return no;
	}
	public void setNo(int no) {
		this.no = no;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getContents() {
		return contents;
	}
	public void setContents(String contents) {
		this.contents = contents;
	}
	public String getWtime() {
		return wtime;
	}
	public void setWtime(String wtime) {
		this.wtime = wtime;
	}
	public int getRcnt() {
		return rcnt;
	}
	public void setRcnt(int rcnt) {
		this.rcnt = rcnt;
	}	
}

 

728x90