Docker 이미지를 다운 받아  빠르게 설치

docker pull centos:latest

Docker 실행 

docker run -d -it -p 9991:9991 -p 9992:9992 -p 9993:9993 -p 7060:7060 --name pinpoint centos:latest

Docker 접속

docker exec -it pinpoint /bin/bash

cd /home/pinpoint

Collector 실행

nohup java -jar -Dserver.port=8081 -Dpinpoint.zookeeper.address=localhost pinpoint-collector-boot-2.3.0.jar >/dev/null 2>&1 &

Web 실행

nohup java -jar -Dserver.port=7060 -Dpinpoint.zookeeper.address=localhost pinpoint-web-boot-2.3.0.jar >/dev/null 2>&1 &

Hbase 테이블 생성 

wget https://raw.githubusercontent.com/pinpoint-apm/pinpoint/master/hbase/scripts/hbase-create.hbase

hbase/bin/hbase shell hbase-create.hbase

 

옵션) 추가적으로 보존 기간을 변경하고 싶다면 다음을 참조

교착 상태에 빠지지 않게 하기 위해 비관점 잠금 타임아웃을 설정할 수 있다. 

Map<String, Object> hints = new HashMap<>();
hints.put("javax.persistence.lock.timeout", 2000);
Order order = entityManager.find(Order.class, LockModeType.PESSIMISTIC_WRITE, hints);

 

@Lock(LockModeType.PESSIMISTIC_WRITE)
@QueryHints({
	@QueryHint(name="javax.persistence.lock.timeout", value="2000" )
})
@Query("select m from Member where m.id = :id")
Optional<Member> findByIdForUpdate(@Param("id") MemberId memberId);

 

조금이라도 정적인 자원들에 대해서 빨리 로딩하는 방법이 없을까 고민하고 있었는데  우연히 정적 리소스에 대한 cache와 compress를 보았다. 그래서 한 번 테스트 해볼까 한다. 

Cache 

application.json 에 아래 내용을 추가한다. (초단위 60x60x10 = 10시간) -> 테스트시간임

spring.resources.cache.period=36000

처음 호출 

처음 호출했을 경우 200과 함께 사이즈 시간을 볼 수있다. 

새로고침 을 누르고 다시 조회 보면 304 와 함께 max-age를 통해 캐쉬가 됨을 확인

사이즈와 시간이 줄어듬을 확인

처음 호출 이후에 지정된 시간까지 캐쉬를 통해 절약할 수 있음 알았다. 

자바소스에서는 WebMvcConfigurer 를 implements 하여 쓸 수 있다. 

registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/")
                .setCachePeriod(36000)
        ;

Compress

압축을 해보자 . application.json 에 다음을 추가하자

server.compression.enabled=true
server.compression.mime-types=text/html,text/xml,text/plain,text/css,text/javascript,application/javascript
server.compression.min-response-size=2048

그리고 호출 

압축 안함
압축 이후에 시간 사이즈와 시간 비교이다 
gzip으로 압축됨을 표시

네트워크 환경에 따라서 속도는 다르게 나올 수 있다 하지만 사이즈가 엄청 크거나 로딩이 느리다면

고민해서 활용을 해보면 좋을 것 같다

@Bean 
UiConfiguration uiConfig() { 
	return UiConfigurationBuilder.builder() 
    .docExpansion(DocExpansion.LIST) // or DocExpansion.NONE or DocExpansion.FULL 
    .build(); 
}

source : http://springfox.github.io/springfox/docs/current/#springfox-swagger2-with-spring-mvc-and-spring-boot

 

Springfox Reference Documentation

The Springfox suite of java libraries are all about automating the generation of machine and human readable specifications for JSON APIs written using the spring family of projects. Springfox works by examining an application, once, at runtime to infer API

springfox.github.io

swagger (2.6.1)버전이 다른 경우 

@Bean
public UiConfiguration uiConfig() {
	return new UiConfiguration(
                null,
                "list",
                "alpha",
                "schema",
                UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS,
                false,
                true,
                null
        );
}

 

