2020년 6월 29일 월요일

springboot 에 swagger 설치해서 api 확인하자

swaggerConfig.java (이름이야뭐..아무거나..) 를 만들어서

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration@EnableSwagger2public class SwaggerConfig {

@Bean public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any()) //모든 url 리스트 찾아내줘 스웍 .paths(PathSelectors.any()) // 모든 패스 전부. .build();
}
}
이것만으로 잘 안되서..


@Overridepublic void addViewControllers(ViewControllerRegistry registry) {
registry.addRedirectViewController("/api/v2/api-docs", "/v2/api-docs");
registry.addRedirectViewController("/api/swagger-resources/configuration/ui", "/swagger-resources/configuration/ui");
registry.addRedirectViewController("/api/swagger-resources/configuration/security", "/swagger-resources/configuration/security");
registry.addRedirectViewController("/api/swagger-resources", "/swagger-resources");
}

@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/api/swagger-ui.html**").addResourceLocations("classpath:/META-INF/resources/swagger-ui.html");
registry.addResourceHandler("/api/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
...
}

했더니 localhost:8080/api/swagger-ui.html 에서 보게 되었다. 
http://localhost:8081/swagger-api/swagger-ui.html#/

오랜만에 cocos2d 폴더를 정리하다가 맥에서 실행해볼까 했더니, 에러가 났다. Cocos2dx compilation problem: Argument value 10880 is outside the valid range [0, 255] btVector3.h

코코스원본소스를 풀어놓은곳(현재 생성되어 복제본이 있는곳이 아니고!) 에 external/bullet/include/bullet/LinearMath/btVerctor3.h 놈이 있다. 이놈의 40 라인 근처에서 아래대로 수정해주면된다.

