오랜만에 주말 풀이~

스케쥴링 구현 문제였고 DP 메모라이제이션을 넣어서 풀었다.


문제 설명

https://school.programmers.co.kr/learn/courses/30/lessons/214288

문제 설명 궁금하면 아래 더보기 누르기

더보기

현대모비스는 우수한 SW 인재 채용을 위해 상시로 채용 설명회를 진행하고 있습니다. 채용 설명회에서는 채용과 관련된 상담을 원하는 참가자에게 멘토와 1:1로 상담할 수 있는 기회를 제공합니다. 채용 설명회에는 멘토 n명이 있으며, 1~k번으로 분류되는 상담 유형이 있습니다. 각 멘토는 k개의 상담 유형 중 하나만 담당할 수 있습니다. 멘토는 자신이 담당하는 유형의 상담만 가능하며, 다른 유형의 상담은 불가능합니다. 멘토는 동시에 참가자 한 명과만 상담 가능하며, 상담 시간은 정확히 참가자가 요청한 시간만큼 걸립니다.

참가자가 상담 요청을 하면 아래와 같은 규칙대로 상담을 진행합니다.

  • 상담을 원하는 참가자가 상담 요청을 했을 때, 참가자의 상담 유형을 담당하는 멘토 중 상담 중이 아닌 멘토와 상담을 시작합니다.
  • 만약 참가자의 상담 유형을 담당하는 멘토가 모두 상담 중이라면, 자신의 차례가 올 때까지 기다립니다. 참가자가 기다린 시간은 참가자가 상담 요청했을 때부터 멘토와 상담을 시작할 때까지의 시간입니다.
  • 모든 멘토는 상담이 끝났을 때 자신의 상담 유형의 상담을 받기 위해 기다리고 있는 참가자가 있으면 즉시 상담을 시작합니다. 이때, 먼저 상담 요청한 참가자가 우선됩니다.

참가자의 상담 요청 정보가 주어질 때, 참가자가 상담을 요청했을 때부터 상담을 시작하기까지 기다린 시간의 합이 최소가 되도록 각 상담 유형별로 멘토 인원을 정하려 합니다. 단, 각 유형별로 멘토 인원이 적어도 한 명 이상이어야 합니다.

예를 들어, 5명의 멘토가 있고 1~3번의 3가지 상담 유형이 있을 때 아래와 같은 참가자의 상담 요청이 있습니다.

참가자의 상담 요청

참가자 번호시각상담 시간상담 유형

1번 참가자10분60분1번 유형
2번 참가자 15분 100분 3번 유형
3번 참가자 20분 30분 1번 유형
4번 참가자 30분 50분 3번 유형
5번 참가자 50분 40분 1번 유형
6번 참가자 60분 30분 2번 유형
7번 참가자 65분 30분 1번 유형
8번 참가자 70분 100분 2번 유형

이때, 멘토 인원을 아래와 같이 정하면, 참가자가 기다린 시간의 합이 25로 최소가 됩니다.

1번 유형2번 유형3번 유형

2명1명2명

1번 유형

1번 유형을 담당하는 멘토가 2명 있습니다.

  • 1번 참가자가 상담 요청했을 때, 멘토#1과 10분~70분 동안 상담을 합니다.
  • 3번 참가자가 상담 요청했을 때, 멘토#2와 20분~50분 동안 상담을 합니다.
  • 5번 참가자가 상담 요청했을 때, 멘토#2와 50분~90분 동안 상담을 합니다.
  • 7번 참가자가 상담 요청했을 때, 모든 멘토가 상담 중이므로 1번 참가자의 상담이 끝날 때까지 5분 동안 기다리고 멘토#1과 70분~100분 동안 상담을 합니다.

2번 유형

2번 유형을 담당하는 멘토가 1명 있습니다.

  • 6번 참가자가 상담 요청했을 때, 멘토와 60분~90분 동안 상담을 합니다.
  • 8번 참가자가 상담 요청했을 때, 모든 멘토가 상담 중이므로 6번 참가자의 상담이 끝날 때까지 20분 동안 기다리고 90분~190분 동안 상담을 합니다.

3번 유형

3번 유형을 담당하는 멘토가 2명 있습니다.

  • 2번 참가자가 상담 요청했을 때, 멘토#1과 15분~115분 동안 상담을 합니다.
  • 4번 참가자가 상담 요청했을 때, 멘토#2와 30분~80분 동안 상담을 합니다.

상담 유형의 수를 나타내는 정수 k, 멘토의 수를 나타내는 정수 n과 참가자의 상담 요청을 담은 2차원 정수 배열 reqs가 매개변수로 주어집니다. 멘토 인원을 적절히 배정했을 때 참가자들이 상담을 받기까지 기다린 시간을 모두 합한 값의 최솟값을 return 하도록 solution 함수를 완성해 주세요.

 


제한사항

  • 1 ≤ k ≤ 5
  • k ≤ n ≤ 20
  • 3 ≤ reqs의 길이 ≤ 300
    • reqs의 원소는 [a, b, c] 형태의 길이가 3인 정수 배열이며, c번 유형의 상담을 원하는 참가자가 a분에 b분 동안의 상담을 요청했음을 의미합니다.
    • 1 ≤ a ≤ 1,000
    • 1 ≤ b ≤ 100
    • 1 ≤ c ≤ k
    • reqs는 a를 기준으로 오름차순으로 정렬되어 있습니다.
    • reqs 배열에서 a는 중복되지 않습니다. 즉, 참가자가 상담 요청한 시각은 모두 다릅니다.