서비스에 어노테이션으로 aop를 조정할 수 있다. 만약 서비스에서 로그인 관련 체크를 한다면 어노테이션을 선언하고 메서드에 어노테이션을 선언하는 것 만으로 aop 를 적용할 수 있다.

1. 인터페이스 선언 

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SecurityCheck {
}

타겟을 메서드에 준다

2. 체크할 포인트를 선언 

@Aspect
@Component
public class SecurityChecker {

  private static final Logger logger = LoggerFactory.getLogger(SecurityChecker.class);

  @Pointcut("@annotation(SecurityCheck)")
  public void checkMethodSecurity() {}

  @Around("checkMethodSecurity()")
  public Object checkSecurity (ProceedingJoinPoint joinPoint) throws Throwable {
    logger.debug("Checking method security...");
    // TODO Implement security check logics here
    Object result = joinPoint.proceed();
    return result;
  }
}

TODO 부분에 실제로 체크해야할 내용을 삽입한다.

3. 서비스 메서드에 적용 

  @SecurityCheck
  @Transactional(noRollbackFor = { UnsupportedOperationException.class })
  public Message save(String text)
  1. HttpServletRequest.getUserPrincipal() 으로 현재 사용자 정보를 가져올 수 있다.  
  2. SecurityContextHolder로 SecurityContextHolder.getContext().getAuthentication() 
  3. 스프링 시큐리트의 @AuthenticationPrincipal이 적용된 어노테이션을 생성할 있다. 그리고 스프링에 UserDetails 구현체를 API 핸들러 메소드에 주입하여 요청

3번의 예제

@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal
public @interface CurrentUser {
}

Controller 에서 변환하여 사용