또 맥에서는 아이폰용으로 컴파일 끝자리에 (The application's Info.plist does not contain CFBundleShortVersionString.)가 뜬다.
cocos2dx 3.x 가 옛날꺼라 ios6 으로 지정되어있는데 일단 그걸 8또는그이상으로 한다.
project->build setting에서 CFBundleShortVersionString 로 검색해서 버전(예,1.0.0) 을 넣어준다. 그리고 재빌드한다.(엿)

http://theeye.pe.kr/archives/2475


https://stackoverflow.com/questions/58064487/xcode-11-cocos2dx-compilation-problem-argument-value-10880-is-outside-the-vali

2020년 6월 28일 일요일

cocos2dx 4.0 에서 mac 에서 cocos run 할때 cmake 오류나면

일단 cmake 를 dmg 로 설치.

https://stackoverflow.com/questions/23849962/cmake-installer-for-mac-fails-to-create-usr-bin-symlinks

여기 있는것처럼

sudo /Applications/CMake.app/Contents/MacOS/CMake
실행후 Tools > How to install command ... 를 선택하고 
sudo "/Applications/CMake.app/Contents/bin/cmake-gui" --install
하면 됨.
cocos run 해보면 잘됨.
첨엔 오래걸림

2020년 6월 27일 토요일

Spring Cloud Zuul

스프링부트 클라우드 + 넥플릭스 덕분에 마이크로서비스 구축이 이렇게나 재밌을수가..

Spring Cloud Zuul
일단 API GATEWAY로서 라우팅, 엔드포인트제공, 필터링, 로깅-모니터링은 기본이고 인증, 로드밸런싱, 장애대응 까지 소프트웨어적으로 가능하게 만들어 두었다니, 사랑해요 넥플릭스 개발자들.

일단 가장 쓰기편한 조합은 Eureka 와 연동하는것. Zuul 에는 Hystrix(Circuit Breaker), Ribbon(Load Balancer-round robin type) 이 탑재되어 있다.

1.Spring Cloud Zuul 설정
부트초기화 사이트에서 zuul 하고 eureka client 추가해서 프로젝트 생성.

- application.yml 생성
server:
port: 8100 <- 암거나

spring:
application:
name: ${NAME:zuul} <- 유레카 서버에서 확인할 이름
zuul:
routes:
service-1:
path: /ping
serviceId: customerservice <- 이거는 유레카 클라이언트의 이름과 동일해야 유레카가 해당이름의 서버를 찾아 준다.
service-2:
path: /fuckapple
serviceId: customerservice2 <- 뻑은 다른서버

eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: ${EUREKA_URL:http://127.0.0.1:8761/eureka/}, http://192.168.0.103:8671/eureka/
추가로 hystrix, ribon 설정을 서비스 별로 지정할수 있다.
hystrix : https://blog.naver.com/sharplee7/221503517533 <- 상세히 잘되어있음
ribon : https://blog.naver.com/sharplee7/221505552280 <- 상세히 잘되어있음

참고 떙큐

Spring boot Eureka 해보자

Spring boot Cloud Eureka 

마이크로서비스에서 다수의 서버들에 대한 정보를 관리하는게 필요하다. 유레카가 그거다. 유레카서버를 두고 각 클라이언트들이 유레카서버에 등록하면 된다.
스프링부트에서는 의존성추가해서 서버-클라이언트 지정하면 끝. 노드제이에스 같은경우 라이브러리로 가능.

유레카 서버는 음..그냥 현재 클라우드에 구성된 하위 마이크로 서비스들에 대한 구성값을 저장할 뿐이다. 사용자의 요청에 따른 라우팅 , 또는 로드밸런싱을 위해서는 Zuul 서비스를 연동한다.

유레카 서버는 30초마다 등록된 클라이언트에 핑을 보내서 이용가능한 서비스인지 확인하다. 로드밸런싱을 위해 리본(클라이언트 로드밸런서?) 를 사용한다.

1. Spring boot Cloud Eureka Server
의존성과 포트 설정만 지정하면 일단 서버 기동은 끝. 스프링부트 정말 좋다.
- spring initializr 사이트에서 eureka 의존성 추가해서 프로젝트 생성, web도 추가.
다른언어로 개발됬거나 별도로 디스커버리서버(유레카 서버가 들고 있는정보) 가 필요할때는 제공되는 유레카 API를 이용한다.
( /eureka/apps, euraka/instance 등등..)

implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'

- application.yml 에 서버 설정
server:
port: 8761 <-- 포트는 맘대로 보통 8761 인가보다

# -- Eureka
eureka:
instance:
hostname: 127.0.0.1
client:
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
register-with-eureka: false <-- 서버자신이 유레카에 등록할지를 결정, 지가 서버이면서 클라이언트도 가능
fetch-registry: false <-- 클라이언트들이 로컬에 서버로부터 받은 클라이언트리스트정보를 로컬에 캐싱할지 결정 server: enable-self-preservation: false <-- true일때는 클라이언트가 죽어도 서버에서 죽지 않은걸로 인식, 보통 false 다

- application에 유레카 서버라고 설정
@EnableEurekaServer
@SpringBootApplication
public class DemoApplication {

public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}

}
이제 서버주소만 입력해도 정보가 출력된다. 유레카!
** resource 밑에 static, template가 있으면 화이트 지랄 화면이 보인다. 폴더 지우고 리빌드 하고 실행.
여러대의 서버에서 구성하고자 할떄는
spring:
profiles: default
application:
name: eureka-server-clustered
server:
port: 8761
eureka:
instance:
hostname: 192.168.0.103
client:
registerWithEureka: true
fetchRegistry: true
serviceUrl:
defaultZone: http://192.168.0.106:8761/eureka/,http://192.168.0.107:8761/eureka/
식으로 디폴트존을 여러개(서로 얽히게끔) 둔다. 일단 peer-to-peer 라고 서버상태를 전부 복제하는 방식이다.
https://projects.spring.io/spring-cloud/spring-cloud.html#_peer_awareness



2. Spring boot Cloud Eureka Client
유레카 클라이언트 의존성을 추가하고 actuator 도 의존성 추가필요. 액튜에이터가 있어야 서버가 클라이언트의 상태를 파악하지.
유레가 서버가 heartbeat 를 보내주는데, 클라이언트가 이를 못받으면 유레카서버는 클라이언트를 삭제한다.
클라이언트는 서버로부터 레지스트리(클라이언트목록)을 캐싱하고 주기적으로 변경사항을 점검한다
- 일단 의존성 추가
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.springframework.boot:spring-boot-starter-actuator'

- application.yml 생성
server:
  port: ${PORT:8081}

spring:
  application:
    name: ${NAME:customerservice} <-- 같은 서비스를 여러개(여러서버)를 두고 서비스하고 싶을떄는 이름을 같게 하면
유레카에서 같은 이름끼리 묶어서 표시한다. 이를 zuul등에서 같은 service-id (serviceId: customerservice) 로 지정하면 라운드로빈 방식으로 서버를 돌아가며 요청한다. 따라서 같은 이름의 서비스서버중 1번 서버에는 출력'가'로 되는데, 2번 서버에는 출력'나'로 되어있으면 가->나->가->나..순으로 데이터가 표시된다.

eureka:
  client:
    serviceUrl:
    defaultZone: ${EUREKA_URL:http://127.0.0.1:8761/eureka/}
    enabled: true
  instance:
    preferIpAddress: true

- 애플리케이션이 유레카 클라이언트라고 지정
@EnableDiscoveryClient
@SpringBootApplication
public class EurekaclientApplication {

public static void main(String[] args) {
SpringApplication.run(EurekaclientApplication.class, args);
}

}
- 이제 유레카 서버페이지에 접속해서 새로고침하면 실행중인 클라이언트 목록이 보인다. 클라이언트의 스타터스를 클릭하면 클라이언트의 액튜에이터 정보페이지가 열린다.
- java -jar -DPORT=8082 -DNAME=taeho  eurekaclient-0.0.1-SNAPSHOT.jar


참고 떙큐
https://projects.spring.io/spring-cloud/spring-cloud.html#_peer_awareness
https://lion-king.tistory.com/12?category=855644
https://coe.gitbook.io/guide/service-discovery/eureka 
https://develop-yyg.tistory.com/5
https://jinhyy.tistory.com/52 <-- 위에게시물을 복불한곳 , 개념이 없는건지....

2020년 6월 26일 금요일

Spring Boot Cloud Config 해보기

Spring Boot Cloud Config 로 분산시스템에서 설정파일을 외부로 분리 해보자
스프링 뿐만 아니라 , 다른 언어로 개발된 외부서버에서도 동일한 설정값을 적용할때 유용하다.

예를들어, 스프링부트 애플리케이션 , 노드제이에스애플리케이션 으로 구성된 프로젝트에서 개발할때는 전부 테스트용 디비에 접속했다가, 프로덕션때는 실서버로 설정파일을 바꾸고자 할때, 스프링 클라우드 콘픽 서버에서 설정값만 바꾸면 두 애플케이션에 동시에 적용되게 한다든지가 가능하다.


일단 로컬에 있는 설정파일을 이용하여 콘픽서버를 구성해보자.

1. Spring Boot Cloud Config Server
누군가는 콘픽설정을 가지고 있어야 되니까 서버가 필요하다.
- spring init 사이트에서 의존성 추가해서 웹어플로 하나 만들자.
- 의존성 : implementation 'org.springframework.cloud:spring-cloud-config-server'
- 설정파일을 로컬에 보관하고 서버에서 그 파일을 사용하겠다고 지정
  -  Config Server 는 application.yml 보다 bootstrap.yml 을 먼저 읽어야하니까 해당화일을 만든다 (또는 bootstrap-local.yml , bootstrap-prod.yml 으로 구분가능)
  - 내용은 일단 간단하게 입력
server:
port: 9000 <- 포트는 아무거나
spring:
profiles:
active: native <- 이게 로컬파일 쓰겠다는 것
cloud:
config:
server:
native:
search-locations: file://${user.home}/springcloud_config <- 콘픽파일들이 들어있는 폴더위치를
지정하면 클라이언트에서 요청한 콘픽파일을 이 폴더에서 찾아서 돌려준다
  - 사용자폴더안에 springcloud_config 폴더를 만들고 파일들을 넣어준다. 내용은 아무거나 좋다.
   단 파일이름사이에 - 표시로 요청 url 가 결정된다.
springcloud_config
 - bootstrap-test1.yml
 - bootstrap-test2.yml
  폴더와 파일들을 만들고
 - bootstrap-test1.yml 파일에는
spring:
  profiles: test1
  message: Hello Spring MemberService Test1 Server!
라고 만들었다.
  - 이제 서버를 기동하고 http://localhost:9000/bootstrap/test1 , 또는 다른컴에서 http://xx,xx,xx,xx:9000/bootstrap/test1 라고 입력하면 json 형태로 설정파일의 데이터가 출력된다.


2. Spring Boot Cloud Config Client
서버에서 Json 형태로 출력이 되니 서버주소만 입력하고 제이손 파싱만하면된다.
같은 스프링부트 프로젝트니까 의존성추가하고 bootstrap.yml 에 콘픽서버놈 설정하고 애플리케이션의 에서 설정속성을 읽어내면 된다. 
기존의 application.yml  에서 읽어 오는걸 콘픽서버에서 읽어 들인다고 생각하면된다.
의존성
implementation 'org.springframework.cloud:spring-cloud-starter-config'
implementation 'org.springframework.boot:spring-boot-starter-actuator' <-- 요놈을 추가하면 서버에서 콘픽설정 
정보가 바뀔때 클라이언트 에서 서버의 재기동없이도 해당값이 적용되도록 하는게 가능하다. 밑의 콘트롤러에서 @RereshScope 를 사용할때 설명
bootstrap.yml 파일
server:
port: 8081

spring:
application:
name: bootstrap
profiles:
active: test1
cloud:
config:
uri: http://localhost:9000
콘트롤러
@RestController
@RefreshScope <-- actuator 의존성이 되어 있다면 이 어노테이션을 설정가능하다. 이놈이 붙어있으면 서버의 설정파일에 변경사항이 있을경우 클라이언트에서 POST http://클라이언트/actuator/refresh 요청하여 설정파일의 갱신이 가능하다.
public class HmmConfigClientController {

@Value("${spring.message}")
private String identify;

@GetMapping("/hi")
public String test() {
return identify;
}

}
지금은 http://localhost:8081/hi 하면 화면에 Hello Spring MemberService Local Server22233!!!!! 가 출력됨.
근데, 서버의 bootstrap-test1.yml 의 내용이 변경되었음
spring:
  profiles: test1
  message: Xuck  Apple !!!!!
하지만 아직 test1 설정을 읽어들인 클라이언트들은 예전메시지 나옴
자, 이제 외부에서 POST 로 클라이언트 서버에 actuator/refresh  해서 콘픽서버의 변경된 설정을 반영해보자.
사실은 콘픽 서버가 변경될떄, 각 클라이언트의 actuator/refresh 를 적용하도록 하는게 맞는거다. 유레카 서비스연동한다면 
등록되어 있는 클라리언트를 찾아서 반영하는것도 가능하다.
여튼. 간단히 curl -X POST  http://localhost:8081/actuator/refresh 하면 클라이언트는 콘픽서버로부터 새로운 
설정값을 가져온다.

지금은 http://localhost:8081/hi 하면 화면에 Xuck  Apple !!!!!
이 나옴. 또는 클라이언트 재가동 하든가. 

일단 콘픽 서버는 json 형태로 출력하니까 Json 파싱이 가능한 다른 애플리케이션에서도 이용가능하다. 스프링부트 에서 쓸때는 
좀더 편리할뿐.

2020년 6월 23일 화요일

AWS + LAMBDA + (Elastic cache) REDIS 연동 방법

일단 AWS Elastic cache 는 외부에서 바로 연결이 안된다. 고로, VPC 설정을 해서 , 내부의 AWS Lambda 함수에서 접근이 가능하도록 해야한다.
그걸 하기위해서
IAM 롤 설정-VPC-SUBNET-LAMBA-API GATEWAY-REDIS-REDIS SUBNET GROUP 설정을 해야한다. 정말 귀찮다.

1. IAM 설정

Role 에 가서 Lambda 선택(어쭈, 아예 상단에 있네. 많이 쓰란 얘긴가) -> AwsLambdaBasicExecutionRole 선택해서 생성해둔다. 이건 나중에 LAMBDA 기능에서 권한 설정할때 쓴다.

2.VPC
2-1. VPC 생성

2-2.그밑의 Subnet 도 생성
10.0.0.0/28 , 10.0.0.16/28 , 10.0.0.32/28
이런식으로 나누어서 생성하면 나중에 zone 별로 지정하기 좋다.

2-3.Security Group 도 설정해야 한다. (VPC IP 로 데이터가 나가고 들어오는 보안설정이다)

3.Elastic cache Redis
3-1 Subnet group 부터 설정한다. 여기서 Zone 별로 Subnet 을 설정하는데, 2-2 에서 여러개 설정해놓았다면 존별로 그것들을 지정하면된다.
3-2 Redis 생성 하면 end-point 가 나오는데 그게 내부에서 접속가능한 주소다. 람다에서 접속하자.

4.Lambda
4-1 새로운 함수를 언어에 맞게 생성한다. 아마존은 크롬을 싫어하니 에디터는 파이어팍스에서 하자. NodeJs 의 경우는 관련패키지가 많이 필요하니 로컬에서 압축파일 만들어서 zip 으로 올리자. [작업->.zip 업로드]
4-2 VPC 지정해야 Lambda 에서 Redis 접속이 가능하다. 설정하자. 보안그룹등도 같이 지정하자.
4-3 권한 탭화면에서 IAM 의 권한 지정하자.
4-4 이제 외부에서 접속가능하게 API GATEWAY 추가하자

5.API GATEWAY
5-1 람다화면에서 트리거 추가.
5-2 API GATEWAY 설정화면에서 리소스 화면에서 메서드/리소스 다 추가하자
5-3 Restful 의 /mymy/sum/1/11 -> /mymy/{whichoper}/{firstvalue}/{lastvalue} 식으로 get/post 하고 싶으면 차례대로 추가하면된다. 단 LAMBDA_PROXY 를 선택해야 된다.

6. 외부에서 curl 등으로 접속해보면 된다.

const redis   = require("ioredis");


exports.handler = async (event, context, callback) => {
    context.callbackWaitsForEmptyEventLoop = false;     
    try {
        //Redis서버접속
        client = await connectRedis();

        switch (currentMethos) {

            case "GET":

                let value = await getValueOfKey(client, keyNameForRedis);
                break;
            case "POST":

                await setValueOfKey(client, keyNameForRedis, setVal);
                break;

       
        //Redis 접속 해제
        disconnectRedis();

        return {
            statusCode: 200,
            headers: {
                "Access-Control-Allow-Headers" : "Content-Type",
                "Access-Control-Allow-Origin": "*"
            },
            body: JSON.stringify(
                    {
                        code :  resultCode,
                        uuid :  in_uuid
                    }
                )
        }
    } catch (error) {
        console.error('error:', error);
    }
};
function connectRedis() {
    return new Promise(function (resolve, reject) {
        
          const client = new redis.Cluster(['//xxxxxxxx.cache.amazonaws.com:6379'],
            {
                slotsRefreshTimeout: 5000
            }
        );


        client.on('connect', () => {
            console.log('Connected');
            resolve(client);
        });
        client.on('error', (error) => {
            console.log(`Connection Error`, error);
            reject(error);
        });
    });
}

function disconnectRedis() {
    if (client) {
       client.disconnect();
    }
}

function getValueOfKey(client, inkey) {
    return new Promise(function (resolve) {
        client.get(inkey, (err, reply) => {
            if(err){
                resolve('error');
                return;
          } else {
            resolve(reply);
          }

        });
    });
}

function setValueOfKey(client, inkey, invalue) {
    
    return new Promise(function (resolve) {
        resolve(client.set(inkey, invalue));
    });
}

function setExpireOfKey(client, inkey, inExpire) {
    
    return new Promise(function (resolve) {
        resolve(client.expire(inkey, inExpire ));
    });
}

외부에서 접속하려면 API GATEWAY 항목에서 확인하면된다. 잘된다.

2020년 6월 4일 목요일

springboot Flyway 간단사용법

그레이들의 특정 태스크에 추가해서 디비정합성이 맞는지도 체크하고,
누군가가 새로운 sql 을 넣은게 있다면 자동으로 갱신도하고. 좋다.

일단 sql 은 resources/db/migration/V6__Create_smple_table.sql( V아무거나__아무설명.sql) 로 저장하면됨.
자동으로 디비에 flyway_schema_history 이런테이블에 들어가는데 형상관리(정합성)해주는거임

사용은 이렇게 ...
plugins {    id "org.flywaydb.flyway" version "6.4.3"}


bootRunMe {    systemProperty 'spring.profiles.active', System.properties['spring.profiles.active']

    doFirst {        
      task migrateDatabase1(type: org.flywaydb.gradle.task.FlywayMigrateTask) {            
             url = 'jdbc:mysql://localhost:3306/mymydb'
            user = 'uuu'
            password = 'ppp'
            baselineVersion = 1.0
            baselineOnMigrate = true
            baselineDescription = "Base Migration"
}
       tasks.migrateDatabase1.execute()

        task migrateDatabase2(type: org.flywaydb.gradle.task.FlywayMigrateTask) {  
            url = 'jdbc:mysql://localhost:3307/mymydb'      
            user = 'uuu'     
            password = 'ppp'   
       }       
      tasks.migrateDatabase2.execute()
}
}

bootRunMe 태스크빌드를 실행하면 doFirst 가 달리면서 여러서버의 디비정보를 최신업데이트된걸로 해줌.
별디비 다해줌. sqlite도 가능하므로 안드로이드에서도 가능

걍 gradle 의 task 중
...
myRunTask{
  doFirst {
     flywayMigrate.execute()
}
...
넣어줘