입출력 예

3 5 [[10, 60, 1], [15, 100, 3], [20, 30, 1], [30, 50, 3], [50, 40, 1], [60, 30, 2], [65, 30, 1], [70, 100, 2]] 25
2 3 [[5, 55, 2], [10, 90, 2], [20, 40, 2], [50, 45, 2], [100, 50, 2]] 90

풀이 방법

1번

각 유형별로 req를 모아둔다.

2번

각 유형별로 멘토가 배정될 수 있는 모든 경우의 수를 가지고 for문을 돌린다.
중복 조합을 사용하였다.

5명 멘토 /.3개 과제

3 1 1
1 2 2
1 3 1
2 1 1
2 1 2

3번

각 과제에 멘토가 배정된 경우일 때 스케쥴링을 돌린 후 기다린 시간의 총합을 계산한다.

4번

각 과제에 멘토가 배정된 수에 따라 기다린 시간의 총합을 일정하므로 그 값을 재활용할 수 있게 dp map에 저장해서 쓴다.

1번 과제에 1명의 멘토가 붙는 경우
1번 과제에 2명의 멘토가 붙는 경우

요런 값들은 기다린 시간의 총합이 정해져있기 때문에 다음 계산 시에도 사용 가능하다.

풀이 코드


from itertools import combinations_with_replacement
from heapq import heappush, heappop
from collections import deque

dp = {}

def solution(k, n, reqs):
    answer = 300 * 1000 + 1

    reqs_by_type = [[] for i in range(k+1)]
    for req in reqs:
        reqs_by_type[req[2]].append(req)
    for req in reqs_by_type:
        req.sort(key = lambda x: x[0])

    if (n == k):
        return get_total_waiting_time(reqs_by_type, [1] * (k+1))

    # 배정한 거 다 돌려보기
    for cwr in combinations_with_replacement(list(range(1, k+1)), n-k):
        mentor_counts = [1] * (k+1)
        for c in cwr:
            mentor_counts[c] += 1
        answer = min(answer, get_total_waiting_time(reqs_by_type, mentor_counts))
    return answer

def get_total_waiting_time(reqs_by_type, mentor_counts) :
    time = 0
    for type_num, m in enumerate(mentor_counts):
        if type_num == 0 : continue
        time += get_waiting_time_by_type(reqs_by_type[type_num], type_num, m)
    return time

def get_waiting_time_by_type(reqs, type_num, mentor_count):
    if not reqs : return 0

    key = (type_num, mentor_count)
    if key not in dp:
        waiting_time = 0
        heap = [(0, i) for i in range(mentor_count)]

        reqs = deque(reqs[:])
        cur_time = 0
        while reqs:
            end_time, mentor_idx = heappop(heap)
            cur = reqs.popleft()
            cur_time = max(cur[0], end_time)
            waiting_time += max(0, end_time - cur[0])
            heappush(heap, (cur_time + cur[1], mentor_idx))
        dp[key] = waiting_time
    return dp[key]

'알고리즘' 카테고리의 다른 글

My github as algorithm hub  (0) 2021.06.16
[이코테] 구현 - 게임 개발  (0) 2021.06.13
[stack] 2504번 괄호의 합  (0) 2019.06.03

참고 할만한 링크

   - <https://mangkyu.tistory.com/76> 글에서 대부분을 참고했음을 알립니다.

   - Spring Security 제대로 이해하기 <https://gngsn.tistory.com/160>

   - Spring Security의 권한 부여 처리 흐름 [<https://velog.io/@cjstk3221/Spring-Security의-권한-부여-처리-흐름>](<https://velog.io/@cjstk3221/Spring-Security%EC%9D%98-%EA%B6%8C%ED%95%9C-%EB%B6%80%EC%97%AC-%EC%B2%98%EB%A6%AC-%ED%9D%90%EB%A6%84>)

   - Spring Seucurity 인가 처리 흐름 [<https://velog.io/@penrose_15/Spring-Security의-인가-처리-흐름>](<https://velog.io/@penrose_15/Spring-Security%EC%9D%98-%EC%9D%B8%EA%B0%80-%EC%B2%98%EB%A6%AC-%ED%9D%90%EB%A6%84>)

해보기

  • 임의의 필터를 만들어서 권한을 부여하고 부여한 권한이 있는 경우에만 특정 API 처리하도록 만들어보기
-> **참고 코드**

http.addFilterBefore(new LshFilter(), UsernamePasswordAuthenticationFilter.class);

.antMatchers("/lsh").hasRole("lsh");

/////////

public class LshFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        SecurityContextHolder.setContext(new SecurityContextImpl(new UsernamePasswordAuthenticationToken("lsh", "lsh")));
    }

}

1. Spring Security란?


인증, 인가

[ Spring Security란? ]

Spring Security는 Spring 기반의 애플리케이션의 보안(인증과 권한, 인가 등)을 담당하는 스프링 하위 프레임워크이다. Spring Security는 '인증'과 '권한'에 대한 부분을 Filter 흐름에 따라 처리하고 있다. Filter는 Dispatcher Servlet으로 가기 전에 적용되므로 가장 먼저 URL 요청을 받지만, Interceptor는 Dispatcher와 Controller사이에 위치한다는 점에서 적용 시기의 차이가 있다. Spring Security는 보안과 관련해서 체계적으로 많은 옵션을 제공해주기 때문에 개발자 입장에서는 일일이 보안관련 로직을 작성하지 않아도 된다는 장점이 있다.