public ResponseEntity<ApiResult> createBoard(
            @RequestBody CreateBoardPayload payload,
            @CurrentUser SimpleUser currentUser) {
...
spring:
  datasource:
    url: jdbc:h2:tcp://localhost/~/task_agile_test
    username: sa
    password:
    driver-class-name: org.h2.Driver

  jpa:
    database-platform: org.hibernate.dialect.H2Dialect
    open-in-view: false
    hibernate:
      ddl-auto: create-drop
    properties:
      hibernate:
        show_sql: true
        format_sql: true
        
logging:
  level:
    org.hibernate.type.descriptor.sql: trace
  • spring.datasource : DB 연결정보 
  • spring.jpa.database-platform : 데이터 플래폼을 지정, 참고 
  • spring.jpa.open-in-view : osiv 웹요청이 완료될때까지 영속성을 가짐 (성능상 안 좋음)
  • spring.jpa.hibernate.ddl-auto :
    • none : No action is performed. The schema will not be generated.
    • create : create the schema, the data previously present (if there) in the schema is lost
    • create-drop : (default) create the schema with destroying the data previously present(if there). It also drop the database schema when the SessionFactory is closed.
    • update : update the schema with the given values.
    • validate : validate the schema. It makes no change in the DB.
  • spring.jpa.properties.hibernate.format_sql : 실행쿼리를 가독성있게 표현
  • spring.jpa.properties.hibernate.show-sql : 콘솔에 jpa 실행쿼리
  • logging.level.org.hibernate.type.descriptor.sql : ? 부문을 로그로 출력해줌

Before 어드바이스는 스프링이 제공하는 가장 유용한 어드바이스 중 하나다. Before 어드바이스는 메서드로 넘겨주는 인자를 수정할 수 있으며 메서드를 실행하기 전에 예외를 발생시켜서 실행을 막을 수도 있다.

import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;

import java.lang.reflect.Method;

public class SimpleBeforeAdvice  implements MethodBeforeAdvice {

    public static void main(String[] args) {
        MessageWriter target = new MessageWriter();

        // create the proxy
        ProxyFactory pf = new ProxyFactory();

        pf.addAdvice(new SimpleBeforeAdvice());
        pf.setTarget(target);

        MessageWriter proxy = (MessageWriter) pf.getProxy();

        // write the messages
        proxy.writeMessage();
    }

    public void before(Method method, Object[] args, Object target)
            throws Throwable {
        System.out.println("Before method: " + method.getName());
    }

}

결과

Before method : writeMessage
World


Before 어드바이스를 사용하여 메서드 접근 보안하기

SecureBean클래스 - 보안처리를 할 클래스

package com.lastjava.spring.ch05.security;

public class SecureBean {
 public void writeSecureMessage() {
        System.out.println("Every time I learn something new, "
                + "it pushes some old stuff out of my brain");
    }
}

UserInfo클래스 - 사용자 정보를 저장

package com.lastjava.spring.ch05.security;

public class UserInfo {
 private String username;
    private String password;
   
    public UserInfo(String username, String password) {
        this.username = username;
        this.password = password;
    }
   
    public String getPassword() {
        return password;
    }

    public String getUsername() {
        return username;
    }
}

SecurityManager클래스 - 사용자 인증을 담당하고 그들의 계정 정보를 담아뒸더가 나중에 참조

package com.lastjava.spring.ch05.security;

public class SecurityManager {

    private static ThreadLocal<UserInfo> threadLocal = new ThreadLocal<UserInfo>();

    public void login(String username, String password) {
        // assumes that all credentials
        // are valid for a login
        threadLocal.set(new UserInfo(username, password));
    }

    public void logout() {
        threadLocal.set(null);
    }
   
    public UserInfo getLoggedOnUser() {
        return threadLocal.get();
    }
}

SecurityAdvice클래스 - 해당 메서드 접근전에 실행 메서드실행을 예외처리로 방지한다.

package com.lastjava.spring.ch05.security;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class SecurityAdvice implements MethodBeforeAdvice {

    private SecurityManager securityManager;

    public SecurityAdvice() {
        this.securityManager = new SecurityManager();
    }

    public void before(Method method, Object[] args, Object target)
            throws Throwable {
        UserInfo user = securityManager.getLoggedOnUser();

        if (user == null) {
            System.out.println("No user authenticated");
            throw new SecurityException(
                    "You must login before attempting to invoke the method: "
                            + method.getName());
        } else if ("janm".equals(user.getUsername())) {
            System.out.println("Logged in user is janm - OKAY!");
        } else {
            System.out.println("Logged in user is " + user.getUsername()
                    + " NOT GOOD :(");
            throw new SecurityException("User " + user.getUsername()
                    + " is not allowed access to method " + method.getName());
        }

    }
}

SecurityExample 클래스

package com.lastjava.spring.ch05.security;

import org.springframework.aop.framework.ProxyFactory;

public class SecurityExample {
 public static void main(String[] args) {
        // get the security manager
        SecurityManager mgr = new SecurityManager();

        // get the bean
        SecureBean bean = getSecureBean();

        // try as robh
        mgr.login("janm", "*****");
        bean.writeSecureMessage();
        mgr.logout();

        // try as janm
        try {
            mgr.login("mallory", "****");
            bean.writeSecureMessage();
        } catch(SecurityException ex) {
            System.out.println("Exception Caught: " + ex.getMessage());
        } finally {
            mgr.logout();
        }

        // try with no credentials
        try {
            bean.writeSecureMessage();
        } catch(SecurityException ex) {
            System.out.println("Exception Caught: " + ex.getMessage());
        }

    }

     private static SecureBean getSecureBean() {
        // create the target
        SecureBean target = new SecureBean();

        // create the advice
        SecurityAdvice advice = new SecurityAdvice();

        // get the proxy
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvice(advice);

        return (SecureBean)factory.getProxy();

    }
}

결과

Logged in user is janm - OKAY!
Every time I learn something new, it pushes some old stuff out of my brain
Logged in user is mallory NOT GOOD :(
Exception Caught: User mallory is not allowed access to method writeSecureMessage
No user authenticated
Exception Caught: You must login before attempting to invoke the method: writeSecureMessage


 

+ Recent posts