<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dev_osj.log</title>
        <link>https://velog.io/</link>
        <description>컴퓨터공학과 + 실무 = 4 + N = 모르는거 ∞ ...</description>
        <lastBuildDate>Wed, 07 Jul 2021 02:13:49 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dev_osj.log</title>
            <url>https://images.velog.io/images/dev_osj/profile/a8ebf068-280f-4b30-afd7-aa8e1358c97f/스크린샷 2021-03-25 오전 12.03.39.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dev_osj.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dev_osj" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[장애] too many open files]]></title>
            <link>https://velog.io/@dev_osj/%EC%9E%A5%EC%95%A0-too-many-open-files</link>
            <guid>https://velog.io/@dev_osj/%EC%9E%A5%EC%95%A0-too-many-open-files</guid>
            <pubDate>Wed, 07 Jul 2021 02:13:49 GMT</pubDate>
            <description><![CDATA[<p>올해 2월 중에 겪었던 장애 내용에 대해 관련 내용을 정리하고자 합니다.
장애 상황은 다음과 같습니다.</p>
<p>현재 제가 맡은 업무 도메인의 경우 API 산출 결과 데이터를 MongoDB에 실시간을 적재하고 있습니다.</p>
<p>언제나 그렇듯 업무를 보던 중에 모니터링 시스템에서 MongoDB가 셧다운 되었다는 알람이 울리게 되었으며 DB 로그 확인 결과 &#39;too many open files&#39; 라는 에러가 찍혀있는 것을 확인하였습니다.</p>
<p>그래서 이번 포스팅은 too many open files라는 에러의 원인과 내용에 대해 정리하려고 합니다.</p>
<p>&#39;too many open files&#39;의 직접적인 원인은 프로세스가 OS에 요청할 수 있는 최대 open 가능한 파일 개수에 limit이 있으며 프로세스가 그 제한을 넘었기 때문에 발생한 것입니다.
이때, open 가능한 파일이란 것은 소켓(HTTP, JDBC 커넥션 등)을 포함한 파일 개수입니다.</p>
<p>프로세스의 limit의 경우 프로세스를 실행한 계정의 limit을 참고하여 설정되기 때문에 계정, 프로세스 limit 모두 확인이 필요합니다.</p>
<p>따라서 &#39;too many open files&#39; 문제의 경우 계정 limt과 프로세스에 대한 limit 두 가지를 보며 해결하도록 하겠습니다.</p>
<br>

<h2 id="계정-limit-확인-및-변경">계정 Limit 확인 및 변경</h2>
<p>우선 Limit의 경우 크게 Soft, Hard 두 가지 종류가 존재합니다.
특징은 아래와 같습니다.</p>
<p><img src="https://images.velog.io/images/dev_osj/post/f776311c-ff85-474f-812b-9f5b7634aa12/image.png" alt=""></p>
<p>이어서 프로세스가 실행되고 있는 계정의 NOFILE Limit을 확인하도록 하겠습니다.
아래 두 가지 명령어를 통해 확인 가능합니다.</p>
<ul>
<li>ulimit -Hn</li>
<li>ulimit -Sn</li>
</ul>
<p>soft, hard 모두 변경을 위해서는 root 계정에서 변경이 가능합니다.</p>
<p>/etc/security/limits.conf 파일에 아래 구문을 추가하도록 하겠습니다.</p>
<ul>
<li>root hard nofile 500000</li>
<li>root soft nofile 500000</li>
</ul>
<p>우선 예시로 500000으로 설정하였으며 구체적인 수치는 각 서버의 상황에 따라 설정해주시면 됩니다.</p>
<p>하지만 이때 주의할 사항이 있습니다.
새로 설정한 500000, 즉 limit 값의 경우 system 전체 limit 보다 작아야합니다.</p>
<p>system 전체 limit은 아래 명령어로 확인 가능합니다.</p>
<ul>
<li>cat /proc/sys/fs/file-max</li>
</ul>
<p>마지막으로 계정의 limit을 변경했다면 터미널을 통해 재 로그인 해주시면 변경이 완료됩니다.</p>
<br>

<h2 id="프로세스에-대한-limit-올리기">프로세스에 대한 limit 올리기</h2>
<p>프로세스 limit의 경우 변경한 뒤 재시작을 하면 되지만 실시간 서비스 환경에서 프로세스 재시작이 안되는 때가 있다고 가정하고 작성하도록 하겠습니다.</p>
<p>먼저 limit을 변경하기 전 해당 프로세스의 PID를 확인해야 하기 때문에 아래 명령어를 통해 PID를 확인합니다.</p>
<ul>
<li>ps -ef | grep [프로세스명]</li>
</ul>
<p>방금 확인한 PID를 기준으로 해당 프로세스의 limit을 확인하도록 하겠습니다.</p>
<ul>
<li>prlimit --nofile -output RESOURCE, SOFT, HARD --pid [PID 번호]</li>
</ul>
<p>마지막으로 limit을 변경하도록 하겠습니다.</p>
<ul>
<li>prlimit --nofile=500000 --pid=[PID 번호]</li>
</ul>
<p>위처럼 변경을 하면 프로세스 재시작없이 soft, hard limit을 변경 가능하게 됩니다.</p>
<h2 id="정리">정리</h2>
<p>OS 레벨에서의 설정을 만질일은 거의 없지만 해당 장애를 통해 새로운 것을 알게 되었습니다.
too many open files 이슈에 대한 해결방법을 정리해보았습니다.
계정의 limit을 확인한 다음 프로세스에 대한 limit을 변경하게 되면 정상적으로 외부요청을 받을 수 있는 것을 확인하실 수 있으실 수 있을 겁니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[트래픽 분산처리 기술]]></title>
            <link>https://velog.io/@dev_osj/%ED%8A%B8%EB%9E%98%ED%94%BD-%EB%B6%84%EC%82%B0%EC%B2%98%EB%A6%AC-%EA%B8%B0%EC%88%A0</link>
            <guid>https://velog.io/@dev_osj/%ED%8A%B8%EB%9E%98%ED%94%BD-%EB%B6%84%EC%82%B0%EC%B2%98%EB%A6%AC-%EA%B8%B0%EC%88%A0</guid>
            <pubDate>Thu, 13 May 2021 08:36:53 GMT</pubDate>
            <description><![CDATA[<p>원본 : <a href="https://d2.naver.com/helloworld/6070967">https://d2.naver.com/helloworld/6070967</a> </p>
<p>D2 네이버 기술블로그에 정리된 내용 중 일부를 별도로 뽑아 정리하고자 합니다.</p>
<br>

<h2 id="gcdn-global-cdn">GCDN (Global CDN)</h2>
<p>CSS와 JavaScript, 이미지와 같이 공통으로 호출되는 리소스는 한 번 업로드되면 잘 변하지 않습니다.
이런 리소스를 웹 서버에서 직접 제공하면 트래픽 부하가 가중됩니다.
예를 들어 100KB 용량의 이미지를 10만명이 조회하면 대략 10GB의 트래픽이 발생합니다.
그래서 공통적으로 호출되는 리소스의 부하 분산을 위해 GCDN을 사용합니다.
리소스를 GCDN으로 분산하면 트래픽을 상당히 절감할 수 있습니다.
또한, GCDN에서 지원하는 GSLB(Global Server LB) 기능은 접속한 IP 주소에서 가장 가까운 CDN 서버를 자동으로 선정해 연결하기 때문에 사용자가 빠른 서비스 속도를 체감할 수 있습니다.</p>
<br>

<h2 id="ssi-server-side-includes">SSI (Server Side Includes)</h2>
<p>SSI는 웹 서버(Apache, NGINX 등)에서 지원하는 서버사이드 스크립트 언어입니다.
서버에 있는 특정 파일을 읽어오거나 특정 쿠키 유무의 판별 등 간단한 기능을 실행하 수 있다.
이런 기능을 WAS에서만 실행할 수 있다고 생각하고 WAS에 요청을 보내는 경우가 많습니다.
하지만 SSI를 사용해 웹 서버에서 기능을 처리하면 WAS의 부담을 줄여 WAS의 성능에 여유를 줄 수 있게 되고, 웹 서버의 활용도도 높여 서버의 자원을 더 효율적으로 사용할 수 있습니다.
<img src="https://images.velog.io/images/dev_osj/post/f4139169-1ab9-4202-b565-3ced50d1e178/image.png" alt="">
(SSI 적용 예, <a href="https://d2.naver.com/helloworld/6070967">https://d2.naver.com/helloworld/6070967</a>)
<br></p>
<h2 id="apache-커스텀-모듈">Apache 커스텀 모듈</h2>
<blockquote>
<p>APR(Apache Portable Runtime)
APR은 프레임워크와 비슷하게 운영체제에 독립적으로 HTTP 기반 통신을 처리할 수 있도록 하는 라이브러리입니다.
메모리 할당, 메모리 폴링, 파일 입출력, 멀티스레드 관련 처리 등에 필요한 기능이 포함되어 있습니다. Apache HTTP 서버도 APR 기반으로 작성되어 있습니다.</p>
</blockquote>
<p>커스텀 모듈을 사용하면 SSI를 사용할 때와 마찬가지로 WAS에서 처리할 기능을 웹 서버에서 처리할 수 있어 WAS의 부담을 줄일 수 있습니다.
또한 SSI에서는 불가능한 고급 기능을 사용할 수 있어서 활용도가 높습니다. C언어 기반이라 실행 성능도 좋습니다.</p>
<p>대표적인 커스텀 기능은 아래와 같습니다.</p>
<ul>
<li>간단한 외부 시스템 연동 : 단순히 API를 호출하거나 결과값을 그대로 사용할 수 있을 때에는 WAS를 거치지 않고 커스텀 모듈을 통해 외부 시스템을 호출합니다.</li>
<li>WAS 연동 시 AJP(Apache JServ Protocol), 리버스 프록시(reverse proxy) 대신 커스텀 모듈 사용 : 모든 WAS가 다운되더라도 웹 서버만 정상이라면 서비스를 제공할 수 있게 커스텀 모듈을 사용해 WAS와 통신합니다.<ul>
<li>AJP나 HTTP를 사용해 WAS와 통신하면 WAS와 통신이 실패했을 때 오류로        처리됩니다.<ul>
<li>커스텀 모듈을 사용하면 WAS와 통신이 실패했을 때 동일한 IDC에 있는           다른 WAS와 통신을 시도합니다.</li>
<li>다른 WAS와 통신도 실패하면 오류로 처리하는 대신 특정한 경로에 있는         파일을 읽어서 반환합니다. 반환할 파일은 정적 파일을 배포하는 Data       Publisher가 특정 시간 주기에 따라 웹 서버에 생성해 놓은 파일입니다.<h2 id="서킷-브레이커">서킷 브레이커</h2>
서킷 브레이커는 외부 서비스의 장애로 인한 연쇄적 장애 전파를 막기 위해 자동으로 외부 서비스와 연결을 차단 및 복구하는 역할을 합니다.
서킷 브레이커를 사용하는 목적은 애플리케이션의 안정성과 장애 저항력을 높이는 데 있습니다. 분산 환경에서는 네트워크 일시 단절 또는 트래픽 폭증으로 인한 간헐적 시간 초과 등의 상황이 종종 발생합니다. 이로 인해 연쇄 장애가 발생할 가능성이 있습니다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<ol>
<li>트래픽 폭증으로 인해 API 서버 등 외부 서비스의 응답이 느려진다.</li>
<li>외부 서비스의 응답이 타임아웃 시간을 초과하면 데이터를 받아오기 위해 외부 서비스를 다시 호출한다.</li>
<li>이전에 들어온 트래픽을 다 처리하지 못한 상태에서 재시도 트래픽이 추가로 적체되어 외부 서비스에 장애가 발생한다.</li>
<li>외부 서비스의 장애로 인하여 데이터를 수신하지 못해 네이버 메인 서비스에도 장애가 발생한다.</li>
</ol>
<p>일시적인 경우라면 2, 3회 재시도로 정상 데이터를 수신할 수 있습니다.
하지만 장애 상황이라면 계속 재시도하는 것이 의미도 없을 뿐 아니라 외부 서비스의 장애 복구에 악영향을 미칠 수 있습니다.
이럴 때는 외부 서비스에서 데이터를 받아오는 것을 포기하고 미리 준비된 응답을 사용자에게 전달하는 것이 시스템 안정성 및 사용 편의성 측면에서 옳다고 할 수 있습니다.</p>
<p><img src="https://images.velog.io/images/dev_osj/post/a8df0c6f-f6cd-4771-9ebd-0337748db201/image.png" alt=""></p>
<p>(서킷 브레이커에서 서킷의 상태 변화, 출처 : <a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker">https://docs.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker</a>)</p>
<p>서킷은 아래와 같은 세 가지 상태를 가집니다. (회로이므로 닫힌 것이 정상, 열린 것이 비정상)</p>
<ul>
<li>Closed 상태 : 메소드가 정상적으로 작동해 서킷이 닫힌 상태</li>
<li>Open 상태 : 메소드에 문제가 생겨서 서킷이 열린 상태</li>
<li>Half-Open 상태 : Open 상태와 Closed 상태의 중간 상태, 메소드를 주기적으로 확인해 정상이라고 판단되면 Closed 상태로 상태를 전환하고, 정상이 아니라면 Open 상태를 유지</li>
</ul>
<p>서킷이 Closed 상태에서 잘 작동하고 있다가 오류가 발생하기 시작해서 일정 횟수에 도달하면 일단 서킷을 Open 상태로 전환합니다.
이때에는 메소드를 호출해도 실제로 메소드가 실행되는 대신 서킷 브레이커가 개입해 미리 지정된 응답(단순 실패를 반환할 수도 있고 특정한 값을 반환할 수 도 있음)을 반환합니다.</p>
<p>특정 주기 동안 Open 상태로 유지하고 있다가 서킷이 자체적으로 메소드가 정상으로 작동하는지 확인하는데, 이 상태가 Half-Open 상태입니다.
Half-Open 상태에서 멧도르르 몇 번 호출해서 정상 응답이 돌아온다면 서킷은 다시 Closed 상태로 전환되어 요청을 처리합니다. 몇 번 호출했는데 게속 실패가 반환된다면 서킷은 Open 상태로 유지합니다.</p>
<p>네이버 메인페이지에서 사용하는 서킷 브레이커 활용 예시</p>
<ul>
<li>정적 파일을 배포하는 Data Publisher를 통해 사전에 정상 응답 결과값을 주기적으로 저장</li>
<li>외부 시스템에 이상이 있다고 판단되면(open 상태) 서킷 브레이커가 개입해 바로 연결 시도를 포기하고 미리 준비해 둔 정상 응답 결과값을 사용해 HTML 화면을 렌더링</li>
<li>이후 계속 상태를 지켜보다(Half-Open 상태) 외부 시스템이 정상으로 돌아오면 서킷 브레이커가 다시 연결을 시도<h2 id="서비스-디스커버리">서비스 디스커버리</h2>
서비스 디스커버리는 동적으로 생성, 삭제되는 서버 인스턴스에 대한 IP 주소와 포트를 자동으로 찾아 설정할 수 있게 하는 기능입니다.
다음은 서비스 디스커버리를 사용하지 않는 서버군의 환경을 나타낸 그림입니다.
서버군 1과 서버군 2가 서로 다른 기능을 실행하는 서버군이라고 가정합니다.
<img src="https://images.velog.io/images/dev_osj/post/5fd4f07b-3df9-4d0a-a9be-7c473b75d3f3/image.png" alt="">
(서비스 디스커버리를 사용하지 않는 환경, 출처 : <a href="https://d2.naver.com/helloworld/6070967">https://d2.naver.com/helloworld/6070967</a>)</li>
</ul>
<p>서버군 2에서 10.0.0.5 서버가 신규로 생성됐지만, 서버군 1에서는 10.0.0.5 서버로 연결을 맺을 수 없다. 서버군 1이 최초에 실행 됐을 때에는 10.0.0.5 서버가 없었기 때문에 서버 목록에 10.0.0.5 서버가 없기 때문입니다. 서버 목록을 갱신하려면 서버군 1에 있ㄴ는 서버를 다시 실행해야 합니다.
하지만 롤링 리스타트를 한다고 해도 서비스를 운영하는 도중에 서버를 다시 실행하는 것은 매우 위험합니다.</p>
<p>아래는 서비스 디스커버리를 도입한 환경을 나타내는 그림입니다.
<img src="https://images.velog.io/images/dev_osj/post/7a8169b0-ff9d-4b5b-93ea-bc86f4965c2c/image.png" alt="">
(서비스 디스커버리를 사용하는 환경, 출처 : <a href="https://d2.naver.com/helloworld/6070967">https://d2.naver.com/helloworld/6070967</a>)</p>
<p>서비스 디스커버리 서버는 각 서버군의 서버 목록을 관리합니다. 서버는 시작할 때 자신의 정보(IP 주소, 포트)를 서비스 디스커버리 서버로 보내고, 서비스 디스커버리 서버는 이 정보를 받아 서버군의 서버 목록을 최신으로 갱신합니다.
각 서버군에 있는 서버는 주기적으로 서비스 디스커버리 서버로 목록을 요청해 서버 목록을 최신으로 유지합니다.
이러한 방법으로 서버는 재시작 등 외부의 개입이 없어도 자동으로 항상 최신 서버 목록을 확보할 수 있습니다.</p>
<p>예를 들어 10.0.0.5 서버가 추가되면 서버군 2의 서버 목록이 갱신되고 이 목록을 받아서 서버군 1의 서버가 자신의 서버 목록을 갱신하면 외부의 개입 없이 10.0.0.5 서버에 연결할 수 있게 됩니다.</p>
<p>서비스 디스커버리는 특히 클라우드 환경에서 빛을 발합니다.
기존 온프레미스 환경에서는 서버를 추가, 삭제하는 일이 많지 않았습니다.
서버를 추가, 삭제하는 경우에도 통제된 환경에서 주로 작업하므로 갱ㅅ니 문제가 크지 않습니다.
하지만 클라우드 환경에서는 수시로 서버가 추가, 삭제됩니다. (특히 오토 스케일링 등의 기능을 사용한다면 더욱 더)
이를 사람이 일일이 확인해 서버를 다시 실행하는 것은 불가능 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Design Pattern in Spring]]></title>
            <link>https://velog.io/@dev_osj/Design-Pattern-in-Spring</link>
            <guid>https://velog.io/@dev_osj/Design-Pattern-in-Spring</guid>
            <pubDate>Tue, 20 Apr 2021 09:22:25 GMT</pubDate>
            <description><![CDATA[<p>스프링 내에서 사용되어지는 디자인 패턴에 관한 정리가 잘되어 있는 문서를 확인하여 해당 내용을 해석 및 정리하는 포스팅입니다.</p>
<p>원본 : <a href="https://www.baeldung.com/spring-framework-design-patterns">https://www.baeldung.com/spring-framework-design-patterns</a></p>
<h2 id="singleton-pattern">Singleton Pattern</h2>
<p>싱글톤 패턴은 애플리케이션 당 객체의 인스턴스를 하나만 생성되도록 하는 메커니즘입니다.</p>
<h3 id="singleton-beans">Singleton Beans</h3>
<p>기본적으로, 싱글톤은 하나의 애플리케이션에 전역적으로 유니크지만, 스프링에서는 이러한 제약이 약간 풀어집니다.
대신에 스프링은 Spring IoC 컨테이너 당 하나의 객체는 싱글톤인 것으로 제한을 둡니다.
즉, 스프링에서는 application context 당 하나의 빈만 생성이 가능하다는 것입니다.
그러므로, 같은 클래스의 여러개의 객체가 하나의 애플리케이션에 존재하게 하려면 여러개의 컨테이너를 사용해야 합니다.</p>
<p><img src="https://images.velog.io/images/dev_osj/post/cf8f08f8-bee0-413f-9e57-5d520cd83fd7/image.png" alt=""></p>
<h3 id="autowired-singletons">Autowired Singletons</h3>
<p>예를 들어, 하나의 application context에서 두 개의 컨트롤러를 만들고 각각 동일한 타입의 빈을 주입한다고 가정하겠습니다.
첫번째로, Book 도메인 객체를 관리하는 BookRepository를 만들겠습니다.
두번째로, 도서관에서 책의 개수를 반환하는 BookRepository를 사용하는 LibraryController를 만들겠습니다.</p>
<pre><code class="language-java">@RestController
public class LibraryController {

    @Autowired
    private BookRepository repository;

    @GetMapping(&quot;/count&quot;)
    public Long findCount() {
        System.out.println(repository);
        return repository.count();
    }
}</code></pre>
<p>마지막으로, 책의 ID로 책을 찾는 것과 같이 Book에 관한 구체적인 행동에 관점을 둔 BookController를 만들겠습니다.</p>
<pre><code class="language-java">@RestController
public class BookController {

    @Autowired
    private BookRepository repository;

    @GetMapping(&quot;/book/{id}&quot;)
    public Book findById(@PathVariable long id) {
        System.out.println(repository);
        return repository.findById(id).get();
    }
}</code></pre>
<p>이후, 애플리케이션을 시작하고 /count와 /book/1에 GET 요청을 하겠습니다.
애플리케이션 결과는 BookRepository 객체가 동일한 객체ID를 가지고 있는 것을 확인할 수 있습니다.</p>
<blockquote>
<p>com.baeldung.spring.patterns.singleton.BookRepository@3ea9524f
com.baeldung.spring.patterns.singleton.BookRepository@3ea9524f</p>
</blockquote>
<p>LibraryController와 BookController에 있는 BookRepository 객체 ID는 동일한 것이고 스프링은 두 개의 컨트롤러에 동일한 빈을 주입했다는 것을 알 수 있습니다.</p>
<p>반대로 BookRepository 빈 인스턴스의 빈 스코프를 singleton에서 prototype으로 @Scope(ConfiguratableBeanFactory.SCOPE_PROTOTYPE)어노테이션을 황용하여 변경한다면 분리된 BookRepository 빈을 생성할 순 있습니다.</p>
<br>

<h2 id="factory-method-pattern">Factory Method Pattern</h2>
<p>팩토리 메서드 패턴은 원하는 객체를 생성하기 위한 추상 메서드가 있는 팩토리 클래스를 수반합니다.</p>
<p>종종, 특정 내용을 바탕으로 다른 객체를 만들어야 할 때가 있습니다.
예를 들어, 애플리케이션이 vehicle 객체를 만드는 것으로 가정하겠습니다.
자연적인 환경에서는 boats를 생성하지만, aerospace 환경에서는 airplanes을 생성해야 합니다.</p>
<p><img src="https://images.velog.io/images/dev_osj/post/45e3dfd4-1e1b-4b25-930d-08165ceae1fe/image.png" alt=""></p>
<p>이것을 해결하기 위해서 각각 원하는 객체를 생성하는 팩토리를 만들고 구체적인 팩토리 메소드로부터 원하는 객체를 반환해주면 됩니다.</p>
<h3 id="application-context">Application Context</h3>
<p>스프링에서는 이러한 기술을 DI Framework의 뿌리에서 사용합니다.
기본적으로, 스프링은 빈을 생성하는 빈 컨테이너를 팩토리로 취급합니다.
그러므로, 스프링은 BeanFactory 인터페이스를 빈 커테이너의 추상으로 정의합니다.</p>
<pre><code class="language-java">public interface BeanFactory {

    getBean(Class&lt;T&gt; requiredType);
    getBean(Class&lt;T&gt; requiredType, Object... args);
    getBean(String name);

    // ...
]</code></pre>
<p>각각의 getBean 메소드들은 빈의 타입과 이름과 같이 메소드에 제공되는 기준을 통해 매칭된 빈을 반환해주는 팩토리 메소드입니다.
이후, 앞서 소개한 바와 같이 스프링은 ApplicationContext 인터페이스로 BeanFactoy를 확장합니다.
스프링은 XML 파일 혹은 Java 어노테이션과 같은 외부 설정들을 기반으로 빈 컨테이너를 실행시킵니다.
AnnotationConfigApplicationContext와 같이 ApplicationContext 클래스를구현하여 사용합니다.
이후 BeanFactory 인터페이스로부터 상속 받은 다양한 팩토리 메소드들을 통해 빈을 생성합니다.</p>
<ol>
<li><p>간단한 애플리케이션 설정을 만든다.</p>
<pre><code class="language-java">@Configuration
@ComponentScan(basePackageClasses = ApplicationConfig.class)
public class ApplicationConfig {
}</code></pre>
</li>
<li><p>생성자를 받지 않는 간단한 Foo 클래스를 만든다.</p>
<pre><code class="language-java">@Component
public class Foo {
}</code></pre>
</li>
<li><p>한개의 생성자를 받는 다른 Bar 클래스를 만든다.</p>
<pre><code class="language-java">@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Bar {

 private String name;

 public Bar(String name) {
     this.name = name;
 }

 // Getter ...
}</code></pre>
</li>
<li><p>ApplicationContext를 구현한 AnnotationConfigApplicationContext 를 통해 빈들을 생성한다.</p>
<pre><code class="language-java">@Test
public void whenGetSimpleBean_thenReturnConstructedBean() {

 ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);

 Foo foo = context.getBean(Foo.class);

 assertNotNull(foo);
}
</code></pre>
</li>
</ol>
<p>@Test
public void whenGetPrototypeBean_thenReturnConstructedBean() {</p>
<pre><code>String expectedName = &quot;Some name&quot;;
ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);

Bar bar = context.getBean(Bar.class, expectedName);

assertNotNull(bar);
assertThat(bar.getName(), is(expectedName));</code></pre><p>}</p>
<pre><code>
getBean 팩토리 메소드를 사용하여, 클래스 타입만으로 설정한 빈을 생성할 수 있다.

### 외부 설정
이 패턴은 외부 설정을 기반으로 애플리케이션 동작 자체를 변경시킬 수 있기 때문에 여러 목적으로 쓰입니다.
만약 애플리케이션에서 주입(autowired)된 객체의 실행을 변경하길 원한다면 사용할 ApplicationContext의 실행만 조정하면 됩니다.
![](https://images.velog.io/images/dev_osj/post/af461735-d263-4c94-bd40-03e41adcbc44/image.png)

예를들어, ClassPathXmlApplicationContext과 같이 AnnotationConfigApplicationContext에서 XML 기반의 설정 클래스로 변경 가능합니다.

```java
@Test 
public void givenXmlConfiguration_whenGetPrototypeBean_thenReturnConstructedBean() { 

    String expectedName = &quot;Some name&quot;;
    ApplicationContext context = new ClassPathXmlApplicationContext(&quot;context.xml&quot;);

    // Same test as before ...
}</code></pre><br>

<h2 id="proxy-pattern">Proxy Pattern</h2>
<p>코드에서 프록시 패턴은 하나의 객체(프록시)가 다른 객체(특정 목적 혹은 서비스)를 접근제어가 가능하도록 하는 기술입니다.
<img src="https://images.velog.io/images/dev_osj/post/879e0217-fd96-47b8-ab1b-5e10fd9d3600/image.png" alt=""></p>
<h3 id="transactions">Transactions</h3>
<p>프록시를 생성하기 위해 Subject와 Subject에 대한 참조에 대한 동일한 인터페이스를 사용한 객체를 생성합니다.
다음으로, 적절한 서브젝에서 프록시를 사용하면 됩니다.
스프링에서 빈들은 특정 빈들에 대한 접근을 제어하기 위해 프록시됩니다.</p>
<pre><code class="language-java">@Service
public class BookManager {

    @Autowired
    private BookRepository repository;

    @Transactional
    public Book create(String author) {
        System.out.println(repository.getClass().getName());
        return repository.create(author);
    }
}</code></pre>
<p>BookManager 클래스에서, create메소드가 @Transactional 어노테이션이 붙은 것을 확인할 수 있습니다.
해당 어노테이션은 스프링이 create 메소드를 원자적으로 실행하도록 안내합니다.
프록시가 없다면 스프링은 BookRepository 빈에 대한 접근 제어나 트랜잭션 동일성에 대한 확신을 할 수 없게 됩니다.</p>
<h3 id="cglib-proxies">CGLib Proxies</h3>
<p>대신에, 스프링은 BookRepository 빈을 감싸고 있는 프록시를 생성하고 원자적으로 create 메소드를 실행하도록 지시합니다.
BookManager#create 메소드를 실행할 때 아래와 같은 출력을 볼 수 있습니다.</p>
<blockquote>
<p>com.baeldung.patterns.proxy.BookRepository$$EnhancerBySpringCGLIB$$3dc2b55c</p>
</blockquote>
<p>전형적으로, 표준 BookRepository 객체 ID를 찾게 되지만, EnhancerBySpringCGLIB 객체 ID를 바라보고 있습니다.
스프링은 BookRepository 객체를 EnhancerBySpringCGLIB 안에 감싸져 있도록 합니다.
<img src="https://images.velog.io/images/dev_osj/post/c9264081-cfff-47b3-ad76-d83a6d5a850d/image.png" alt=""></p>
<p>전형적으로 스프링은 두 가지 종류의 프록시를 사용합니다.</p>
<ol>
<li>CGLib Proxies : 클래스들을 프록시할 때 사용</li>
<li>JDK Dynamic Proxies : 인터페이스를 프록시할 때 사용</li>
</ol>
<p>트랜잭션을 사용하여 위와 같은 프록시들을 노출시킬 때, 스프링은 빈들에 대한 접근을 제어하기 위한 어떤 시나리오에서도 프록시들을 사용할 것 입니다.
<br></p>
<h2 id="template-method-pattern">Template Method Pattern</h2>
<p>다양한 프레임워크에서 코드의 중요한 부분은 boilerplate(<a href="https://en.wikipedia.org/wiki/Boilerplate_code">https://en.wikipedia.org/wiki/Boilerplate_code</a>) 코드입니다.
예를 들어, 데이터베이스에서 쿼리를 실행할 때, 동일한 형태의 단계가 완료되어야만 합니다.</p>
<ol>
<li>커넥션 발생</li>
<li>쿼리 실행</li>
<li>초기화 수행</li>
<li>커넥션 종료</li>
</ol>
<p>이러한 단계들이 template method pattern의 이상적인 시나리오 입니다.</p>
<h3 id="templates--callbacks">Templates &amp; Callbacks</h3>
<p>template method pattern은 어떠한 행동을 위한 단계들을 정의하고, boilerplate 단계들을 실행하고, 추상화된 커스텀 단계들을 종료할 때 사용하는 기술입니다.
서브클래스들은 이러한 추상화 클래스들을 실행할 수 있고 놓친 단계들을 위한 구체적인 실행을 제공할 수 있습니다.
데이터베이스 쿼리의 케이스들에서 템플릿을 생성할 수 있습니다.</p>
<pre><code class="language-java">public abstract DatabaseQuery {

    public void execute() {
        Connection connection = createConnection();
        executeQuery(connection);
        closeConnection(connection);
    } 

    protected Connection createConnection() {
        // Connect to database...
    }

    protected void closeConnection(Connection connection) {
        // Close connection...
    }

    protected abstract void executeQuery(Connection connection);
}</code></pre>
<p>다른 대안으로, 콜백 메소드를 제공하여 놓친 단계를 제공할 수 있습니다.
콜백 메소드는 서브젝트가 어떤 액션을 끝내기를 원하는 클라이언트에게 신호를 줄 수 있도록 하는 메소드입니다.
이러한 케이스들에서, 서브젝트는 mapping result와 같은 액션을 취할 수 있도록 콜백을 사용할 수 있습니다.
<img src="https://images.velog.io/images/dev_osj/post/b61d568b-a0a8-43f0-a023-57235abad183/image.png" alt=""></p>
<p>예를 들어, executeQuery 메소드를 가지는 대신, execute 메소드를 쿼리 문자열과 결과를 다룰 콜백 메소드를 제공해야 합니다.</p>
<p>첫번째로, Results 객체와 T 타입의 객체를 받을 수 있는 콜백 메소드를 생성합니다.</p>
<pre><code class="language-java">public interface ResultsMapper&lt;T&gt; {
    public T map(Results results);
}</code></pre>
<p>다음으로, 콜백을 사용할 수 있도록 DatabaseQuery 클래스를 변경합니다.</p>
<pre><code class="language-java">public abstract DatabaseQuery {

    public &lt;T&gt; T execute(String query, ResultsMapper&lt;T&gt; mapper) {
        Connection connection = createConnection();
        Results results = executeQuery(connection, query);
        closeConnection(connection);
        return mapper.map(results);
    ]

    protected Results executeQuery(Connection connection, String query) {
        // Perform query...
    }
}</code></pre>
<p>이러한 콜백 매카니즘은 스프링에서 JdbcTemplate 클래스를 사용할 때의 접근 방법입니다.</p>
<h3 id="jdbctemplate">JdbcTemplate</h3>
<p>JdbcTemplate 클래스는 쿼리 String과 ResultSetExtractor 객체를 받을 query 메소드를 제공합니다.</p>
<pre><code class="language-java">public class JdbcTemplate {

    public &lt;T&gt; T query(final String sql, final ResultSetExtractor&lt;T&gt; rse) throws DataAccessException {
        // Execute query...
    }

    // Other methods...
}</code></pre>
<p>ResultSetExtractor는 T타입의 도메인 객체에서 ResultSet 객체(쿼리의 결과를 보여주는)로 변환해야 합니다.</p>
<pre><code class="language-java">@FunctionalInterface
public interface ResultSetExtractor&lt;T&gt; {
    T extractData(ResultSet rs) throws SQLException, DataAccessException;
}</code></pre>
<p>스프링은 더 나아가 조금 더 구체적인 콜백 인터페이스 생성에 의해 boilerplate코드를 감소시킬 수 있습니다.
예를 들어, RowMapper 인터페이스는 T 타입의 도메인 객체에서 한줄의 SQL 데이터로 변환할 때 사용합니다.</p>
<pre><code class="language-java">@FunctionalInterface
public interface RowMapper&lt;T&gt; {
    T mapRow(ResultSet rs, int rowNum) throws SQLException;
}</code></pre>
<p>RowMapper 인터페이스를 예정된 ResultSetExtractor를 실행하기 위해, 스프링은 RowMapperResultSetExtractor 클래스를 생성합니다.</p>
<pre><code class="language-java">public class JdbcTemplate {

    public &lt;T&gt; List&lt;T&gt; query(String sql, RowMapper&lt;T&gt; rowMapper) throws DataAccessException {
        return result(query(sql, new RowMapperResultSetExtractor&lt;&gt;(rowMapper)));
    }

    // Other methods...
}</code></pre>
<p>row들에 대한 순회 반복을 포함하여 전체 ResultSet 객체를 변경하는 로직을 제공하는 대신, 어떻게 하나의 row를 변경하기 위한 로직을 제공할 수 있습니다. </p>
<pre><code class="language-java">public class BookRowMapper implements RowMapper&lt;Book&gt; {

    @Override
    public Book mapRow(ResultSet rs, int rowNum) throws SQLException {

        Book book = new Book();

        book.setId(rs.getLong(&quot;id&quot;));
        book.setTitle(rs.getString(&quot;title&quot;));
        book.setAuthor(rs.getString(&quot;author&quot;));

        return book;
    }
}</code></pre>
<p>이런 컨버터와 함께 JdbcTemplate을 사용하여 데이터베이스에 쿼리를 전송할 수 있고 각각의 결과 row에 매핑할 수 있습니다.</p>
<pre><code class="language-java">JdbcTemplate template = // create template...
template.query(&quot;SELECT * FROM books&quot;, new BookRowMapper());</code></pre>
<p>JDBC 데이터베이스 관리와 별개로 스프링은 아래와 같은 것에 탬플릿을 사용합니다.</p>
<ol>
<li>Java Message Service (JMS)</li>
<li>JAVA Persistence API (JPA)</li>
<li>Hibernate (현재 deprecated됨)</li>
<li>Transactions</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Junit @Transactional]]></title>
            <link>https://velog.io/@dev_osj/Junit-Transactional</link>
            <guid>https://velog.io/@dev_osj/Junit-Transactional</guid>
            <pubDate>Mon, 19 Apr 2021 04:44:08 GMT</pubDate>
            <description><![CDATA[<p>Junit을 사용하여 jpa 데이터 검증을 시도할 때 @Transactional을 사용하곤 합니다.
<br></p>
<pre><code class="language-java">@Test
@Transactional
public void test(){}</code></pre>
<p>다만, 위와 같이 @Transactional을 선언할 경우 테스트를 실행할 때 마다 트랜잭션을 시작하고, 테스트가 완료되면 트랜잭션을 강제 롤백합니다.
이유는 아무래도 테스트 코드 내에서 코드 검증인 만큼 실제 DB의 값이 테스트 환경으로 인해 변경되지 않도록 하기 위해서 강제 롤백을 시키는 것으로 생각됩니다.
<br></p>
<pre><code class="language-java">@Test
@Transactional
@Rollback(false)
public void test(){}</code></pre>
<p>만약 롤백을 원하시지 않는다면 @Rollback(false) 해당 어노테이션을 추가적으로 작성해주시면 junit 환경에서도 강제 롤백되지 않도록 설정 가능합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[EHCache (3. 실제 사용)]]></title>
            <link>https://velog.io/@dev_osj/EHCache-3.-%EC%8B%A4%EC%A0%9C-%EC%82%AC%EC%9A%A9</link>
            <guid>https://velog.io/@dev_osj/EHCache-3.-%EC%8B%A4%EC%A0%9C-%EC%82%AC%EC%9A%A9</guid>
            <pubDate>Thu, 15 Apr 2021 05:24:28 GMT</pubDate>
            <description><![CDATA[<p>앞선 포스팅에서 EHCache 사용을 위한 셋팅에 대해 작성하였습니다.
이번 포스팅은 실제 캐시를 활용하여 코드를 작성해보겠습니다.
예제는 클라이언트로부터 요청이 발생하면 아주 간단한 값을 반환하는 API를 작성해보도록 하겠습니다.
<br></p>
<h3 id="cachecontroller">CacheController</h3>
<pre><code class="language-java">@RestController
@RequiredArgsConstructor
public class CacheController {
    private final CacheService cacheService;

    @GetMapping(&quot;/cache/test&quot;)
    public ResponseEntity&lt;String&gt; getCacheData(CacheDto cacheDto){
        return new ResponseEntity&lt;&gt;(cacheService.isUsingCacheInfo(cacheDto.getId()), HttpStatus.OK);
    }

    @Getter @Setter
    class CacheDto{
        private Integer id;
    }
}
</code></pre>
<br>


<h3 id="cacheentity">CacheEntity</h3>
<pre><code class="language-java">@Entity
@Getter
public class Cache {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer cacheId;
    private String using;
}</code></pre>
<br>

<h3 id="cacheservice">CacheService</h3>
<pre><code class="language-java">@Service
@RequiredArgsConstructor
public class CacheService {
    private final CacheRepository cacheRepository;

    @Cacheable(value = &quot;cacheInfo&quot;, key=&quot;#id&quot;)
    @Transactional(readOnly = true)
    public String isUsingCacheInfo(Integer id){
        Cache cacheInfo = cacheRepository.findByCacheId(id);
        if(cacheInfo.getUsing().equals(&quot;yes&quot;)){
            return &quot;Yes&quot;;
        }
        return &quot;No&quot;;
    }
}</code></pre>
<br>

<h3 id="cacherepository">CacheRepository</h3>
<pre><code class="language-java">public interface CacheRepository extends JpaRepository&lt;Cache, Integer&gt; {
    Cache findByCacheId(Integer cacheId);
}</code></pre>
<br>

<p>위 예제에서 자세히 봐야할 부분은 CacheService의 isUsingCacheInfo 메소드입니다.
isUsingCacheInfo 메소드에 동일한 파라미터(키) 값을 가진 호출이 발생한다면 데이터베이스에 접근하지 않고 EHCache의 지정한 저장소(heap, off-heap, Disk)에서 캐시 데이터를 읽은 뒤 반환하게 됩니다.</p>
<p>만약, 캐시 저장소를 heap 영역으로 지정하였다면 캐시 처리한 데이터의 참조가 어느정도 발생하는지 확인하여 캐시 데이터를 얼마나 유지시킬지 등을 한번 고민해보시는 것을 추천드립니다.
자주 참조되지 않는 데이터를 힙 영역에 지속적으로 유지시키다보면 아무래도 가비지 컬렉터 대상이되므로 GC 횟수가 오히려 증가하게 되는 불상사가 발생할 수 있기 때문입니다.</p>
<p>이번 프로젝트를 통해 EHCache를 사용하게 되면서 확실히 성능상의 효과를 보았으며 혹여나 동일 데이터 반복 조회를 하는 부분에 성능개선을 하고 싶으신 분들이 있으시다면 한번 쯤 사용해보시는 것도 괜찮을 것으로 생각됩니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[EHCache (2. 사용환경 셋팅)]]></title>
            <link>https://velog.io/@dev_osj/EHCache-2.-%EA%B5%AC%ED%98%84%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@dev_osj/EHCache-2.-%EA%B5%AC%ED%98%84%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Thu, 15 Apr 2021 00:47:26 GMT</pubDate>
            <description><![CDATA[<p>이번 포스팅은 EHCache 사용설정 및 구현 방법에 대해 작성하도록 하겠습니다.
환경은 SpringBoot + maven + EHcache 3 버전 입니다.
특히 EHCache3버전의 경우 2 버전 대와 달리 JSR-107 호환성을 제공하기 때문에 표준을 기반으로 개발되었습니다.</p>
<br>

<h3 id="maven-dependency">Maven dependency</h3>
<p>EHCache 사용을 위해 아래 dependency 등록</p>
<pre><code>&lt;dependency&gt;
  &lt;groupId&gt;org.ehcache&lt;/groupId&gt;
  &lt;artifactId&gt;ehcache&lt;/artifactId&gt;
  &lt;version&gt;3.6.2&lt;/version&gt; 
&lt;/dependency&gt;</code></pre><br>


<p>JSR-107 API 사용을 위해 아래 dependency 등록</p>
<pre><code>&lt;dependency&gt;
  &lt;groupId&gt;javax.cache&lt;/groupId&gt;
  &lt;artifactId&gt;cache-api&lt;/artifactId&gt;
  &lt;version&gt;1.1.0&lt;/version&gt;
&lt;/dependency&gt;</code></pre><br>

<p>스프링 부트를 사용하므로 아래 dependency를 등록</p>
<pre><code>&lt;dependency&gt;
  &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
  &lt;artifactId&gt;spring-boot-starter-cache&lt;/artifactId&gt;
  &lt;version&gt;2.1.1.RELEASE&lt;/version&gt; 
&lt;/dependency&gt;</code></pre><p>특히 위 spring-boot-starter-cache의 경우 CacheManager 등 스프링에서 지원하는 캐시 관련 bean들을 자동 생성 해줍니다.
또한, 버전의 경우 각자 스프링부트 프로젝트 버전에 맞추어 설정해주시면 됩니다.</p>
<br>

<h3 id="ehcachexml">ehcache.xml</h3>
<p>위와 같이 Maven dependency 설정이 완료되었다면 EHCache에 대한 설정을 해주어야 합니다.
설정의 경우 xml 파일 혹은 스프링의 CacheManger, Cache를 사용하여 코드로 설정해주는 방법이 존재합니다.
저의 경우 xml 파일을 사용하는 설정하는 것이 조금 더 편리함을 느껴 xml 기준으로 진행하도록 하겠습니다.</p>
<pre><code>&lt;config
    xmlns:xsi=&#39;http://www.w3.org/2001/XMLSchema-instance&#39;
    xmlns=&#39;http://www.ehcache.org/v3&#39;
    xsi:schemaLocation=&quot;http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core.xsd&quot;&gt;

(1) &lt;cache alias=&quot;foo&quot;&gt; 
(2)    &lt;key-type&gt;java.lang.String&lt;/key-type&gt; 
(2)    &lt;value-type&gt;java.lang.String&lt;/value-type&gt; 
    &lt;resources&gt;
(3) &lt;heap unit=&quot;entries&quot;&gt;20&lt;/heap&gt; 
(4)      &lt;offheap unit=&quot;MB&quot;&gt;10&lt;/offheap&gt; 
    &lt;/resources&gt;
  &lt;/cache&gt;

(5) &lt;cache-template name=&quot;myDefaults&quot;&gt; 
      &lt;key-type&gt;java.lang.Long&lt;/key-type&gt;
      &lt;value-type&gt;java.lang.String&lt;/value-type&gt;
      &lt;heap unit=&quot;entries&quot;&gt;200&lt;/heap&gt;
(6)   &lt;expiry&gt; 
            &lt;ttl unit=&quot;minutes&quot;&gt;10&lt;/ttl&gt;
      &lt;/expiry&gt;
    &lt;/cache-template&gt;

(7) &lt;cache alias=&quot;bar&quot; uses-template=&quot;myDefaults&quot;&gt; 
      &lt;key-type&gt;java.lang.Number&lt;/key-type&gt;
    &lt;/cache&gt;

(8) &lt;cache alias=&quot;simpleCache&quot; uses-template=&quot;myDefaults&quot; /&gt; 

&lt;/config&gt;</code></pre><br>

<p>(1) 캐시 이름
(2) foo 캐시의 key와 value의 타입이며 default는 java.lang.Object
(3) foo 캐시는 총 20개의 항목을 heap 영역에 저장
(4) off-heap은 heap 메모리 영역의 외부에 존재하는 영역으로 GC 대상이 되는 공간이 아닙니다. foo의 경우 10MB 사용하는 것으로 설정
(5) cache-template의 경우 cache 태그 내 선언할 정보들의 공통 설정
(6) 캐시 데이터 지속 시간 설정
(7) bar 캐시를 만들었으며 uses-templete을 활용하여 cache-template 설정을 사용하였고 key-type의 경우 cache-template의 key-type에 오버라이딩 가능
(8) simpleCache를 만들었으며 cache-template 설정 사용
<br></p>
<p>작성한 ehcache.xml 파일을 Spring이 알 수 있도록 properties에 아래 문구 추가</p>
<blockquote>
<p>spring.cache.jcache.config=classpath:ehcache.xml</p>
</blockquote>
<p>이후 SpringBoot의 Main 클래스에 @EnableCaching 어노터이션을 붙여주면 EHCache를 사용하기 위한 설정은 끝이 납니다.</p>
<br>

<p>참고</p>
<ul>
<li><a href="https://www.ehcache.org/documentation/3.8/getting-started.html">https://www.ehcache.org/documentation/3.8/getting-started.html</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[MariaDB] 동적쿼리 (4. 정리)
]]></title>
            <link>https://velog.io/@dev_osj/MariaDB-%EB%8F%99%EC%A0%81%EC%BF%BC%EB%A6%AC-4.-%EA%B7%B8-%EC%99%B8</link>
            <guid>https://velog.io/@dev_osj/MariaDB-%EB%8F%99%EC%A0%81%EC%BF%BC%EB%A6%AC-4.-%EA%B7%B8-%EC%99%B8</guid>
            <pubDate>Wed, 07 Apr 2021 09:12:07 GMT</pubDate>
            <description><![CDATA[<h3 id="왜-앞선-예시에서-동적쿼리를-사용하였나">왜 앞선 예시에서 동적쿼리를 사용하였나?</h3>
<p>예시의 특징이 테이블명이 변경되어야 한다는 것입니다.
개발자가 직접 수작업으로 변경하고 테이블을 생성해줄 수 있지만 이건....누가봐도 잘못된 방법인 것 같습니다.
그 외로는 java 기준으로 JDBC 혹은 Mybatis의 Preparedstatement 기능을 사용하면 가능하겠지만 이는 별도의 테이블 관리 스케줄링 프로젝트를 별도 운영해야 한다는 단점이 있습니다.
물론 이런 동작을 담당하는 프로젝트가 있다면 그 방향에 맞추어 운영을 하는게 맞지 않을 까 싶습니다.
<br></p>
<h3 id="쿼리-작성-이후-작업">쿼리 작성 이후 작업?</h3>
<p>쿼리를 작성하고 이후 작업은 여러가지가 있을 수 있다고 생각합니다.
우선 이번 포스팅에서의 예제를 사용해야 한다면 스케줄링 프로젝트처럼 일정 주기를 설정한 뒤에 쿼리문을 실행시켜야 합니다.
이런 경우에는 프로시져를 사용해도 되고 프로시져를 사용할 여건이 되지 않는다면 SQL파일을 별도 생성해서 리눅스의 Crontab 기능을 사용해도 동작 가능합니다.
필요한 상황에 맞추어 사용하시면 됩니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[MariaDB] 동적쿼리 (3. 쿼리작성)
]]></title>
            <link>https://velog.io/@dev_osj/MariaDB-%EB%8F%99%EC%A0%81%EC%BF%BC%EB%A6%AC-2.-%EC%BF%BC%EB%A6%AC%EC%9E%91%EC%84%B1</link>
            <guid>https://velog.io/@dev_osj/MariaDB-%EB%8F%99%EC%A0%81%EC%BF%BC%EB%A6%AC-2.-%EC%BF%BC%EB%A6%AC%EC%9E%91%EC%84%B1</guid>
            <pubDate>Wed, 07 Apr 2021 09:11:32 GMT</pubDate>
            <description><![CDATA[<p>그럼 이어서 동적 쿼리를 작성해보겠습니다.</p>
<pre><code class="language-sql">#######################   1. 6일전 테이블 삭제  ##############################################
(1) SET @deletename = CONCAT(&#39;DROP TABLE IF EXISTS `&#39;,&#39;LOG_DATA_&#39;,DATE_FORMAT(DATE_ADD(NOW(), INTERVAL -5 DAY), &#39;%Y%m%d&#39;), &#39;`&#39;);
(2) PREPARE STMT1 FROM @deletename;
(3) EXECUTE STMT1;
(4) DEALLOCATE PREPARE STMT1;




#######################   2. 테이블명 변경       ###############################################
(5) SET @tablename = &#39;LOG_DATA&#39;;
(6) SET @query = CONCAT(&#39;RENAME TABLE `&#39;, @tablename, &#39;` TO `&#39;, @tablename,&#39;_&#39;,DATE_FORMAT(DATE_ADD(NOW(), INTERVAL -1 DAY),&#39;%Y%m%d&#39;));
(7) PREPARE STMT2 FROM @query;
(8) EXECUTE STMT2;
(9) DEALLOCATE PREPARE STMT2;



#######################   3. 당일 적재될 테이블 생성  ###########################################
(10) CREATE TABLE `LOG_DATA` (
        `LOG_NO` INT(11) NOT NULL AUTO_INCREMENT,
        `LOG_CONTENT` LONGTEXT NULL COLLATE &#39;utf8_bin&#39;,
        `ADD_DATE` DATETIME NULL DEFAULT NULL,
        PRIMARY KEY (`LOG_NO`)
)
(11) COLLATE=&#39;utf8_general_ci&#39;
(12) ENGINE=INNODB;

</code></pre>
<br>

<h3 id="쿼리-분석">쿼리 분석</h3>
<h4 id="1">(1)</h4>
<blockquote>
<p>SET : 시스템 변수 설정하는 구문으로 deletename이라는 변수명에 CONCAT을 사용하여 &#39;기본 테이블명 + (현재날짜-5)&#39; 쿼리문을 할당합니다.</p>
</blockquote>
<br>

<h4 id="2">(2)</h4>
<blockquote>
<p>@deletename을 지칭하는 쿼리명을 STMT1로 지정하여 PREPARE합니다.
<br></p>
</blockquote>
<h4 id="3">(3)</h4>
<blockquote>
<p>쿼리를 실행합니다.
<br></p>
</blockquote>
<h4 id="4">(4)</h4>
<blockquote>
<p>쿼리명을 해제합니다.</p>
</blockquote>
<h4 id="5--9">(5) ~ (9)</h4>
<blockquote>
<p>(1) ~ (4)와 동일합니다.
다른점은 (6)에서 RENAME 문을 사용하여 테이블명을 수정합니다.
<br></p>
</blockquote>
<h4 id="10">(10)</h4>
<blockquote>
<p>LOG_DATA라는 테이블을 생성합니다.
<br></p>
</blockquote>
<h4 id="11">(11)</h4>
<blockquote>
<p>문자 인코딩 유형을 설정합니다.
<br></p>
</blockquote>
<h4 id="12">(12)</h4>
<blockquote>
<p>스토리지 엔진은 INNODB로 설정합니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[MariaDB] 동적쿼리 (2. 개념)]]></title>
            <link>https://velog.io/@dev_osj/MariaDB-%EB%8F%99%EC%A0%81%EC%BF%BC%EB%A6%AC-%ED%99%9C%EC%9A%A9-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4</link>
            <guid>https://velog.io/@dev_osj/MariaDB-%EB%8F%99%EC%A0%81%EC%BF%BC%EB%A6%AC-%ED%99%9C%EC%9A%A9-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4</guid>
            <pubDate>Wed, 07 Apr 2021 08:40:43 GMT</pubDate>
            <description><![CDATA[<p>이번 장에선 동적쿼리의 각 구문별 간단한 내용을 정리하도록 하겠습니다.</p>
<h3 id="prepare-구문">PREPARE 구문</h3>
<p>PREPARE 구문은 명령문을 준비하고 이후에 명령문 대신 사용될 이름을 할당합니다.
명령문 이름은 대소문자 구분하지 않습니다.
이름의 경우 하나의 SQL 구문만 할당해서 사용하여야 합니다.
SQL 구문 내부에 &quot;?&quot;와 같은 기호를 사용한다면 이후에 쿼리가 실행될 때 파라미터 값과 바운딩되도록 가리키는 파라미터 마커로 사용합니다.
prepared statement의 범위는 작성된 세션까지 입니다.
만약에 동일한 이름의 prepared statement가 존재하면 새로운 SQL구문이 준비되기 전에 할당해제됩니다. 즉, 새로운 SQL 구문이 에러가 포함되어 있고 실행 준비되지 못하면 에러는 반환되고 주어진 이름의 SQL 구문이 존재하지 않게 됩니다.</p>
<h3 id="execute-구문">EXECUTE 구문</h3>
<p>PREPARE 구문을 통해 SQL 명령문을 준비한 후 prepared statement의 이름을 참조하는 EXECUTE 구문을 통해 실행합니다.
만약 prepared statement가 파라미터 마커를 포함하고 있다면 USING 절을 사용하여 파라미터에 값이 바운딩되도록 선언한 사용자 변수 리스트를 작성해야 합니다.
파라미터 값의 경우 오직 사용자 변수에 의해서만 제공될 수 있으며, USING 절은 선언된 SQL에 있는 파라미터 마커의 수 많큼 정확하게 변수이름을 지정해야 합니다.
만약 SQL구문이 PREPARE되지 못했다면 아래와 비슷한 에러가 발생되게 됩니다.</p>
<blockquote>
<p>ERROR 1243 (HY000): Unknown prepared statement handler (stmt_name) given to EXECUTE</p>
</blockquote>
<br>

<h3 id="deallocatedrop-prepare">DEALLOCATE/DROP PREPARE</h3>
<p>PREPARE로 생성되어 prepared statement을 할당 해제하려면 SQL 명령문 이름을 참조하는 DEALLOCATE PREPARE를 사용하시면 됩니다.
prepared statement는 새 PREPARE 명령이 발생될 때 내재적으로 할당 해제됩니다. 이런 경우에는 DEALLOCATE를 사용할 필요가 없습니다.</p>
<p>prepared statement가 자원 해제(deallocate)되었을 때 실행을 시도하면 아래와 같은 에러가 발생하게 됩니다.</p>
<blockquote>
<p>ERROR 1243 (HY000): Unknown prepared statement handler (stmt_name) given to EXECUTE</p>
</blockquote>
<p>지정된 명령문이 PREPARE되지 않은 경우 아래와 같은 에러가 발생할 수 있습니다.</p>
<blockquote>
<p>ERROR 1243 (HY000): Unknown prepared statement handler (stmt_name) given to DEALLOCATE PREPARE</p>
</blockquote>
<br>

<h3 id="execute-immediate">EXECUTE IMMEDIATE</h3>
<p>EXECUTE IMMEDIATE는 동적 쿼리문을 실행하여 성능 오버헤드를 줄일 수 있습니다.</p>
<p>예를들어 아래와 같습니다.</p>
<ol>
<li>EXECUTE IMMEDIATE &#39;SELECT 1&#39;</li>
</ol>
<p>또는</p>
<ol start="2">
<li>prepare stmt from &#39;select 1&quot;;
execute stmt;
deallocate prepare stmt;</li>
</ol>
<p>위의 1과 2는 서로 동일한 의미입니다.</p>
<p>EXECUTE IMMEDIATE는 준비된 소스와 파라미터를 가진 복잡한 표현식도 지원합니다.</p>
<blockquote>
<p>EXECUTE IMMEDIATE CONCAT(&#39;SELECT COUNT(*) FROM &#39;, &#39;t1&#39;, &#39; WHERE a=?&#39;) USING 5+5;</p>
</blockquote>
<p>하지만 subselect문이거나 저장된 함수 호출은 지원하지 않습니다.</p>
<p>아래 예제는 오류를 반환 합니다.</p>
<pre><code class="language-sql">CREATE OR REPLACE FUNCTION f1() RETURNS VARCHAR(64) RETURN &#39;SELECT * FROM t1&#39;;
EXECUTE IMMEDIATE f1();

ERROR 1970 (42000): EXECUTE IMMEDIATE does not support subqueries or stored functions

EXECUTE IMMEDIATE (SELECT &#39;SELECT * FROM t1&#39;);

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that 
  corresponds to your MariaDB server version for the right syntax to use near 
  &#39;SELECT &#39;SELECT * FROM t1&#39;)&#39; at line 1

CREATE OR REPLACE FUNCTION f1() RETURNS INT RETURN 10;
EXECUTE IMMEDIATE &#39;SELECT * FROM t1 WHERE a=?&#39; USING f1();

ERROR 1970 (42000): EXECUTE..USING does not support subqueries or stored functions

EXECUTE IMMEDIATE &#39;SELECT * FROM t1 WHERE a=?&#39; USING (SELECT 10);

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that 
  corresponds to your MariaDB server version for the right syntax to use near 
  &#39;SELECT 10)&#39; at line 1</code></pre>
<p>해결 방법으로는 사용자 또는 SP 변수를 사용하시면 됩니다.</p>
<pre><code class="language-sql">CREATE OR REPLACE FUNCTION f1() RETURNS VARCHAR(64) RETURN &#39;SELECT * FROM t1&#39;;
SET @stmt=f1();
EXECUTE IMMEDIATE @stmt;

SET @stmt=(SELECT &#39;SELECT 1&#39;);
EXECUTE IMMEDIATE @stmt;

CREATE OR REPLACE FUNCTION f1() RETURNS INT RETURN 10;
SET @param=f1();
EXECUTE IMMEDIATE &#39;SELECT * FROM t1 WHERE a=?&#39; USING @param;

SET @param=(SELECT 10);
EXECUTE IMMEDIATE &#39;SELECT * FROM t1 WHERE a=?&#39; USING @param;</code></pre>
<h3 id="참고">참고</h3>
<ul>
<li><a href="https://mariadb.com/kb/en/prepare-statement/">https://mariadb.com/kb/en/prepare-statement/</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[MariaDB] 동적쿼리 (1. 사용 예시)]]></title>
            <link>https://velog.io/@dev_osj/Mariadb-%EB%8F%99%EC%A0%81%EC%BF%BC%EB%A6%AC</link>
            <guid>https://velog.io/@dev_osj/Mariadb-%EB%8F%99%EC%A0%81%EC%BF%BC%EB%A6%AC</guid>
            <pubDate>Wed, 07 Apr 2021 08:12:50 GMT</pubDate>
            <description><![CDATA[<p>이번 포스팅은 mariadb 동적쿼리에 대해 작성해보려고 합니다.
단순히 개념만 작성하기보다는 사용 예시를 활용하여 작성해보려고 합니다.</p>
<h3 id="시나리오">시나리오</h3>
<ul>
<li>테이블 구조</li>
</ul>
<p>테이블명</p>
<blockquote>
<p>LOG&#95;DATA&#95;날짜</p>
</blockquote>
<p>테이블 용도</p>
<blockquote>
<p>로그 적재 테이블 </p>
</blockquote>
<p>스토리지 엔진</p>
<blockquote>
<p>INNODB</p>
</blockquote>
<p>컬럼  </p>
<blockquote>
<ol>
<li>LOG_NO (INT, NOT NULL, AUTO_INCREMENT, PK)</li>
<li>LOG_CONTENT (LONGTEXT, NULL 허용, COLLATE &#39;utf8_bin&#39;) </li>
<li>ADD_DATE (DATETIME, NULL 허용, DEFAULT NULL)</li>
</ol>
</blockquote>
<ul>
<li>테이블 관리</li>
</ul>
<blockquote>
<ol>
<li>테이블은 날짜로 구분한다.</li>
<li>최대 5일전 테이블까지 유지한다.</li>
<li>5일이 넘어간 테이블은 DROP 한다.</li>
</ol>
</blockquote>
<p>즉, 포스팅을 작성하는 오늘날짜(20210407)를 기준으로 관리되어야 할 테이블은 아래와 같습니다.
LOG_DATA (당일 로그데이터 적재)
LOG_DATA_20210406
LOG_DATA_20210405
LOG_DATA_20210404
LOG_DATA_20210403</p>
<h3 id="시나리오-분석">시나리오 분석</h3>
<p>위와 같은 상황이라면 아래와 같은 절차로 SQL을 작성하려고 합니다.</p>
<ol>
<li>6일 전 테이블은 삭제</li>
<li>당일 적재되는 로그 데이터의 테이블은 00시 기점으로 어제 날짜(20210406)로 테이블 명 변경</li>
<li>당일 로그 데이터 적재를 위해 새로운 테이블 생성</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Crontab]]></title>
            <link>https://velog.io/@dev_osj/Crontab</link>
            <guid>https://velog.io/@dev_osj/Crontab</guid>
            <pubDate>Wed, 07 Apr 2021 07:20:15 GMT</pubDate>
            <description><![CDATA[<p>리눅스에서 Crontab이란 일정시간마다 시스템에서 자동으로 실행시키는 데몬입니다.
Crontab 작업을 위한 리눅스 명령어를 간단히 정리하려고 합니다.
CentOS 7 기준으로 작성합니다.</p>
<br>

<h4 id="crontab-설치">crontab 설치</h4>
<blockquote>
<p>yum -y install cronie</p>
</blockquote>
<h4 id="crontab-시작">crontab 시작</h4>
<blockquote>
<p>systemctl start crond</p>
</blockquote>
<h4 id="crontab-상태확인">crontab 상태확인</h4>
<p>Active 항목에 초록색 글자로 active (running) 존재하는 확인</p>
<blockquote>
<p>systemctl status crond</p>
</blockquote>
<h4 id="crontab-등록">crontab 등록</h4>
<blockquote>
<p>crontab -e</p>
</blockquote>
<p>crontab -e 명령어 입력 후 편집기를 사용하여 </p>
<blockquote>
<p>&#42; &#42; &#42; &#42; &#42; &#42; /test/bin/test.sh</p>
</blockquote>
<p>위와 같이 작업을 작성해주시고 저장하시면 됩니다.
맨 앞의 와일드카드 표시는 시간대를 설정하는 부분인데 자세한 내용은 아래에 작성하도록 하겠습니다.</p>
<h4 id="등록된-cron-작업-확인">등록된 cron 작업 확인</h4>
<blockquote>
<p>crontab -l</p>
</blockquote>
<h4 id="crontab-작성-방법">Crontab 작성 방법</h4>
<p><img src="https://images.velog.io/images/dev_osj/post/f0baa781-6807-4fd7-ba36-4723b41087c3/image.png" alt=""></p>
<p>crontab 작업을 주의해야 할 점은 crontab -e 명령어 입력 후 작업 작성할 때 시간대 설정 + 파일을 한 줄로 작성하셔야 합니다.</p>
<p>예를들어 매일 오전 6시에 test/bin/test.sh를 실행시킨다라는 작업을 등록한다고 가정하겠습니다.
-&gt; 00 06 &#42; &#42; &#42; /test/bin/test.sh
위와 같이 한 줄로 작성해야 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Cache Pattern]]></title>
            <link>https://velog.io/@dev_osj/Cache-Pattern</link>
            <guid>https://velog.io/@dev_osj/Cache-Pattern</guid>
            <pubDate>Wed, 07 Apr 2021 00:49:32 GMT</pubDate>
            <description><![CDATA[<p>캐시 패턴의 경우 크게 3가지가 있습니다.</p>
<ul>
<li>No caching</li>
<li>Cache-Aside</li>
<li>Cache-Through</li>
</ul>
<p>위 세가지에 대해 간단히 정리하고자 합니다.
<br></p>
<h3 id="no-caching">No Caching</h3>
<p>제목 그대로 캐시를 적용하지 않는 일반적인 형태입니다.
애플리케이션에서 직접 DB로 요청하는 방식이며 별도 캐시한 내역이 없으므로 매번 DB와 통신이 필요합니다.
또한, 부하가 유발되는 SQL이 지속 수행되면 DB의 I/O에 영향을 주게되어 전체적으로 성능 저하가 발생할 수 있습니다.
<img src="https://images.velog.io/images/dev_osj/post/91ab7ba2-1eed-4749-85f4-43bf39f681d2/image.png" alt="">
(출처 : <a href="https://cla9.tistory.com/100">https://cla9.tistory.com/100</a>)
<br></p>
<h3 id="cache-aside">Cache-Aside</h3>
<p>애플리케이션에서 캐시를 직접 사용합니다.
즉, 애플리케이션 코드가 DB가 아닌 캐시를 먼저 탐색하고 만약 캐시가 찾던 데이터가 존재하면 캐시에서 바로 반환시켜 줍니다.
이후에 동일한 요청을 반복하면, 캐시에 데이터가 존재(cache hit)한다면 DB 조회없이 바로 데이터를 전달 받을 수 있습니다.
하지만, 캐시에 데이터가 존재하지 않는다면(cache miss) DB에서 데이터를 조회한 후 캐시에 다시 적재를 진행합니다.</p>
<h4 id="pseudocode-for-reading-values">Pseudocode for reading values</h4>
<pre><code>v = cache.get(k)
if (v == null) {
  v = sor.get(k)
  cache.put(k, v)
}</code></pre><h4 id="pseudocode-for-writing-values">Pseudocode for writing values</h4>
<pre><code>v = newV
sor.put(k, v)
cache.put(k, v)
</code></pre><p><img src="https://images.velog.io/images/dev_osj/post/96e802c4-07de-405b-a5f2-18238c5c146e/image.png" alt="">
(출처 : <a href="https://codeahoy.com/2017/08/11/caching-strategies-and-how-to-choose-the-right-one/">https://codeahoy.com/2017/08/11/caching-strategies-and-how-to-choose-the-right-one/</a>)
<br></p>
<h3 id="cache-through">Cache-Through</h3>
<p>cache miss가 발생했을 때 애플리케이션이 아닌 캐시에서 데이터를 처리한 다음 애플리케이션에 전달합니다.
즉, Cache-Aside에서는 데이터 처리에 대한 책임이 애플리케이션에 있었다면 Cache-Through는 캐시에게 위임합니다.</p>
<h4 id="read-through">Read-Through</h4>
<p>애플리케이션에서 데이터 요청 시 Cache hit이면 캐시에서 데이터를 전달해주고 cache miss이면 캐시에서 DB를 조회하여 캐시 내에 적재합니다.
<img src="https://images.velog.io/images/dev_osj/post/ecf068a6-e8e9-472b-88a8-17b4ca6b930e/image.png" alt="">
(출처 : <a href="https://codeahoy.com/2017/08/11/caching-strategies-and-how-to-choose-the-right-one/">https://codeahoy.com/2017/08/11/caching-strategies-and-how-to-choose-the-right-one/</a>)</p>
<h4 id="write-through">Write-Through</h4>
<p>애플리케이션에서 데이터 쓰기 요청 시, 애플리케이션은 쓰기 요청만 캐시로 전달 한 뒤 캐시에서 DB로 데이터 쓰기 요청을 하여 DB에 적재한다.
<img src="https://images.velog.io/images/dev_osj/post/3ccfab8c-533b-4f26-9ffd-bb6cde362f74/image.png" alt="">
(출처 : <a href="https://codeahoy.com/2017/08/11/caching-strategies-and-how-to-choose-the-right-one/">https://codeahoy.com/2017/08/11/caching-strategies-and-how-to-choose-the-right-one/</a>)</p>
<h4 id="write-behind">Write-Behind</h4>
<p>애플리케이션에서 데이터 쓰기 요청 시, 캐시에 데이터를 반영한 다음 일정시간 비동기적으로 캐시에서 DB로 데이터 적재를 요청합니다.
Write-Behind의 경우 애플리케이션에서 데이터 쓰기에 대한 성능은 향상시킬 수 있지만 캐시가 다운될 경우 유실 가능성이 존재합닏.
<img src="https://images.velog.io/images/dev_osj/post/b553293d-2807-4db5-b121-4cdfd081216b/image.png" alt="">
(출처 : <a href="https://codeahoy.com/2017/08/11/caching-strategies-and-how-to-choose-the-right-one/">https://codeahoy.com/2017/08/11/caching-strategies-and-how-to-choose-the-right-one/</a>)</p>
<h3 id="참고">참고</h3>
<ul>
<li><a href="https://codeahoy.com/2017/08/11/caching-strategies-and-how-to-choose-the-right-one/">https://codeahoy.com/2017/08/11/caching-strategies-and-how-to-choose-the-right-one/</a></li>
<li><a href="https://www.ehcache.org/documentation/3.3/caching-patterns.html#cache-aside">https://www.ehcache.org/documentation/3.3/caching-patterns.html#cache-aside</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[OOME-Out Of Memory Error(3. 힙 덤프)]]></title>
            <link>https://velog.io/@dev_osj/OOME-Out-Of-Memory-Error2-5951n881</link>
            <guid>https://velog.io/@dev_osj/OOME-Out-Of-Memory-Error2-5951n881</guid>
            <pubDate>Tue, 06 Apr 2021 09:14:48 GMT</pubDate>
            <description><![CDATA[<p>지금까지 OOME가 발생 가능한 케이스와 발생했을 떄의 heap 지표를 확인했습니다.
이번 포스팅은 OOME를 대비하기 위해 필요한 프로파일링 도구를 소개하고자 합니다.
프로파일링 도구는 여러개가 존재하지만 우선은 제가 사용했던 방법을 위주로 작성하도록 하겠습니다.</p>
<h3 id="heap-dump-생성">Heap Dump 생성</h3>
<ol>
<li><p>힙 덤프를 뜰 자바 어플리케이션 프로세스의 PID 확인</p>
<blockquote>
<p>EX) ps aux | grep java</p>
</blockquote>
</li>
<li><p>jmap 툴 사용</p>
<blockquote>
<p>예시: 힘 덤프파일은 heap.dump라 설정하고 PID가 777이다
jmap -dump:format=b,file=heap.dump 777</p>
</blockquote>
</li>
</ol>
<p>jmap을 활용해서 heap dump를 사용할 때 주의할 점은 가급적 실시간으로 트래픽을 처리해야 하는 프로세스에 사용하는 것에 대해 조심해야 할 필요가 있습니다. heap dump를 뜨게 되면 프로세스가 일시적으로 중지될 수 있습니다.</p>
<br>

<h3 id="eclipse-matmemory-analyzer">Eclipse MAT(Memory Analyzer)</h3>
<p>저 같은 경우 덤프 뜬 힙 파일을 분석할 툴로 이클립스에서 지원하는 MAT를 사용했습니다.
MAT 같은 경우 이클립스 marketplace에서 설치 가능합니다.</p>
<p>설치가 완료되면 Open Perspective 창에서 Memory Analysis 선택을 합니다.
<img src="https://images.velog.io/images/dev_osj/post/a98481f5-8112-418c-a7de-4fc3ddcc0d1d/image.png" alt=""></p>
<p>Memory Analysis 선택을 하면 아래 사진처럼 이클립스 창이 바뀝니다.
<img src="https://images.velog.io/images/dev_osj/post/7001ab22-8b51-493f-b5d9-4b1ce8c236de/image.png" alt=""></p>
<p>다음으로 File &gt; Open Heap Dump를 클릭 하신 뒤 jmap을 통해 덤프 받은 힙 파일을 열어 주시면 아래처럼 화면이 나오게 됩니다.
<img src="https://images.velog.io/images/dev_osj/post/f4b6c05a-b0b4-4330-b00b-8bda66f8a6b0/image.png" alt=""></p>
<p>해당 화면의 경우 메모리 릭이 의심되는 부분을 원형 차트로 표현하였고 스크롤을 살짝 내리시면 아래 창 처럼 Problem Suspect 부분을 확인하실 수 있으십니다.
이곳에서 Details를 클릭하시어 본격적으로 본인이 개발한 프로젝트의 프로파일링을 시작하실 수 있습니다.
<img src="https://images.velog.io/images/dev_osj/post/4c9f515a-ef37-4397-9d55-013057ee6188/image.png" alt=""></p>
<p>MAT에 대한 자세한 사용법은 아래 링크로 첨부해 드립니다.
<a href="https://www.eclipse.org/mat/about/screenshots.php">https://www.eclipse.org/mat/about/screenshots.php</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[OOME-Out Of Memory Error(2. 케이스)]]></title>
            <link>https://velog.io/@dev_osj/OOME-Out-Of-Memory-Error2</link>
            <guid>https://velog.io/@dev_osj/OOME-Out-Of-Memory-Error2</guid>
            <pubDate>Tue, 06 Apr 2021 07:00:34 GMT</pubDate>
            <description><![CDATA[<p>이번 포스팅에서는 OOME가 발생하는 몇 가지 케이스에 대해서 작성해보려고 합니다.</p>
<h3 id="oome-케이스">OOME 케이스</h3>
<ol>
<li>java.lang.OutOfMemoryError: Java Heap Space</li>
<li>java.lang.OutOfMemoryError: GC Overhead Limit Exceeded</li>
<li>java.lang.OutOfMemoryError: Metaspace</li>
<li>java.lang.OutOfMemoryError: Requested array size exceeds VM limit</li>
</ol>
<h4 id="javalangoutofmemoryerror-java-heap-space">java.lang.OutOfMemoryError: Java Heap Space</h4>
<p>해당 에러는 저 역시 개발하면서 종종 보았던 메시지입니다. 아마 많은 분들이 이 에러를 경험해보시지 않으셨을까 생각됩니다.
이 에러는 단순히 생성하고자 하는 오브젝트가 JVM의 Heap 메모리 가용 영역을 넘어설 경우 발생합니다.</p>
<p><strong>원인 및 특징</strong></p>
<ul>
<li>이 에러 메시지는 오브젝트를 java heap에 할당할 수 없을 때 발생</li>
<li>꼭 memory leak을 의미하지는 않음</li>
<li>애플리케이션을 위한 충분한 heap 공간이 없을 때 발생</li>
<li>다른 경우로는 애플리케이션의 생존 주기가 지나치게 길 경우 객체 참조를 의도적이지 않게 오래 참조하거나 이로 인해 GC 대상에서 제외되게 되어 발생할 수 있음, 외부 API를 사용할 경우 의도치 않게 객체 참조를 오래하여 발생할 수 도 있음</li>
<li>또 다른 원인으론 지나친 finalizer 사용으로 인해 발생할 수 있음</li>
<li>만약 클래스가 finalize 메소드를 가지고 있다면 해당 타입의 객체는 자신들의 공간에 대한 GC를 포함시킬 수 없음</li>
<li>finalizer가 finalizer queue를 처리하는 속도보다 빠를 경우 힙 공간이 가득차서 발생 가능</li>
</ul>
<h4 id="javalangoutofmemoryerror-gc-overhead-limit-exceeded">java.lang.OutOfMemoryError: GC Overhead Limit Exceeded</h4>
<p><strong>원인 및 특징</strong></p>
<ul>
<li>이 에러는 GC 빈도가 빈번하고 Java 프로그램 실행이 느려지게 만들 수 있습니다.</li>
<li>GC 이후 만약에 Java 프로세스가 GC 작업 하는 동안 전체 동작시간의 98%를 소비했음에도 heap 영역이 2% 이하로 확보되었을 때 발생</li>
<li>라이브 데이터가 할당 받아야 할 heap 영역의 크기가 적을 때 발생 가능. 즉, 위 heapspace가 부족한 것과 관련이 있음</li>
</ul>
<h4 id="javalangoutofmemoryerror-metaspace">java.lang.OutOfMemoryError: Metaspace</h4>
<p><strong>원인 및 특징</strong></p>
<ul>
<li>Metaspace는 로드된 Class들의 메타데이터가 저장되는 공간</li>
<li>클래스 메타데이터에 의해 사용되어질 Metaspace의 크기는 MaxMetaspaceSize 명령어로 설정 가능</li>
<li>Class 메타데이터들 위해 필요한 native memory(Metaspace)의 크기가 MaxMetaSpaceSize를 초과했을 때 에러 발생</li>
<li>Heap 크기 조정을 통해 조치 가능</li>
</ul>
<h4 id="javalangoutofmemoryerror-requested-array-size-exceeds-vm-limit">java.lang.OutOfMemoryError: Requested array size exceeds VM limit</h4>
<p><strong>원인 및 특징</strong></p>
<ul>
<li>애플리케이션이 배열의 할당을 heap size보다 크게 했을 때 발생</li>
<li>예를들어 512mb 크기의 배열 할당을 시도한다고 했을 때, 최대 heap size가 256mb이면 에러 발생</li>
</ul>
<h3 id="참고">참고</h3>
<ul>
<li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/memleaks002.html">https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/memleaks002.html</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[OOME-Out Of Memory Error(1. 개념)]]></title>
            <link>https://velog.io/@dev_osj/OOME-Out-Of-Memory-Error1</link>
            <guid>https://velog.io/@dev_osj/OOME-Out-Of-Memory-Error1</guid>
            <pubDate>Tue, 06 Apr 2021 06:33:48 GMT</pubDate>
            <description><![CDATA[<p>회사에서 신규 API를 개발한 뒤 테스트를 진행하면서 memory leak에 대해 구체적으로 공부하던 내용을 제 블로그에 정리하고자 합니다.
<br></p>
<h3 id="java의-메모리-관리">Java의 메모리 관리</h3>
<p><img src="https://images.velog.io/images/dev_osj/post/450d4e2e-89ff-4a7c-9d25-f1986da0d399/image.png" alt="">
(출처 : <a href="https://stackoverflow.com/questions/6091615/difference-between-on-heap-and-off-heap">https://stackoverflow.com/questions/6091615/difference-between-on-heap-and-off-heap</a>)</p>
<p>우선 OOME에 대해 알아보기전에 Java의 메모리 관리를 간략히 살펴보겠습니다.
우리가 JVM 내에서 OOME를 정리하기 전에 살펴보아야 할 곳이 바로 Heap 영역과 Metaspace 영역입니다.
heap 영역의 경우 대표적으로 new 키워드로 생성된 인스터스들이 메모리를 할당하여 존재하고 Metaspace의 경우 클래스의 메타데이터가 주로 위치합니다.
추가적으로 Off-heap이라는 부분을 확인해야 합니다.
Off-heap 영역은 Direct ByteBuffer를 이용하여 할당받은 버퍼 공간은 GC 대상이 되지 않습니다. off-heap 공간을 사용하는 대표적인 예로 EHCache와 같은 캐싱 데이터가 존재할 수 있으며 객체 직렬화를 통해 적재시킬 수 있습니다.
실제로 GC 수행횟수를 감소시켜 성능향상을 도모하기 위해 여러 오픈소스가 off-heap 활용을 하고 있습니다.</p>
<br>

<h3 id="정상동작과-oome-그래프">정상동작과 OOME 그래프</h3>
<p><img src="https://images.velog.io/images/dev_osj/post/536d8c9f-ff3a-4f33-ab72-9c5048f1e4c4/image.png" alt="">
(그림 1, 정상동작중인 VisualVM 모니터)</p>
<p><img src="https://images.velog.io/images/dev_osj/post/3b5eb71a-3a0c-4649-8ef4-66fa4d318001/image.png" alt="">
(그림 2, OOME일때 VisualVM 모니터, 출처 : <a href="https://www.torsten-horn.de/techdocs/jmx.htm">https://www.torsten-horn.de/techdocs/jmx.htm</a>)</p>
<p>위 그림의 경우 정상동작 중일 때와 OOME가 발생했을 때 VisualVM에서 모니터링한 화면입니다.
그림을 확인하면 크게 Heap size(주황색), Used heap(파랑색) 그래프로 구성된 것을 볼 수 있습니다.
중요하게 봐야할 그래프는 Used heap(파랑색) 부분입니다.
이 부분이 감소하지 않고 지속적으로 증가하거나 최대 힙 사이즈까지 차게되는 경우가 그림 2 처럼 OOME와 Memory Leak이 발생했다고 예상할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로젝트 배포 전략]]></title>
            <link>https://velog.io/@dev_osj/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B0%B0%ED%8F%AC-%EC%A0%84%EB%9E%B5</link>
            <guid>https://velog.io/@dev_osj/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B0%B0%ED%8F%AC-%EC%A0%84%EB%9E%B5</guid>
            <pubDate>Tue, 06 Apr 2021 05:47:19 GMT</pubDate>
            <description><![CDATA[<p>신규 프로젝트를 개발을 한 뒤 실 서버에 배포를 진행합니다.
이때, 배포에서도 전략이 있습니다.
이들 중 대표적인 전략에 대해 간단히 정리하고자 합니다.</p>
<h3 id="롤링-rolling">롤링 (Rolling)</h3>
<p><img src="https://images.velog.io/images/dev_osj/post/a56c641a-a9fc-4f01-9b9d-86f63f2ae2e6/image.png" alt=""></p>
<p>롤링 배포는 서버를 한 대씩 구버전에서 새 버전으로 교체해가는 전략입니다.
서비스 중인 서버 한 대를 제외시키고 그 자리에 새 버전의 서버를 추가합니다.
이렇게 구 버전에서 새 버전으로 트래픽을 점진적으로 전환합니다.
이와 같은 방식은 서버 수의 제약이 있을 경우 유용하나 배포 중 인스턴스의 수가 감소되므로 서버 처리 용량을 미리 고려해야 합니다.</p>
<br>

<h3 id="bluegreen">Blue/Green</h3>
<p><img src="https://images.velog.io/images/dev_osj/post/285aaf11-2e03-45c3-9bd8-05122889e775/image.png" alt=""></p>
<p>Blue/Green 배포는 구 버전에서 새 버전으로 일제히 전환하는 전략입니다.
구 버전의 서버와 새 버전의 서버들을 동시에 나란히 구성하고 배포 시점이 되면 트래픽을 일제히 전환시킵니다.
하나의 버전만 프로덕션 되므로 버전 관리 문제를 방지할 수 있고, 또한 빠른 롤백이 가능합니다.
또 다른 장점으로 운영 환경에 영향을 주지 않고 실제 서비스 환경으로 새 버전 테스트가 가능합니다.
예를 들어 구 버전과 새 버전을 모두 구성하고 포트를 다르게 주거나 내부 트래픽일 경우 새 버전으로 접근하도록 설정하여 테스트를 진행해 볼 수 있습니다.
단, 시스템 자원이 두 배로 필요하고, 전체 플랫폼에 대한 테스트가 진행되어야 합니다.</p>
<br>

<h3 id="canary">Canary</h3>
<p><img src="https://images.velog.io/images/dev_osj/post/c59064ca-cfc9-4e78-9be3-6a3dbc2ff48a/image.png" alt=""></p>
<p>Canary 배포는 위험을 빠르게 감지할 수 있는 배포 기법입니다.
구 버전의 서버와 새 버전의 서버들을 구성하고 일부 트래픽을 새 버전으로 분산하여 오류 여부를 판단합니다.
오류율 및 성능 모니터링에 유용합니다. 
트래픽을 분산시킬 라우팅은 랜덤으로 할 수 있고 사용자 프로필 등을 기반으로 분류할 수 있습니다.
분산 후 결과에 따라 새 버전이 운영 환경을 대체할 수 있고, 다시 구 버전으로 돌아갈 수 있습니다.
<br></p>
<h3 id="참고">참고</h3>
<ul>
<li><a href="https://onlywis.tistory.com/10">https://onlywis.tistory.com/10</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Process vs Thread (4. PCB)]]></title>
            <link>https://velog.io/@dev_osj/Process-vs-Thread-4</link>
            <guid>https://velog.io/@dev_osj/Process-vs-Thread-4</guid>
            <pubDate>Wed, 31 Mar 2021 03:02:33 GMT</pubDate>
            <description><![CDATA[<h2 id="process-table-and-process-control-block-pcb">Process Table and Process Control Block (PCB)</h2>
<br>

<p>프로세스를 생성하는 동안 운영체제는 몇 가지 작업을 수행한다.
프로세스를 식별하기 위해 프로세스 식별 번호(PID)를 각각의 프로세스에게 할당한다.
운영체제가 multi-programming을 지원하면서 모든 프로세스들을 추적할 필요가 있다.
이러한 작업을 위해, PCB는 프로세스의 실행 상태 추적을 위해 사용되어 진다.
각 메모리의 블록은 process state, program counter, stack pointer, status of opened files, scheduling algorithms 등의 정보를 포함한다.
이러한 모든 정보는 피요하고 프로세스가 현 상태에서 다른 상태로 전환될 때 저장되어야 한다.
프로세스가 다른 상태로 변화할 때, 운영체제는 프로세스의 PCB 정보를 수정한다.
PCB는 프로세스에 대한 정보를 포함한다.
프로세스 테이블은 PCB들의 배열이며 즉, 현재 시스템 내 모든 프로세스들에 대한 PCB를 논리적으로 포함하고 있다는 의미이다.</p>
<p><img src="https://images.velog.io/images/dev_osj/post/c3ec15d5-68ca-49bc-8922-bd3c72b6d9ba/image.png" alt="">
(출처 : <a href="https://www.geeksforgeeks.org/process-table-and-process-control-block-pcb/?ref=lbp">https://www.geeksforgeeks.org/process-table-and-process-control-block-pcb/?ref=lbp</a> )</p>
<ul>
<li>pointer : 프로세스가 다른 상태로 변화될 때 현재 프로세스의 위치를 가지고 있는 스택 포인터다.</li>
<li>process state : 프로세스의 각 상태를 저장</li>
<li>process number : 모든 프로세스는 ID 혹은 PID라고 불리어지는 프로세스의 식별 번호를 할당 받는다</li>
<li>Program Counter : 프로세스를 실행한 다음 지시의 주소를 포함하고 있다</li>
<li>Regiester : accumulator, base, regiester를 CPU 레지스터는 포함하고 있다</li>
<li>Memory limits : 운영체제에 의해 메모리 운영 시스템에 대한 정보를 포함하고 있다. page tables, setment table 등과 같은 정보를 포함하고 있다</li>
<li>open files list : 프로세스를 위해 현재 열려 있는 파일의 리스트 정보<br>

</li>
</ul>
<h3 id="miscellaneous-accounting-and-status-data">Miscellaneous accounting and status data</h3>
<p><img src="https://images.velog.io/images/dev_osj/post/fa6e3ecd-34a5-414d-8a11-31ea6483db66/image.png" alt="">
(출처 : <a href="https://www.geeksforgeeks.org/process-table-and-process-control-block-pcb/?ref=lbp">https://www.geeksforgeeks.org/process-table-and-process-control-block-pcb/?ref=lbp</a> )</p>
<p>CPU 사용량, 시간 제약, 작업 혹은 프로세스 번호를 포함하고 있다.
PCB는 running상태로부터 blocked가 되었을 때 프로세서의 실행 내용이라고도 하는 레지스터 내용을 저장한다. 이러한 내용들은 운영체제가 프로세스가 running상태로 돌아왔을 때 프로세스 실행 내용들을 복구가 가능하도록 한다.
프로세스가 다른 상태로 전이될 때, 운영체제는 이러한 정보들을 프로세스의 PCB에 수정한다. 
운영체제는 프로세스 테이블 안에 있는 각각의 프로세스의 PCB를 가리키면서 PCB 접근이 빨라지도록 하고 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Open JDK vs Oracle JDK]]></title>
            <link>https://velog.io/@dev_osj/Open-JDK-vs-Oracle-JDK</link>
            <guid>https://velog.io/@dev_osj/Open-JDK-vs-Oracle-JDK</guid>
            <pubDate>Tue, 30 Mar 2021 07:56:26 GMT</pubDate>
            <description><![CDATA[<p>현재 회사에서는 open jdk를 사용하고 있습니다.
그런던 와중에 oracle jdk와 open jdk 차이가 갑자기 궁금해져 검색을 해보았고 Baeldung에 있는 내용을 간략이 정리하려고 합니다.</p>
<h3 id="oracle-jdk-vs-open-jdk">Oracle JDK vs Open JDK</h3>
<h4 id="release-schedule">Release Schedule</h4>
<p>오라클은 매 3년 마다 출시하며 openJDK는 매 6개월마다 출시합니다.
Oracle은 릴리스에 대한 장기적인 지원을 제공합니다. 반면에 OpenJDK는 다음 버전이 릴리스 될 때까지만 릴리스에 대한 변경 사항을 지원합니다 
<br></p>
<h4 id="licenses">Licenses</h4>
<p>오라클 JDK는 Oracle Binary Code License Agreement 산하에 라이센스를 두고 있으며 OpenJDK는 GNU General Public License version2에 라이센스를 두고 있습니다.</p>
<p>Oracle이 발표 한 바와 같이 2019 년 1 월 이후에 출시 된 Oracle Java SE 8에 대한 공개 업데이트는 상업용 라이선스없이 비즈니스, 상업용 또는 프로덕션 용도로 사용할 수 없습니다.
하지만 OpenJDK는 완전한 오픈소스이며 무료로 사용 가능합니다.
<br></p>
<h4 id="performance">Performance</h4>
<p>OracleJDK의 빌드 프로세스는 OpenJDK 빌드 프로세스에 기반하기 때문에 실질적인 기술 차이는 없습니다.
성능적인 측면을 보면 오라클이 응답성 및 JVM 성능면에서는 나은 모습을 보이고 있습니다. 왜냐하면 엔터프라이즈급 고객들에게 제공하기 위해 안정성에 조금 더 집중하기 때문입니다.
반대로 OpenJDK의 경우 좀 더 자주 드라이버를 릴리즈 합니다. 
결과적으로 불안정한 문제에 직면할 수 있습니다.
커뮤니티 피드백에 따르면, OpenJDK 사용자들이 성능 이슈에 종종 직면하는 것을 알 수 있습니다.
<br></p>
<h3 id="features">Features</h3>
<p>만약 우리가 특징과 옵션을 비교해야 한다면 우리는 오라클 제품의 경우 Flight Recorder, java Mission Control, Application Class-Data Sharing 특징을 가지고 있는 것을 알 수 있으며 OpenJDK는 Font Renderer 특징을 가지고 있는 것을 알 수 있습니다.
또한, 오라클은 Garbage Collection 옵션을 더 가지고 있으며 더 나은 렌더링을 지원합니다.
<br></p>
<h3 id="development-and-popularity">Development and Popularity</h3>
<p>OracleJDK의 경우 Oracel Corporation에 의해 완전히 개발되었지만 OpenJDK는 Oracle, OpenJDK, Java Community에 의해서 개발 되었습니다. 하지만, 탑 기업인  Red Hat, Azul Systems, Azul System, IBM, Apple 등 또한 OpenJDK 개발 프로젝트에 참여하였습니다.
Android Studio 또는 IntelliJ IDEA와 같은 도구에서 Java Development Kit를 사용하는 최고 기업의 선호도에 관해서는 Oracle JDK가 더 선호되었지만 둘 다 OpenJDK 기반 JetBrains 빌드로 전환했습니다.
반면에, 주요 리눅스 제공사인 (Fedora, Ubuntu, Red hat Enterprise Linux)는 OpenJDK를 기본 Java SE로 지원합니다.
<br></p>
<h3 id="changes-since-java11">Changes Since Java11</h3>
<p>Oracle은 Oracle 제품 또는 서비스의 일부로 Oracle JDK를 사용할 때 또는 다음과 같은 경우에 Oracle JDK를 사용할 때 오픈 소스 GNU General Public License v2, Classpath Exception (GPLv2 + CPE) 및 상업용 라이선스의 조합으로 과거 &quot;BCL&quot;라이선스를 변경합니다. 오픈 소스 소프트웨어는 환영하지 않습니다.
각 라이선스는 서로 다른 빌드를 가지지만 기능적으로는 약간의 외형 및 포장 차이만 있습니다.
또한, Flight Recorder, Java Mission Control, and Application Class-Data Sharing과 같은 상업용 특징들은 현재 OpenJDK에서도 가능합니다.
그러므로, OracleJDK와 OpenJDK 빌드는 본질적으로 java11부터 동일합니다.</p>
<h3 id="참고">참고</h3>
<p><a href="https://www.baeldung.com/oracle-jdk-vs-openjdk">https://www.baeldung.com/oracle-jdk-vs-openjdk</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Process vs Thread (3. 스케줄러 상세)]]></title>
            <link>https://velog.io/@dev_osj/Process-vs-Thread-3</link>
            <guid>https://velog.io/@dev_osj/Process-vs-Thread-3</guid>
            <pubDate>Tue, 30 Mar 2021 05:34:01 GMT</pubDate>
            <description><![CDATA[<h2 id="process-schedulers-in-operating-system">Process Schedulers in Operating System</h2>
<br>

<p>프로세스를 스케줄링하기 위한 Queue에는 세 가지 종류가 존재한다.</p>
<ul>
<li>Job Queue : 현재 시스템 내에 있는 모든 프로세스의 집합</li>
<li>Ready Queue : 현재 메모리 내에 있으면서 CPU를 잡아서 실행되기를 기다리는 프로세스의 집합</li>
<li>Device Queue : Device I/O 작업을 대기하고 있는 프로세스의 집합</li>
</ul>
<p>각각의 Queue에 프로세스들을 넣고 빼주는 스케줄러에도 크게 아래 세 가지종류가 있다
<br></p>
<h3 id="long-term-or-job-scheduler">Long term or job scheduler</h3>
<p>장기 스케쥴러는 새로운 프로세스를 &#39;ready 상태&#39;로 가져온다. 
장기 스케쥴러는 Degree of Multi-programming을 제어한다.
장기 스케쥴러가 IO와 CPU bound 프로세스 모두 신중한 선택을 하는 것은 중요하다. IO 바운드 작업의 경우 input과 output 동작에 더 많은 시간을 할애 하며 CPU 바운드 프로세스는 CPU에서 더 많은 시간을 할애한다. 작업 스케줄러는 IO 바운드와 CPU 바운드 사이에서 효율적인 균형 유지를 향상 시킨다.</p>
<h4 id="정리">정리</h4>
<ul>
<li>메모리와 디스크 사이의 스케줄링</li>
<li>프로세스에 memory(및 각종 리소스) 할당</li>
<li>degree of Multiprogramming 제어 (실행중인 프로세스의 수 제어)</li>
<li>프로세스의 상태 new -&gt; ready (in memory)<br>
### Short term or CPU scheduler
단기 스케쥴러는 어느 한 프로세스를 running 시킬지 결정한다. 단기 스케줄러는 프로세스를 running 상태로 올리는 것이 아닌 어떤 프로세스를 선택하는 것인지에 대한 것만 관여한다.
CPU 스케줄러는 높은 burst time을 가지는 프로세스들로 인해 starvation(기아상태)가 발생하지 않도록 해야 한다.
Dispatcher는 단기 스케줄러가 선택한 프로세스를 CPU에 로드하는 역할을 한다 (Ready to Running State) 컨텍스트 전환은 디스패처에 의해서만 수행된다.
Dispatcher는 아래와 같은 작업을 한다.</li>
</ul>
<ol>
<li>Switching context</li>
<li>Switching to user mode</li>
<li>새롭게 로드된 프로그램과 같이 적절한 위치로 이동시킨다<h4 id="정리-1">정리</h4>
</li>
</ol>
<ul>
<li>CPU와 메모리 사이의 스케줄링 담당</li>
<li>ready queue에 존재하는 프로세스 중 어떤 프로세스를 running시킬지 결정</li>
<li>프로세스에 CPU를 할당</li>
<li>프로세스 상태 ready -&gt; running -&gt; waiting -&gt; ready<br>

</li>
</ul>
<h3 id="medium-term-scheduler">Medium-term scheduler</h3>
<p>프로세스를 중단하거나 재시작하는 역할을 한다.
프로세스들을 메인 메모리부터 디스크나 다른 프로세스로 swapping을 하도록 한다.
I/O bound와 CPU bound 사이에서 균형을 유지시키는데 도움을 준다
degree of multiprogramming을 감소시킨다.</p>
<h4 id="정리-2">정리</h4>
<ul>
<li>여유 공간 마련을 위해 프로세스를 통째로 메모리에서 디스크로 쫒아냄(swapping)</li>
<li>프로세스에게서 memory를 deallocate</li>
<li>프로세스의 상태 ready -&gt; suspended<br>

</li>
</ul>
<p><img src="https://images.velog.io/images/dev_osj/post/bcdfe1df-10a6-43ba-ba85-dc307589d1bb/image.png" alt="">
(출처 : <a href="https://velog.io/@ss-won/OS-CPU-Scheduler%EC%99%80-Dispatcher">https://velog.io/@ss-won/OS-CPU-Scheduler%EC%99%80-Dispatcher</a>)</p>
<h3 id="출처">출처</h3>
<ul>
<li><a href="https://www.geeksforgeeks.org/process-schedulers-in-operating-system/?ref=lbp">https://www.geeksforgeeks.org/process-schedulers-in-operating-system/?ref=lbp</a></li>
<li><a href="https://github.com/JaeYeopHan/Interview_Question_for_Beginner/blob/master/OS/README.md#%EB%A9%80%ED%8B%B0-%EC%8A%A4%EB%A0%88%EB%93%9C">https://github.com/JaeYeopHan/Interview_Question_for_Beginner/blob/master/OS/README.md#%EB%A9%80%ED%8B%B0-%EC%8A%A4%EB%A0%88%EB%93%9C</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Process vs Thread (2. 상태 및 스케쥴러)]]></title>
            <link>https://velog.io/@dev_osj/Process-vs-Thread-2</link>
            <guid>https://velog.io/@dev_osj/Process-vs-Thread-2</guid>
            <pubDate>Tue, 30 Mar 2021 04:54:31 GMT</pubDate>
            <description><![CDATA[<h2 id="states-of-a-process-in-operating-systems">States of a Process in Operating Systems</h2>
<br>

<h3 id="프로세스의-상태">프로세스의 상태</h3>
<p><img src="https://images.velog.io/images/dev_osj/post/50ab0b42-ac54-434b-85e1-09e2624061e6/image.png" alt="">
(출처 : <a href="https://www.geeksforgeeks.org/states-of-a-process-in-operating-systems/?ref=lbp">https://www.geeksforgeeks.org/states-of-a-process-in-operating-systems/?ref=lbp</a>)</p>
<ul>
<li>New (Create) : 프로세스의 경우 생성이 될 예정인 상태로, 프로세스를 선택하기 위해 OS에 의해 선택되는 보조 메모리에 존재하는 프로그램입니다. </li>
<li>Ready : New -&gt; Ready, 프로세스의 생성이 완료된 후에, 프로세스는 ready 상태로 진입합니다. 즉, 프로세스는 메인 메모리에 적재가 됩니다. ready 상태의 프로세스는 CPU 자원 할당을 기다리고 CPU에 의해 실행 준비가 된 프로세스는 준비된 프로세스들을 위한 queue에 들어가게 됩니다.</li>
<li>Run : 실행을 위해 CPU에 의해 선택되어지고 프로세스 내 지시들은 사용 가능한 CPU 코어 중 하나에 의해 실행됩니다.</li>
<li>Blocked or wait : 프로세스가 I/O 접근을 요청하거나 사용자로부터의 입력이 필요하거나, 임게 구역에 대한 접근이 필요할 때 blocked 혹은 wait 상태에 진입하게 됩니다. 프로세스는 지속적으로 메인 메모리에 대기하게되고 CPU에 요청하지 못합니다. I/O 동작이 완료되면 프로세스는 ready 상태로 전환됩니다.</li>
<li>Terminated or completed : 프로세스가 종료되고 PCB도 삭제됩니다.</li>
<li>Suspend Ready : 처음에는 준비상태에 있던 프로세스이지만 메인 메모리로 밀려나고 스케줄러에 의해 외부 저장소에 위치하게 되는 것이 suspend ready 상태라고 한다. 프로세스는 메인 메모리에 올라가게 될 때 다시 ready 상태로 전환이 된다.</li>
<li>Suspend wait or suspend blocked : suspend ready와 비슷하지만 I/O 동작이나 메인 메모리가 부족하게 되는 작업을 수행하는 프로세스는 보조 메모리로 이동하게 된다. 작업이 완료 되었을 때 다시 suspend ready 상태로 간다.<br>
### CPU and IO Bound Processes
CPU 연산 작업을 많이 사용하는 것은 CPU Bound 프로세스라고 하며 I/O작업을 많이 사용하는 것은 IO Bound 프로세스라고 한다.
#### 스케줄러의 종류</li>
</ul>
<ol>
<li>long term (장기 스케쥴러) - 성능 : 얼마만큼의 프로세스가 ready 상태에 머물러야 하는지 결정해주며 degree of <strong>MultiProgramming</strong>을 결정해준다, 한번 결정이 이루어지만 오랜 시간 동안 지속되는데 그러므로 long term scheduler라고 부른다</li>
<li>Short term (단기 스케쥴러) - 문맥교환 시간 : short term 스케쥴러는 어떤 프로세스가 실행될 지 결정하고 dispatcher를 호출 한다. dispatcher는 프로세스를 실행 가능한 상태로 이동시키는 소프트웨어다</li>
<li>Medium term (중기 스케쥴러) - Swapping time : suspension 결정은 중기 스케쥴러에 의해 결정된다. 중기 스케쥴러는 프로세스를 메인 메모리에서 보조 메로리로 이동시키는 스왑 작업에 사용된다. <h4 id="multiprogramming">MultiProgramming</h4>
</li>
<li>pre-emption(선점) : 프로세스가 강제로 CPU로부터 제거 된다. 선점은 시간 공유 혹은 멀티 태스킹이라고도 불린다.</li>
<li>Non Pre-emtion (비선점) : 프로세스가 실행이 완료될 때 까지 제거되지 않는다.<h4 id="degree-of-multiprogramming">Degree of MultiProgramming</h4>
ready 상태에 있는 최대한의 프로세스의 개수를 의미함<h4 id="dispatcher">Dispatcher</h4>
CPU의 제어권을 CPU 스케쥴러에 의해 선택된 프로세스에게 넘긴다.
CPU 스케줄러 내부에 포함된 것으로, 단기 스케줄러가 <strong>선택한 프로세스에 실질적으로 프로세서를 할당하는 역할</strong>이다;
프로세스의 레지스터를 적재하고(문맥교환), 운영체제 모드(kernel mode)에서 사용자 상태(User mode)로 전환시켜주면 프로세스가 다시 시작할 때 사용자 프로그램이 올바른 위치를 찾을 수 있도록 한다.<br>

</li>
</ol>
<h3 id="참고">참고</h3>
<p><a href="https://www.geeksforgeeks.org/states-of-a-process-in-operating-systems/?ref=lbp">https://www.geeksforgeeks.org/states-of-a-process-in-operating-systems/?ref=lbp</a></p>
]]></description>
        </item>
    </channel>
</rss>