이러한 Spring Security의 아키텍쳐는 아래와 같다.


[ 인증(Authorizatoin)과 인가(Authentication) ]

  • 인증(Authentication): 해당 사용자가 본인이 맞는지를 확인하는 절차
  • 인가(Authorization): 인증된 사용자가 요청한 자원에 접근 가능한지를 결정하는 절차

Spring Security는 기본적으로 인증 절차를 거친 후에 인가 절차를 진행하게 되며, 인가 과젱에서 해당 리소스에 대한 접근 권한이 있는지 확인을 하게 된다. Spring Security에서는 이러한 인증과 인가를 위해 Principal을 아이디로, Credential을 비밀번호로 사용하는 Credential 기반의 인증 방식을 사용한다.

  • Principal(접근 주체): 보호받는 Resource에 접근하는 대상
  • Credential(비밀번호): Resource에 접근하는 대상의 비밀번호

2. Spring Security 모듈


[ Spring Security 주요 모듈 ]

Spring Security의 주요 모듈은 아래와 같이 구성되며 각 항목들에 대해서 간단히 살펴보도록 하자.

 

[ SecurityContextHolder ]

SecurityContextHolder는 보안 주체의 세부 정보를 포함하여 응용프래그램의 현재 보안 컨텍스트에 대한 세부 정보가 저장된다. SecurityContextHolder는 기본적으로 SecurityContextHolder.MODE_INHERITABLETHREADLOCAL 방법과SecurityContextHolder.MODE_THREADLOCAL 방법을 제공한다.

[ SecurityContext ]

Authentication을 보관하는 역할을 하며, SecurityContext를 통해 Authentication 객체를 꺼내올 수 있다.

[ Authentication ]

Authentication는 현재 접근하는 주체의 정보와 권한을 담는 인터페이스이다. Authentication 객체는 Security Context에 저장되며, SecurityContextHolder를 통해 SecurityContext에 접근하고, SecurityContext를 통해 Authentication에 접근할 수 있다.

public interface Authentication extends Principal, Serializable {
// 현재 사용자의 권한 목록을 가져옴
    Collection<? extends GrantedAuthority> getAuthorities();

// credentials(주로 비밀번호)을 가져옴Object getCredentials();

    Object getDetails();

// Principal 객체를 가져옴.Object getPrincipal();

// 인증 여부를 가져옴boolean isAuthenticated();

// 인증 여부를 설정함void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

[ UsernamePasswordAuthenticationToken ]

UsernamePasswordAuthenticationToken은 Authentication을 implements한 AbstractAuthenticationToken의 하위 클래스로, User의 ID가 Principal 역할을 하고, Password가 Credential의 역할을 한다. UsernamePasswordAuthenticationToken의 첫 번째 생성자는 인증 전의 객체를 생성하고, 두번째 생성자는 인증이 완려된 객체를 생성한다.

public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
// 주로 사용자의 ID에 해당함private final Object principal;
// 주로 사용자의 PW에 해당함private Object credentials;

// 인증 완료 전의 객체 생성public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
		super(null);
		this.principal = principal;
		this.credentials = credentials;
		setAuthenticated(false);
	}

// 인증 완료 후의 객체 생성public UsernamePasswordAuthenticationToken(Object principal, Object credentials,
			Collection<? extends GrantedAuthority> authorities) {
		super(authorities);
		this.principal = principal;
		this.credentials = credentials;
		super.setAuthenticated(true);// must use super, as we override
	}
}

public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {
}

[ AuthenticationProvider ]

AuthenticationProvider에서는 실제 인증에 대한 부분을 처리하는데, 인증 전의 Authentication객체를 받아서 인증이 완료된 객체를 반환하는 역할을 한다. 아래와 같은 AuthenticationProvider 인터페이스를 구현해서 Custom한 AuthenticationProvider을 작성해서 AuthenticationManager에 등록하면 된다.

public interface AuthenticationProvider {

// 인증 전의 Authenticaion 객체를 받아서 인증된 Authentication 객체를 반환Authentication authenticate(Authentication var1) throws AuthenticationException;

    boolean supports(Class<?> var1);

}

[ Authentication Manager ]

인증에 대한 부분은 SpringSecurity의 AuthenticatonManager를 통해서 처리하게 되는데, 실질적으로는 AuthenticationManager에 등록된 AuthenticationProvider에 의해 처리된다. 인증이 성공하면 2번째 생성자를 이용해 인증이 성공한(isAuthenticated=true) 객체를 생성하여 Security Context에 저장한다. 그리고 인증 상태를 유지하기 위해 세션에 보관하며, 인증이 실패한 경우에는 AuthenticationException를 발생시킨다.

public interface AuthenticationManager {
	Authentication authenticate(Authentication authentication)
		throws AuthenticationException;
}

AuthenticationManager를 implements한 ProviderManager는 실제 인증 과정에 대한 로직을 가지고 있는 AuthenticaionProvider를 List로 가지고 있으며, ProividerManager는 for문을 통해 모든 provider를 조회하면서 authenticate 처리를 한다.

public class ProviderManager implements AuthenticationManager, MessageSourceAware,
InitializingBean {
    public List<AuthenticationProvider> getProviders() {
		return providers;
	}
    public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
		Class<? extends Authentication> toTest = authentication.getClass();
		AuthenticationException lastException = null;
		Authentication result = null;
		boolean debug = logger.isDebugEnabled();
//for문으로 모든 provider를 순회하여 처리하고 result가 나올 때까지 반복한다.for (AuthenticationProvider provider : getProviders()) {
            ....
			try {
				result = provider.authenticate(authentication);

				if (result != null) {
					copyDetails(authentication, result);
					break;
				}
			}
			catch (AccountStatusException e) {
				prepareException(e, authentication);
// SEC-546: Avoid polling additional providers if auth failure is due to// invalid account statusthrow e;
			}
            ....
		}
		throw lastException;
	}
}

위에서 설명한 ProviderManager에 우리가 직접 구현한 CustomAuthenticationProvider를 등록하는 방법은 WebSecurityConfigurerAdapter를 상속해 만든 SecurityConfig에서 할 수 있다. WebSecurityConfigurerAdapter의 상위 클래스에서는 AuthenticationManager를 가지고 있기 때문에 우리가 직접 만든 CustomAuthenticationProvider를 등록할 수 있다.

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public AuthenticationManager getAuthenticationManager() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public CustomAuthenticationProvider customAuthenticationProvider() throws Exception {
        return new CustomAuthenticationProvider();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(customAuthenticationProvider());
    }
}

[ UserDetails ]

인증에 성공하여 생성된 UserDetails 객체는 Authentication객체를 구현한 UsernamePasswordAuthenticationToken을 생성하기 위해 사용된다. UserDetails 인터페이스를 살펴보면 아래와 같이 정보를 반환하는 메소드를 가지고 있다. UserDetails 인터페이스의 경우 직접 개발한 UserVO 모델에 UserDetails를 implements하여 이를 처리하거나 UserDetailsVO에 UserDetails를 implements하여 처리할 수 있다.

public interface UserDetails extends Serializable {

    Collection<? extends GrantedAuthority> getAuthorities();

    String getPassword();

    String getUsername();

    boolean isAccountNonExpired();

    boolean isAccountNonLocked();

    boolean isCredentialsNonExpired();

    boolean isEnabled();

}

[ UserDetailsService ]

UserDetailsService 인터페이스는 UserDetails 객체를 반환하는 단 하나의 메소드를 가지고 있는데, 일반적으로 이를 구현한 클래스의 내부에 UserRepository를 주입받아 DB와 연결하여 처리한다. UserDetails 인터페이스는 아래와 같다.

public interface UserDetailsService {

    UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;

}

[ Password Encoding ]

AuthenticationManagerBuilder.userDetailsService().passwordEncoder() 를 통해 패스워드 암호화에 사용될 PasswordEncoder 구현체를 지정할 수 있다.

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// TODO Auto-generated method stub
	auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}

@Bean
public PasswordEncoder passwordEncoder(){
	return new BCryptPasswordEncoder();
}

[ GrantedAuthority ]

GrantAuthority는 현재 사용자(principal)가 가지고 있는 권한을 의미한다. ROLE_ADMIN나 ROLE_USER와 같이 ROLE_*의 형태로 사용하며, 보통 "roles" 이라고 한다. GrantedAuthority 객체는 UserDetailsService에 의해 불러올 수 있고, 특정 자원에 대한 권한이 있는지를 검사하여 접근 허용 여부를 결정한다.

부록

  • 전체 그림
  • 필터 순서

[ Spring Security 처리 과정 요약 ]

!https://blog.kakaocdn.net/dna/UOabX/btqEJBBNixH/AAAAAAAAAAAAAAAAAAAAAIZntjG0jp9VK7z0IxQXsiT1Pyd5pzJme_DM7llfp23e/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1756652399&allow_ip=&allow_referer=&signature=o9cbjr4U80rXjNrr6BopwmRyAYg%3D

  1. 사용자가 아이디 비밀번호로 로그인을 요청함
  2. AuthenticationFilter에서 UsernamePasswordAuthenticationToken을 생성하여 AuthenticaionManager에게 전달
  3. AuthenticaionManager는 등록된 AuthenticaionProvider(들)을 조회하여 인증을 요구함
  4. AuthenticaionProvider는 UserDetailsService를 통해 입력받은 아이디에 대한 사용자 정보를 DB에서 조회함
  5. 입력받은 비밀번호를 암호화하여 DB의 비밀번호화 매칭되는 경우 인증이 성공된 UsernameAuthenticationToken을 생성하여 AuthenticaionManager로 반환함
  6. AuthenticaionManager는 UsernameAuthenticaionToken을 AuthenticaionFilter로 전달함
  7. AuthenticationFilter는 전달받은 UsernameAuthenticationToken을 LoginSuccessHandler로 전송하고, SecurityContextHolder에 저장함

3. Spring Security 샘플 코드 및 실행


[ Spring Security 예제 실행 방법 ]

  1. https://github.com/MangKyu/SpringSecurity-Example/tree/formbased으로부터 소스를 클론받는다.
  2. CREATE DATABASE security DEFAULT CHARSET UTF8; 으로 데이터베이스를 생성한다.
  3. application.properties에서 DB username과 password를 개인에 맞게 변경해준다.
  4. 서버를 실행시킨다.
  5. http://localhost:8081/user/init 로 접속해서 사용자 데이터를 초기화 한다.
  6. index 페이지에 접속하여 실행한다.

Kafka 모니터링 여러 가지 많이 써봤는데

요거만한 게 없다

 

https://plugins.jetbrains.com/plugin/12494-big-data-tools

 

Big Data Tools - IntelliJ IDEs Plugin | Marketplace

A plugin for data engineers and other specialists engaged with big data workloads. Installed in your favorite JetBrains IDE, Big Data Tools helps develop, visualize...

plugins.jetbrains.com

 

IntelliJ 플러그인이다.

JetBrains 사에서 만든 플러그인이다

 

다운 받으면 요런 식으로 왼쪽이나 오른쪽 사이드바에 붙어있다.

\

들어가면 요렇게 보인다.

+ 버튼 누르면 이렇게 추가할 수 있다.

name 내 맘대로하고 URL 카프카 url로 넣으면 된다.

 

각 환경마다 추가할 수 있고,

등록 후 더블 클릭하면 아래처럼 토픽 목록이 Replicas, Partitions 등등의 정보와 같이 뜬다.

 

왼쪽 아래의 Producer와 Consumer 를 눌러 내가 메세지 publish, consume 해볼 수 있다.

 

'Kafka' 카테고리의 다른 글

Kafka 좀 더 알아보기  (0) 2023.04.17
Kafka 알아보기  (0) 2023.04.16

카프카가 자꾸 말썽을 부리고 있다.

왜일까...

공식 홈페이지의 기본 개념을 살짝 정리해보자.

https://kafka.apache.org/090/documentation.html#gettingStarted

잘못된 정보 수정은 환영입니다.

 

카프카는 topic을 통해 메시지 피드를 유지 관리한다.

topic producer를 통해 kafka cluster에 메시지를 적재한다.

consumer를 통해 kafka cluster에서 topic을 감지하고 메시지를 가져온다.

그림의 kafka cluster는 1개지만 그 이상의 서버로 구성될 수 있다.

 

cluster에 1개의 토픽에 관한 파티션을 살펴보면 위의 그림과 같다.

파티션을 1개로 놓는것도 가능하지만,
나눠 놓는것으로 메시지의 read와 write의 부하 분산을 줄여 성능 개선을 할 수 있다.

각 파티션의 메시지는 지속적으로 추가되고, 정렬 또는 변경이 불가능하다.

파티션 별로 메시지에 오프셋이라는 순차 ID가 부여된다.

메시지 유지 기간

cluster는 구성 가능 기간 동안 소비 여부에 관계없이 모든 메시지를 유지한다.

(보존 기간이 2일로 설정된 경우 메시지가 게시된 후 2일 동안 사용할 수 있고, 그 후에는 공간 확보를 위해 삭제한다.)

offset

각각의 consumer는 오프셋 번호를 가지고 있다.

오프셋은 소비자 마음대로 왔다 갔다 할 수 있으며,

그로 인해 처리했던 메시지를 다시 처리할 수 있다.

consumer의 오프셋 변화는 cluster 또는 producer에 영향을 미치지 않는다.

파티션

파티션은 1개의 topic에 대해 많은 양의 파티션으로 구성할 수 있고, 이것은 병렬 처리가 가능하다.

주의할 점은

kafka를 사용하면서 파티션을 늘릴 수는 있지만 줄일 수는 없다.

파티션을 줄이고 싶다면 토픽을 삭제하고 다시 만들어야 한다.

복사 전략

replication-factor 옵션에 따라 여러 cluster 서버에 데이터를 복제하여 안전하게 관리할 수 있다.

2로 설정한다면 원본과 복사본의 갯수를 합쳐서 2개의 데이터가 존재한다.

전략은 leader - follwers 방식으로 아래 사진을 참고할 수 있다.

 

leader-follwers 방식

클러스터하나의 클러스터 안에는 여러개의 브로커가 존재한다.
각 파티션에서 리더를 선출하여 팔로워들은 리더의 데이터를 복사한다.
리더가 죽게 되면 팔로워 중 하나를 리더로 선출한다.

앞에서 파티션을 늘려 부하 분산을 할 수 있다고 언급했는데,

무분별한 파티션 증설은 안 좋은 영향을 줄 수 있다.

이유는 위의 리더 선출과 복사 전략 때문인데,만약 Brooker101 이 다운된다면 Broker 102의 partition 0 은 일시적으로 사용할 수 없게 된다.
(새로운 리더 선출로부터 영향)

또한 다운된 브로커가 컨트롤러 역할을 수행하고 있었다면, 새로운 컨트롤러 선출을 위해서 모든 파티션의 데이터를 읽어야하기 때문에 더 큰 지연을 불러올 수 있다.

메시지 적제

producer는 topic 내의 어떤 파티션에 메시지를 할당할지 선택해야 한다.

방식은 메시지의 key 기반으로 분할되거나,(따로 공부하자)

key 값이 없다면 기본적으로 라운드 로빈 방식으로 분할된다.

메시지를 컨슘 할 때, 파티션 내부에서는 순서를 보장한다.

하지만 토픽 단위로 봤을 때 라운드로빈 기반으로 메시지를 저장했다면,

각각의 파티션끼리는 순서를 보장하지 않으니 주의하자.

메시지 소비

kafka는 소비자 그룹을 제공한다.

 

consumer에 group-id를 지정한다.

(C1과 C2는 A라는 group-id를 지정하여 하나의 그룹으로 본다.)

동일한 group-id를 가진 consumer 중 하나라도 메시지를 소비하면,

같은 그룹 내의 consumer들은 메시지를 소비하지 않고 넘어간다.

(C1과 C2는 1개의 topic에 대한 파티션처럼 동작한다)

'Kafka' 카테고리의 다른 글

Kafka 모니터링 추천  (0) 2023.04.18
Kafka 알아보기  (0) 2023.04.16

Kafka

오픈 소스 분산 이벤트 스트리밍 플랫폼입니다. 

 

kafka는 실시간으로 데이터 스트림을 수집하여 처리하기 위한 플랫폼으로, 메시지 스트리밍 데이터를 쓰고 읽게 해주는 게시(pub)-구독(sub)형 메시징 시스템입니다. 

 

Apache Kafka

 

Apache Kafka

Apache Kafka: A Distributed Streaming Platform.

kafka.apache.org

 

동작 원리

 

카프카는 3가지 컴포넌트로 구분됩니다.

Producer : kafka에 데이터 입력 클라이언트.
Broker : 중간 역할을 해줄 클러스터.
Consumer : kafka의 데이터를 가져오는 클라이언트.

producer는 이벤트(데이터, record, message)를 게시(쓰기)하는 클라이언트 애플리케이션이고,
consumer는 이벤트를 읽는 애플리케이션입니다.

브로커라는 클러스터에 파일 시스템의 폴더와 같은 ‘topic’을 생성하고,
파일처럼 이벤트를 생성하고 읽는 것이죠.

하지만 하나의 topic에 많은 producer와 consumer가 접근하게 되면, 부하가 걸리고 딜레이가 생길 수 있는데, 이러한 것들 때문에 topic은 n개의 partition으로 분산됩니다.

새 이벤트가 topic에 게시되면 실제로는 topic의 partition 중의 1개에 추가됩니다.

각각의 파티션은 append-only 방식으로 쌓여가는 로그 파일 형태입니다.

offset이라고 불리는 각각의 파티션 내 위치를 의미하는 식별자 정보를 가지고 이벤트를 쓰고 읽을 수 있습니다. 한번 쓰여진 이벤트는 영구적으로 저장됩니다(영속성 보장). 타 메세지 시스템과는 다르게, 읽는다고 삭제되는게 아니라 topic별 구성 설정을 통해 이벤트를 유지하는 기간을 정의하여 삭제 관리합니다.

 

각각의 파티션 내에선 순서가 보장되고, topic 내의 파티션 간의 순서는 보장되지 않습니다.

만약, 특정 이벤트들간의 작동 순서대로 읽고 싶다면, 이벤트 키(파티션 키)를 통해 동일한 파티션에 기록하게 하여 순서를 보장해야합니다.(없으면 라운드로빈 방식으로 파티션을 돌아가며 씀)

 

 

Consumer

컨슈머 그룹은 컨슈머들을 논리적으로 묶은 것입니다. 

컨슈머 그룹 내 리더 컨슈머가 해당 메시지를 누가 읽을건지 선정하고 나머지 컨슈머는 해당 메시지를 읽지 않습니다.

카프카의 특징

 

1) 카프카는 브로커라 불리는 하나 또는 그 이상의 서버들로 이루어진 클러스터로, 토픽의 파티션들을 여러 브로커에 배포합니다.
이로 인해, 하나의 토픽을 여러 consumer에 의해 동시 처리가 가능하고, 서로 다른 브로커에 있는 파티션에 접속함으로써 메시지 처리량을 높일 수 있습니다.

2) 카프카는 복사정책으로 여러 브로커에 동일 파티션의 복사본(replica)을 3개 이상 유지합니다.
이를 통해 1개의 브로커가 장애가 발생해도 복사본을 통해 지속적으로 메시지 제공이 가능합니다.

3) 카프카는 컨슈머 리더쪽에서 현재 구독중인 파티션 할당 정책을 담당합니다.
broker에서 관리안하고 컨슈머쪽에서 관리하는 이유는  broker를 재시작할 필요 없게 하여 유연성과 확장성을 가지게 됩니다.

 

 

'Kafka' 카테고리의 다른 글

Kafka 모니터링 추천  (0) 2023.04.18
Kafka 좀 더 알아보기  (0) 2023.04.17

Spring Security의 filter chain에 있는 필터들을 알아보자!

 


Spring Security의 기본필터를 알아보는 시리즈

 

Security 기본 필터는 뭐 있을까? 😗

스프링 시큐리티를 내 스프링 프로젝트에서 사용하고 있다면, 모두들 눈으로 보진 않았지만 뒤에서 돌아가고 있는 기본 필터를 알아보자 ! 스프링 공식 문서에서 안내하는 기본 필터는 아래와

lsmman.tistory.com


바로 들어가자 - doFilter

ChannelProcessingFilter 의 doFilter 부분을 가지고 왔다.

@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {
   HttpServletRequest request = (HttpServletRequest) req;
   HttpServletResponse response = (HttpServletResponse) res;
   FilterInvocation filterInvocation = new FilterInvocation(request, response, chain);
   Collection<ConfigAttribute> attributes = this.securityMetadataSource.getAttributes(filterInvocation);
   if (attributes != null) {
      this.logger.debug(LogMessage.format("Request: %s; ConfigAttributes: %s", filterInvocation, attributes));
      this.channelDecisionManager.decide(filterInvocation, attributes);
      if (filterInvocation.getResponse().isCommitted()) {
         return;
      }
   }
   chain.doFilter(request, response);
}

 

여기서 핵심은 channelDecisionManager.decide 이다.

this.channelDecisionManager.decide(filterInvocation, attributes);

 

ChannelDecisionManager 의 decide 의 구현체를 보면,

ANY_CHANNEL이라는 attribute가 포함되어 있으면 바로 리턴을 해주고,

ChannelProcessor가 지정되어 있으면 decide 메소드로 연결되어 있다.

 

ChannelProcessor가 무엇인가하면 프로토콜에 따라 어떻게 요청을 처리할 지 각 채널 (http, https 등) 처리를 관리하는 프로세서이다.

ChannelProcessor의 구현체는 2개 있는데 Spring으로 주로 web 요청을 처리하기 때문에,

InsecureChannelProcessor는 http를, SecureChannelProcessor는 https를 처리하는 걸로 알고 있으면 된다

 

조금 더 자세하게는 각 프로세스 안에서는 AbstractRetryEntryPoint의 commence라는 동작으로 연결되는데, 

port와 request 형태, EntryPoint를 잡아주는 동작을 하고 있고

AbstractRetryEntryPoint 구현체는 RetryWithHttpEntryPoint, RetryWithHttpsEntryPoint 2개 있다.


RetryWithHttpsEntryPoint는 이렇게 생겼다. 

 

실은 조사한 건 여기까지인데,

아쉬우니 chatGPT의 설명을 끝으로 마치려한다.

 ChannelProcessingFilter는 웹 애플리케이션에 대한 요청의 인증 및 승인을 처리하는 데 사용되는 Spring Security 프레임워크의 클래스입니다.

이 필터는 들어오는 요청을 검사하고 어떤 보안 속성이 적용되는지 결정한 다음 인증 및 권한 부여 프로세스를 Spring Security 프레임워크 내의 적절한 구성 요소에 위임하는 역할을 합니다.

ChannelProcessingFilter는 HTTPS와 같은 보안 채널이 필요한 요청을 처리하도록 특별히 설계되었으며 필요한 경우 요청을 보안 채널로 리디렉션할 수 있습니다. 일반적으로 AuthenticationFilter 및 AuthorizationFilter와 같은 다른 보안 필터보다 먼저 필터 체인의 초기에 배치됩니다.

전반적으로 ChannelProcessingFilter는 웹 애플리케이션에 대한 요청이 적절하게 인증 및 승인되고 민감한 정보가 네트워크를 통해 안전하게 전송되도록 하는 데 도움이 되는 Spring Security 프레임워크의 중요한 구성 요소입니다.

Bye!

 

ChannelProcessingFilter 웹 요청이 어떤 프로토콜로 (http 또는 https) 전달되어야 하는지 처리
SecurityContextPersistenceFilter SecurityContextRepository를 통해 SecurityContext를 Load/Save 처리
LogoutFilter 로그아웃 URL로 요청을 감시하여 매칭되는 요청이 있으면 해당 사용자를 로그아웃 시킴
UsernamePasswordAuthenticationFilter ID/비밀번호 기반 Form 인증 요청 URL(기본값: /login) 을 감시하여 사용자를 인증함
DefaultLoginPageGeneratingFilter 로그인을 수행하는데 필요한 HTML을 생성함
RequestCacheAwareFilter 로그인 성공 이후 인증 요청에 의해 가로채어진 사용자의 원래 요청으로 이동하기 위해 사용됨
SecurityContextHolderAwareRequestFilter 서블릿 3 API 지원을 위해 HttpServletRequest를 HttpServletRequestWrapper 하위 클래스로 감쌈
RememberMeAuthenticationFilter 요청의 일부로 remeber-me 쿠키 제공 여부를 확인하고, 쿠키가 있으면 사용자 인증을 시도함
AnonymousAuthenticationFilter 해당 인증 필터에 도달할때까지 사용자가 아직 인증되지 않았다면, 익명 사용자로 처리하도록 함
ExceptionTranslationFilter 요청을 처리하는 도중 발생할 수 있는 예외에 대한 라우팅과 위임을 처리함
FilterSecurityInterceptor 접근 권한 확인을 위해 요청을 AccessDecisionManager로 위임

 

'Java Spring > Spring Security' 카테고리의 다른 글

[Big Picture] Spring Security  (0) 2023.06.21
Security 기본 필터는 뭐 있을까? 😗  (0) 2023.04.14
스프링 시큐리티를 내 스프링 프로젝트에서 사용하고 있다면, 모두들 체감은 안되지만 저절로 사용되고 있는 기본 필터를 알아보자 !

Spring Security Architecture

기본 구조
  • Spring Security는 애플리케이션의 보안에 핵심적인 "인증"과 "인가"를 Filter와 annotation을 사용해서 손쉽게 구현할 수 있도록 도와준다.

AuthenticationManager : 사용자 인증 관련 처리
AccessDecisionManager : 사용자가 보호받는 리소스에 접근할 수 있는 적절한 권한이 있는지 확인 

FilterChainProxy


Spring Security는 서블릿 필터 javax.servlet.Filter의 구현체로 동작한다.
@EnableWebSecurity 어노테이션과 함께 WebSecurityConfigurerAdapter 추상 클래스를 상속받아 구현함
웹 요청은 이러한 필터 체인을 차례로 통과하게 됨 보통 SpringSecurityFilterChain 이라는 이름으로 Bean 등록

 

필터 체인에 Custom Filter를 추가해서 쓰는 일도 많지만 기본적으로 설정하지 않아도 작동되는 많은 필터들에 대해 오늘 알아보려 한다.

 

스프링 공식 문서에서 안내하는 기본 필터는 아래와 같다.

영어 주의

 

Filter Ordering

The order that filters are defined in the chain is very important. Irrespective of which filters you are actually using, the order should be as follows:

  1. ChannelProcessingFilter, because it might need to redirect to a different protocol
  2. SecurityContextPersistenceFilter, so a SecurityContext can be set up in the SecurityContextHolder at the beginning of a web request, and any changes to the SecurityContext can be copied to the HttpSession when the web request ends (ready for use with the next web request)
  3. ConcurrentSessionFilter, because it uses the SecurityContextHolder functionality but needs to update the SessionRegistry to reflect ongoing requests from the principal
  4. Authentication processing mechanisms - UsernamePasswordAuthenticationFilter, CasAuthenticationFilter, BasicAuthenticationFilter etc - so that the SecurityContextHolder can be modified to contain a valid Authentication request token
  5. The SecurityContextHolderAwareRequestFilter, if you are using it to install a Spring Security aware HttpServletRequestWrapper into your servlet container
  6. RememberMeAuthenticationFilter, so that if no earlier authentication processing mechanism updated the SecurityContextHolder, and the request presents a cookie that enables remember-me services to take place, a suitable remembered Authentication object will be put there
  7. AnonymousAuthenticationFilter, so that if no earlier authentication processing mechanism updated the SecurityContextHolder, an anonymous Authentication object will be put there
  8. ExceptionTranslationFilter, to catch any Spring Security exceptions so that either an HTTP error response can be returned or an appropriate AuthenticationEntryPoint can be launched
  9. FilterSecurityInterceptor, to protect web URIs and raise exceptions when access is denied

 

물론 Spring Security의 `FilterOrderRegistration` 클래스를 보면 실제 spring security에서 사용하는 Filter Chain을 확인할 수 있다.

 

- FilterOrderRegistration에 등록된 필터 체인 - 코드로 보기

더보기
FilterOrderRegistration() {
   Step order = new Step(INITIAL_ORDER, ORDER_STEP);
   put(ChannelProcessingFilter.class, order.next());
   order.next(); // gh-8105
   put(WebAsyncManagerIntegrationFilter.class, order.next());
   put(SecurityContextPersistenceFilter.class, order.next());
   put(HeaderWriterFilter.class, order.next());
   put(CorsFilter.class, order.next());
   put(CsrfFilter.class, order.next());
   put(LogoutFilter.class, order.next());
   this.filterToOrder.put(
         "org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter",
         order.next());
   this.filterToOrder.put(
         "org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationRequestFilter",
         order.next());
   put(X509AuthenticationFilter.class, order.next());
   put(AbstractPreAuthenticatedProcessingFilter.class, order.next());
   this.filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter", order.next());
   this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter",
         order.next());
   this.filterToOrder.put(
         "org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter",
         order.next());
   put(UsernamePasswordAuthenticationFilter.class, order.next());
   order.next(); // gh-8105
   this.filterToOrder.put("org.springframework.security.openid.OpenIDAuthenticationFilter", order.next());
   put(DefaultLoginPageGeneratingFilter.class, order.next());
   put(DefaultLogoutPageGeneratingFilter.class, order.next());
   put(ConcurrentSessionFilter.class, order.next());
   put(DigestAuthenticationFilter.class, order.next());
   this.filterToOrder.put(
         "org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter",
         order.next());
   put(BasicAuthenticationFilter.class, order.next());
   put(RequestCacheAwareFilter.class, order.next());
   put(SecurityContextHolderAwareRequestFilter.class, order.next());
   put(JaasApiIntegrationFilter.class, order.next());
   put(RememberMeAuthenticationFilter.class, order.next());
   put(AnonymousAuthenticationFilter.class, order.next());
   this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter",
         order.next());
   put(SessionManagementFilter.class, order.next());
   put(ExceptionTranslationFilter.class, order.next());
   put(FilterSecurityInterceptor.class, order.next());
   put(AuthorizationFilter.class, order.next());
   put(SwitchUserFilter.class, order.next());
}

 

여튼 그렇다. 공식 문서에 나온 것부터 하나씩 알아보자

 

필터들

1. ChannelProcessingFilter 🔗

2. SecurityContextPersistenceFilter

3. ConcurrentSessionFilter

4. UsernamePasswordAuthenticationFilter

5. SecurityContextHolderAwareRequestFilter

6. RememberMeAuthenticationFilter

7. AnonymousAuthenticationFilter

8. ExceptionTranslationFilter

9. FilterSecurityInterceptor

 

 

참고

- https://docs.spring.io/spring-security/reference/index.html

- https://hanjo8813.github.io/til/42/

'Java Spring > Spring Security' 카테고리의 다른 글

[Big Picture] Spring Security  (0) 2023.06.21
[Spring-Security filter] 1. ChannelProcessingFilter  (0) 2023.04.14

고객에 따른 분류

플랫폼 (서로 다른 롤의 B2C, B2B가 혼합되어 거래활동이 일어나는 형태), B2C, B2B

 

프로덕트가 제공되는 형태에 따라 분류

 

https://brainhub.eu/library/cloud-architecture-saas-faas-xaas

 

Cloud: IaaS vs PaaS vs SaaS vs DaaS vs FaaS vs DBaaS

Learn what are the major types of backend architecture, and compare IaaS vs SaaS vs DaaS.

brainhub.eu

 

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=doonibest&logNo=220861308003 

 

IaaS, PaaS, SaaS, BaaS, FaaS

IaaS, PaaS, SaaS, Baas, Faas 클라우드 IaaS (Infrastructure as a Service) -- 인프라 스트...

blog.naver.com

 

그리고.

정보 제공을 위한 소프트웨어, 사람과 사람을 연결하는 형태의 소프트웨어

+ Recent posts