java7 이전에서 Map의 내용을 확인하기 위해 출력하는 코드는 verbose 하다.


Map<Integer,String> map = new HashMap<Integer,String>();


for(Map.Entry<Integer,String> entry: map.entrySet()){

    System.out.println(entry.getKey()+" - "+ entry.getValue());

}




Apache Collections 라이브러리는  MapUtils.debugPrint 메소드를 통해 Map을 출력할 수 있다. 이 방법은 slf4j나 log4j의 OutputStream을 얻기 어려워 쓰기 애매한 부분이 있다. 


MapUtils.debugPrint(System.out, "myMap", map);




java8부터는 stream의 forEach를 사용하면 그나마 좀 쓰기 편한 듯 하다. 



map.forEach((key, value) -> { logger.debug(key + " : " + value); });

'java core' 카테고리의 다른 글

jnr  (0) 2017.02.27
enum 데이터를 리스트(list)로 얻기  (0) 2016.12.19
자바의 clone, guava의 Maps, Lists 사용  (0) 2016.12.01
JVM의 safepoint  (0) 2016.09.08
JDK의 Flight Recorder 라이선스  (0) 2016.09.08
Posted by '김용환'
,



scala의 어떤 클래스가 클래스를 상속받고 트레이트(trait)를 믹스인하는 초기화는 다음 순서와 같다.


object Main extends App {
trait T1 {
println("t1") // 4
}

trait T2 {
println("t2") // 5
}

class BaseParent {
println("base-parent") // 2
}

class Parent extends BaseParent {
println("parent") // 3
}

class Example extends Parent with T1 with T2 {
println("example") // 6
}

println(s"before") // 1
new Example
println(s"after") // 7
}


결과는 다음과 같다. 


부모 클래스의 부모 클래스부터 초기화되고 다음에 부모 클래스가 생성된다. 

다음에 믹스인(mix in)한 트레이트가 초기화된다. 


before

base-parent

parent

t1

t2

example

after





'scala' 카테고리의 다른 글

[scala] 쉘 실행하기  (0) 2016.12.13
[scala] 클래스 초기화하기(생성)  (0) 2016.12.13
[scala] for 내장  (0) 2016.12.08
[scala] try-catch/Try-match/Either/Validation  (0) 2016.12.06
[scala] 부분 적용 함수 / 커링 / 부분 함수  (0) 2016.12.05
Posted by '김용환'
,

echo ?, * 파일

unix and linux 2016. 12. 11. 01:08


유닉스 시스템에서 *는 모든 글자를 의미하고, ?은 한 단어를 의미한다.


특이하게도 cat a? 하면 a로 시작하고 2글자의 파일 이름을 가진 모든 파일을 의미하는데,


cat ?는 한 글자로 된 모든 파일 이름을 출력한다는 의미를 가진다. 



한 글자 파일이 없을 때 아래와 같은 에러가 발생한다.


$ cat ?

cat: ?: No such file or directory



한 글자 파일을 생성하고 cat ?을 실행해본다. 한 글자 파일을 출력한다.


$ cat > a

1

1

1


$ cat > b

2

2

2

2


$ cat ?

1

1

1

2

2

2

2


파일 명을 보고 싶다면, echo *을 실행한다.


$ echo ?

a b



2글자 이상의 파일명을 보려면 다음과 같이 실행한다.


$ echo ??*




참고로 *를 살펴본다.


echo * 커맨드를 실행하면 현재 디렉토리의 모든 이름을 출력한다. 중요한 것은 알파벳 순서대로 출력된다.


쉘은 *을 분석해 현재 디렉토리의 모든 파일을 검색해서 파일 이름 대체(filename substitution)하는 작업을 진행한다.  따라서 echo의 매개변수 개수는 1개가 아니라 현재 디렉토리의 파일 개수가 된다.



x=*의 값은 현재 디렉토리의 값이 된다. 


$ ls

AUTHORS                    LICENSE.txt-spymemcached   buildfile                  etc                        pom.xml                    xdocs

ChangeLog                  PATENTS                    devtools                   install-arcus-memcached.sh src

LICENSE                    README.md                  docs                       mvnTestConf.json


$ x=*


$ echo $x

AUTHORS ChangeLog LICENSE LICENSE.txt-spymemcached PATENTS README.md buildfile devtools docs etc install-arcus-memcached.sh mvnTestConf.json pom.xml src target xdocs



쉘이 커맨드 라인을 파악하는 단계는 다음과 같다. 

1. 쉘은 x 값으로 * 를 대체하여 라인을 스캔했다.

2. 쉘은 라인을 다시 살펴보고 * 를 발견 한 후 현재 디렉토리의 모든 파일 이름을 대체했다.

3. 쉘은 echo의 실행을 시작해 파일 목록을 매개변수로 전달했다.

(변수 대체를 먼저 진행하고 파일 대체를 진행한다)



grep에서 *를 사용할 때도 유의할 필요가 있다. 


특정 파일에서 '*'만 검색하려고 할 때  $ grep '*' file 을 사용할 수 있지만,


$ grep * file을 사용할 때 *는 현재 디렉토리의 모든 파일을 의미한다. 

아래 예시에서 의미를 파악할 수 있다. 


$ ls

Main$.class                 aZ

Main$delayedInit$body.class b

Main.class                  print_a.sh

Main.scala                  scalahello

a                           xx


$ grep * 123131

grep: 123131: No such file or directory

$ grep * xx

$ echo $?

1











참조 

https://en.wikipedia.org/wiki/Glob_(programming)

Posted by '김용환'
,


webpagetest.org를 EC2로 쉽게 포팅이 된다고 한다.






설치 관련 내용은 https://sites.google.com/a/webpagetest.org/docs/private-instances에 존재한다. 


Easy Deployment (on EC2)

There is an EC2 AMI available for the WebPageTest server that provides a lot of benefits:
  • No configuration required (up and running in 5 minutes).
  • Automatically starts and stops EC2 test agents in all of the EC2 regions as needed.
  • Automatically updates to the latest server and agent code.




https://sites.google.com/a/webpagetest.org/docs/using-webpagetest/quick-start-quide를 따라 하면 된다.




외부 클라우드에서 구축을 하면 Rest Api(https://sites.google.com/a/webpagetest.org/docs/advanced-features/webpagetest-restful-apis )로 외부에 오픈하는 서버를 확인할 수 있고, 성능도 확인할 수 있을 것 같다.


웹 페이지의 성능, 스크린샷을 확인할 수 있어서 좋은 듯 하다.

Posted by '김용환'
,



cassandra에서 alter table로 compaction 전략을 변경할 수 있다. 



ALTER TABLE table WITH compaction = { 'class' :  'org.apache.cassandra.db.compaction.LeveledCompactionStrategy'  }


사실 처음부터 compaction을 설정하려면 ALTER TABLE 없이 CREATE TABLE에 with compaction을 두어도 된다.

CREATE TABLE story.test1 (

  block_id uuid,

  species text,

  alias text,

  population varint,

  PRIMARY KEY (block_id)

) WITH compression =

    { 'sstable_compression' : 'DeflateCompressor', 'chunk_length_kb' : 64 }

  AND compaction =

    { 'class' : 'LeveledCompactionStrategy'};




카산드라 2.0.11 이상의 버전을 사용할 때 compaction 정책을 정할 때, 다음 규칙을 이용하는 것이 좋다. 


- 쓰기가 많은 작업에는 SizeTiredCompationStrategy을 사용한다.

- 읽기가 많은 작업에는 LeveledCompactionStrategy를 사용한다.

- 시계열과 데이터 만료 작업에는 DateTieredCompactionStrategy를 사용한다. 


https://docs.datastax.com/en/cassandra/2.0/cassandra/dml/dml_write_path_c.html#concept_ds_wt3_32w_zj__logging-writes-and-memtable-storage



You can configure these types of compaction to run periodically: SizeTieredCompactionStrategy, DateTieredCompactionStrategy (Cassandra 2.0.11), and LeveledCompactionStrategy.


SizeTieredCompactionStrategy is designed for write-intensive workloads, DateTieredCompactionStrategy for time-series and expiring data, and LeveledCompactionStrategy for read-intensive workloads. You can manually start compaction using the nodetool compact command.





compaction 전략에 대한 설명은 다음을 참고한다. 


http://www.datastax.com/dev/blog/when-to-use-leveled-compaction



https://docs.datastax.com/en/cassandra/2.1/cassandra/operations/ops_configure_compaction_t.html



http://www.datastax.com/dev/blog/when-to-use-leveled-compaction



http://www.datastax.com/dev/blog/datetieredcompactionstrategy



https://labs.spotify.com/2014/12/18/date-tiered-compaction/







cassandra의 compaction설정은 테이블 단위라면, hbase의 compaction 설정은 클러스터 단위이다.


http://knight76.tistory.com/entry/%EA%B3%B5%EB%B6%80-Hbase-compaction





'cassandra' 카테고리의 다른 글

[cassandra] nodetool disablebinary  (0) 2017.01.11
[cassandra] cassandra 서버를 안전하게 재시작하기  (0) 2017.01.09
[cassandra] select count(*) 구하기  (0) 2016.12.07
[cassandra] read repair  (0) 2016.11.23
[cassandra] cqlsh 팁  (0) 2016.11.21
Posted by '김용환'
,

[scala] for 내장

scala 2016. 12. 8. 11:44

for 내장(comprehension) (또는 for yield문)에 대한 예시이다. 


for 내장은 다중 for 문으로 생각하는 게 좋다. 


그리고, yield를 map/flatmap/foreach와 비슷한 느낌으로 보는게 좋은 듯 하다. 따라서 for 내장의 라인은 큰 의미가 있기 때문에 주의해서 쓸 필요가 있다. 





즉, 아래와 같은 문장은 쓸 수 없고, 컴파일 에러가 난다. 

val m = for {
a = 5
b = 3
} yield (a + b)

에러 내용이다.


Error:(12, 7) '<-' expected but '=' found.

    a = 5

Error:(13, 1) illegal start of simple expression

          b = 3




아래 코드도 에러가 발생하지만, for 내장에 대해서 조금 이해할 수 있다. 

val m = for {
a <- 1
b <- 2
} yield (a + b)


에러 내용이다.


Error:(12, 10) value flatMap is not a member of Int

    a <- 1

Error:(13, 10) value map is not a member of Int

    b <- 2




다음과 같은 에러가 발생하는데. 첫 번째 라인에서는 flatmap, 다음은 map관련 에러가 발생한다. 

즉, for 내장은 내부적으로 변환된다는 의미를 가진다. 

마지막 문장은 map이고, 이전 문장은 flatmap으로 변환된다.





동작되는 for 내장 예시를 소개한다. 

for {
l <- List(1,2,3,4,5)
} yield l.println


결과는 다음과 같다.


1

2

3

4

5





val m = for {
s <- List("a", "bb", "ccc")
} yield s.length
println(m)

결과는 다음과 같다. 


List(1, 2, 3)



그리고 2차 for문처럼 쓸 수 있다. 


for {
i <- 1 to 2
j <- 3 to 4
} println(s"$i + $j = ${i + j}")


결과는 다음과 같다.


1 + 3 = 4

1 + 4 = 5

2 + 3 = 5

2 + 4 = 6





object Main extends App {
val m = for {
s <- List("1", "a", "3")
c <- s
} yield s"${s} - ${c}"
println(m)
}

결과는 다음과 같다.


List(1 - 1, a - a, 3 - 3)


val m = for {
s <- List("1", "a", "3")
c <- s
if c.isLetter
} yield s"${s} - ${c}"
println(m)

결과는 다음과 같다.


List(a - a)




yield에 콜렉션(튜플)을 사용할 수 있다. 

val m = for {
s <- List("b4", "a", "3")
c <- s
} yield (s, c)

m.foreach(println)


결과는 다음과 같다.

(b4,b)

(b4,4)

(a,a)

(3,3)



option으로 테스트 해본다.

val m = for {
i <- List(1, 2, 3, 0)
} yield (i * 10)

m.foreach(println)

결과는 다음과 같다.


10

20

30

0



Some으로 내용을 감싸면 내부적으로 Some  None에 따라 값을 구한다. 

val m = for {
Some(i) <- List(Some(1), Some(2), Some(3), None)
} yield (i * 10)

m.foreach(println)



결과는 다음과 같다.


10

20

30




참고로 인스턴스는 사용한 콜렉션을 따르다. 아래 결과는 Vector(10, 20, 30)이다. 


val m = for {
Some(i) <- Vector(Some(1), Some(2), Some(3), None)
} yield (i * 10)

println(m)






함수를 사용할 때는 리턴 타입이 Option로 감쌀 때 잘 동작한다. 그냥 하면 에러가 발생한다. 



다음은 에러 케이스이다. 

def isAllDigits(s: String) : Int = {
if (s forall Character.isDigit) {
return s.toInt
}
0
}

val m = for {
a <- isAllDigits("1")
b <- isAllDigits("2")
c <- isAllDigits("9")
} yield (a + b + c)

println(m)

결과는 다음과 같다. 


Error:(33, 21) value flatMap is not a member of Int

    a <- isAllDigits("1")

Error:(34, 21) value flatMap is not a member of Int

    b <- isAllDigits("2")

Error:(35, 21) value map is not a member of Int

    c <- isAllDigits("9")




다음은 Option을 래핑한 예시이다.


def isAllDigits(s: String) : Option[Int] = {
if (s forall Character.isDigit) {
return Some(s.toInt)
}
Some(0)
}

val m = for {
a <- isAllDigits("1")
b <- isAllDigits("2")
c <- isAllDigits("9")
} yield (a + b + c)

println(m)


결과는 다음과 같다. 


Some(12)



잘못된 결과가 나와도 잘 동작한다. 

def isAllDigits(s: String) : Option[Int] = {
if (s forall Character.isDigit) {
return Some(s.toInt)
}
Some(0)
}

val m = for {
a <- isAllDigits("1")
b <- isAllDigits("a")
c <- isAllDigits("2")
} yield (a + b + c)

println(m)



Posted by '김용환'
,


cassandra에서 테이블의 row 개수를 구하려면, 다음과 같은 cql을 사용할 수 있다. 


select count(*) from table



하지만, 대용량 데이터가 존재한다면, timeout이 발생한다.


이를 위해 timeout를 설정할 수 있지만, 성능 이슈가 발생할 수 있으니..


cqlsh --request-timeout="60"이라고 지정할 수 있다.



https://docs.datastax.com/en/cql/3.3/cql/cql_reference/cqlsh.html


--request-timeout="timeout" CQL request timeout in seconds; default: 10



하지만, 문제는 성능 이슈이다..


(서비스에서 매번 카운트를 불러 읽는 경우라면 따로 cassandra counter를 이용해 구현하는 것이 좋다. hbase나 cassandra에 카운트 계산을 매번 호출하는 것은 위험한 작업이다!!)



대용량 데이터의 row 개수를 구할 수 있는 또 다른 방법은 nodetool을 이용하는 것이다.

nodetool cfstat를 사용해서 테이블의 Number of keys(estimate)를 확인하면 대략적인 내용을 확인할 수 있다. 



$./nodetool cfstat

Table (index): table
Number of keys (estimate): 184251
..


Table: table

Number of keys (estimate): 538971 




추정치 값은 아래 cassandra 코드를 따라 들어가 확인할 수 있다. 


데이터 스트림 크기를 기반으로 hyperloglog 계산을 이용한 추정치이기 때문에 신뢰할만하다.





https://github.com/apache/cassandra/blob/42e0fc5ee221950875d93b4cd007d4f5bcaa4244/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsHolder.java


                Object estimatedPartitionCount = probe.getColumnFamilyMetric(keyspaceName, tableName, "EstimatedPartitionCount");

                if (Long.valueOf(-1L).equals(estimatedPartitionCount))

                {

                    estimatedPartitionCount = 0L;

                }

                statsTable.numberOfKeysEstimate = estimatedPartitionCount;








https://github.com/apache/cassandra/blob/979af884ee4ecef78a21c4bd58992d053256f8f0/src/java/org/apache/cassandra/tools/NodeProbe.java


    /**

     * Retrieve ColumnFamily metrics

     * @param ks Keyspace for which stats are to be displayed or null for the global value

     * @param cf ColumnFamily for which stats are to be displayed or null for the keyspace value (if ks supplied)

     * @param metricName View {@link TableMetrics}.

     */

    public Object getColumnFamilyMetric(String ks, String cf, String metricName)

    {

        try

        {

            ObjectName oName = null;

            if (!Strings.isNullOrEmpty(ks) && !Strings.isNullOrEmpty(cf))

            {

                String type = cf.contains(".") ? "IndexTable" : "Table";

                oName = new ObjectName(String.format("org.apache.cassandra.metrics:type=%s,keyspace=%s,scope=%s,name=%s", type, ks, cf, metricName));

            }

            else if (!Strings.isNullOrEmpty(ks))

            {

                oName = new ObjectName(String.format("org.apache.cassandra.metrics:type=Keyspace,keyspace=%s,name=%s", ks, metricName));

            }

            else

            {

                oName = new ObjectName(String.format("org.apache.cassandra.metrics:type=Table,name=%s", metricName));

            }

            switch(metricName)

            {

                case "BloomFilterDiskSpaceUsed":

                case "BloomFilterFalsePositives":

                case "BloomFilterFalseRatio":

                case "BloomFilterOffHeapMemoryUsed":

                case "IndexSummaryOffHeapMemoryUsed":

                case "CompressionMetadataOffHeapMemoryUsed":

                case "CompressionRatio":

                case "EstimatedColumnCountHistogram":

                case "EstimatedPartitionSizeHistogram":

                case "EstimatedPartitionCount":

                case "KeyCacheHitRate":

                case "LiveSSTableCount":

                case "MaxPartitionSize":

                case "MeanPartitionSize":

                case "MemtableColumnsCount":

                case "MemtableLiveDataSize":

                case "MemtableOffHeapSize":

                case "MinPartitionSize":

                case "PercentRepaired":

                case "RecentBloomFilterFalsePositives":

                case "RecentBloomFilterFalseRatio":

                case "SnapshotsSize":

                    return JMX.newMBeanProxy(mbeanServerConn, oName, CassandraMetricsRegistry.JmxGaugeMBean.class).getValue();







https://github.com/apache/cassandra/blob/81f6c784ce967fadb6ed7f58de1328e713eaf53c/src/java/org/apache/cassandra/metrics/TableMetrics.java



public class TableMetrics

{



    /** Approximate number of keys in table. */

    public final Gauge<Long> estimatedPartitionCount;



        estimatedPartitionCount = Metrics.register(factory.createMetricName("EstimatedPartitionCount"),

                                                   aliasFactory.createMetricName("EstimatedRowCount"),

                                                   new Gauge<Long>()

                                                   {

                                                       public Long getValue()

                                                       {

                                                           long memtablePartitions = 0;

                                                           for (Memtable memtable : cfs.getTracker().getView().getAllMemtables())

                                                               memtablePartitions += memtable.partitionCount();

                                                           return SSTableReader.getApproximateKeyCount(cfs.getSSTables(SSTableSet.CANONICAL)) + memtablePartitions;

                                                       }

                                                   });






https://github.com/apache/cassandra/blob/4a2464192e9e69457f5a5ecf26c094f9298bf069/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java


    /**

     * Calculate approximate key count.

     * If cardinality estimator is available on all given sstables, then this method use them to estimate

     * key count.

     * If not, then this uses index summaries.

     *

     * @param sstables SSTables to calculate key count

     * @return estimated key count

     */

    public static long getApproximateKeyCount(Iterable<SSTableReader> sstables)

    {

        long count = -1;


        if (Iterables.isEmpty(sstables))

            return count;


        boolean failed = false;

        ICardinality cardinality = null;

        for (SSTableReader sstable : sstables)

        {

            if (sstable.openReason == OpenReason.EARLY)

                continue;


            try

            {

                CompactionMetadata metadata = (CompactionMetadata) sstable.descriptor.getMetadataSerializer().deserialize(sstable.descriptor, MetadataType.COMPACTION);

                // If we can't load the CompactionMetadata, we are forced to estimate the keys using the index

                // summary. (CASSANDRA-10676)

                if (metadata == null)

                {

                    logger.warn("Reading cardinality from Statistics.db failed for {}", sstable.getFilename());

                    failed = true;

                    break;

                }


                if (cardinality == null)

                    cardinality = metadata.cardinalityEstimator;

                else

                    cardinality = cardinality.merge(metadata.cardinalityEstimator);

            }

            catch (IOException e)

            {

                logger.warn("Reading cardinality from Statistics.db failed.", e);

                failed = true;

                break;

            }

            catch (CardinalityMergeException e)

            {

                logger.warn("Cardinality merge failed.", e);

                failed = true;

                break;

            }

        }

        if (cardinality != null && !failed)

            count = cardinality.cardinality();


        // if something went wrong above or cardinality is not available, calculate using index summary

        if (count < 0)

        {

            for (SSTableReader sstable : sstables)

                count += sstable.estimatedKeys();

        }

        return count;

    }





https://github.com/apache/cassandra/blob/4a2464192e9e69457f5a5ecf26c094f9298bf069/src/java/org/apache/cassandra/io/sstable/metadata/CompactionMetadata.java



/**

 * Compaction related SSTable metadata.

 *

 * Only loaded for <b>compacting</b> SSTables at the time of compaction.

 */

public class CompactionMetadata extends MetadataComponent

{

    public static final IMetadataComponentSerializer serializer = new CompactionMetadataSerializer();


    public final ICardinality cardinalityEstimator;


    public CompactionMetadata(ICardinality cardinalityEstimator)

    {

        this.cardinalityEstimator = cardinalityEstimator;

    }


.,...

   public static class CompactionMetadataSerializer implements IMetadataComponentSerializer<CompactionMetadata>

    {

        public int serializedSize(Version version, CompactionMetadata component) throws IOException

        {

            int sz = 0;

            byte[] serializedCardinality = component.cardinalityEstimator.getBytes();

            return TypeSizes.sizeof(serializedCardinality.length) + serializedCardinality.length + sz;

        }


        public void serialize(Version version, CompactionMetadata component, DataOutputPlus out) throws IOException

        {

            ByteBufferUtil.writeWithLength(component.cardinalityEstimator.getBytes(), out);

        }


        public CompactionMetadata deserialize(Version version, DataInputPlus in) throws IOException

        {

            ICardinality cardinality = HyperLogLogPlus.Builder.build(ByteBufferUtil.readBytes(in, in.readInt()));

            return new CompactionMetadata(cardinality);

        }

    }






Posted by '김용환'
,


google의 guava의 hash 함수가 존재한다.


패키지명은 com.google.common.hash.Hashing이다.


아주 친절하게도 http://goo.gl/jS7HH에 잘 저장되어 있어서 어느 것을 선택할 지 알려주고 있다!!





adler32, crc32,sha256 등등의 경우는 추천하지 않고 있다. Not stable것도 있으니 노트를 잘 보고 용도에 맞게 써야 할 것 같다. 


예를 들어, Hashing.murmur3_32는 다음 메소드를 가지고 있다. 


  /**

   * Returns a hash function implementing the

   * <a href="http://smhasher.googlecode.com/svn/trunk/MurmurHash3.cpp">

   * 32-bit murmur3 algorithm, x86 variant</a> (little-endian variant),

   * using the given seed value.

   *

   * <p>The exact C++ equivalent is the MurmurHash3_x86_32 function (Murmur3A).

   */

  public static HashFunction murmur3_32(int seed) {

    return new Murmur3_32HashFunction(seed);

  }


   /**

   * Returns a hash function implementing the

   * <a href="http://smhasher.googlecode.com/svn/trunk/MurmurHash3.cpp">

   * 32-bit murmur3 algorithm, x86 variant</a> (little-endian variant),

   * using a seed value of zero.

   *

   * <p>The exact C++ equivalent is the MurmurHash3_x86_32 function (Murmur3A).

   */

  public static HashFunction murmur3_32() {

    return Murmur3_32Holder.MURMUR3_32;

  }



다음과 같이 사용할 수 있다. 


 com.google.common.hash.HashFunction algorithm = com.google.common.hash.Hashing.murmur3_32(0);

 long b = algorithm.hashBytes("google:plus:201612123:na".getBytes()).padToLong()






참고로 jedis에도 hashing 코드가 존재한다.


https://github.com/xetorthio/jedis/blob/master/src/main/java/redis/clients/util/Hashing.java


다음과 같이 사용할 수 있다. 


redis.clients.util.Hashing algorithm = redis.clients.util.Hashing.MURMUR_HASH;

long b = algorithm.hash(redis.clients.util.SafeEncoder.encode("google:plus:20161223:na"));


Posted by '김용환'
,


capistrano에서 다른 task를 호출하는 방법과 예시이다. 

1. invoke 실행하기 

2. Rake:Task 실행하기

Rake::Task["namespace:task"].invoke




1. invoke 실행하기


  task :status do

    invoke 'deploy:status'

  end


  task :ping do

    invoke 'deploy:ping'

  end



2. Rake::Task 실행하기


  task :change do

    Rake::Task["deploy:change_port"].invoke

  end


  task :change_port do

..

  end



'Ruby' 카테고리의 다른 글

ruby zookeeper  (0) 2017.02.07
[ruby] http call 예시  (0) 2017.01.06
ruby on rails 애플리케이션 실행하기  (0) 2016.10.12
[ruby] File 존재 여부 확인할 때 홈 디렉토리 주의  (0) 2016.08.18
[ruby] or equals 기능 - ||=  (0) 2016.08.17
Posted by '김용환'
,


scala에서 Exception처리에 대한 다양한 내용을 살펴본다.




1. try-catch 

자바와 비슷하게 처리한다.


object Main extends App {
def throwException: Unit = {
try {
println("A".toLong)
} catch {
case ex: Exception => println(ex)
} finally {
println("finally")
}
}
throwException
}


결과는 다음과 같다.


java.lang.NumberFormatException: For input string: "A"

finally




2. scala.util.Try-match



import scala.util.{Failure, Success, Try}

object Main extends App {
def throwException: Any = {
Try {
println("A".toLong)
} match {
case Success(result) => result
case Failure(exception) => exception
}
}
println(throwException)
}

결과는 다음과 같다.


java.lang.NumberFormatException: For input string: "A"



다른 예제


val loginUser =
Try(requestHeader.headers("loginUser")) match {
case Success(user) => user
case _ => ""
}




3. scala.util.Either 


Either는 Exception과 결과 값을 한번에 받을 수 있는 타입이다. 


def throwException: Either[Long, Throwable] = {
try {
Left("A".toLong)
} catch {
case ex: Exception => Right(ex)
} finally {
println("finally")
}
}
println(throwException)
println(throwException.merge)


결과는 다음과 같다.


finally

Right(java.lang.NumberFormatException: For input string: "A")

finally

java.lang.NumberFormatException: For input string: "A"



merge메소드는 예외와 리턴값 사이의 값을 하나로 병합한다. 





4. Exception.allCatch


1,2,3에 연관되어 Exception.allCatch라는 것이 있다. 이를 이용하면 try/catch를 한 줄에 사용할 수도 있다.

import scala.util.control.Exception._
println(allCatch.opt("A".toLong))
println(allCatch.toTry("A".toLong))
println(allCatch.either("A".toLong))

결과는 다음과 같다.


None

Failure(java.lang.NumberFormatException: For input string: "A")

Left(java.lang.NumberFormatException: For input string: "A")




5. scalaz의 disjunction

Either보다 더 좋은 형태의 예외 처리를 할 수 있다. 

scala 고수 개발자들이 scalaz의 disjunction을 추천한다고 하니 알아두면 좋을 것 같다.


\/를 사용하면서 해괴하지만, 간단해서 쓴다는...



val m = scalaz.\/.right[Throwable, Int](5).map(_ * 2)
println(m)
println(m.merge)

val n = scalaz.\/.left[Throwable, String](new Exception("1")).map(_.toLong)
println(n)
println(n.merge)

val ez : String \/ String = "a".right
println(ez)
println(ez.merge)


결과는 다음과 같다. Either와 비슷하게 쓰인다. 훨씬 either보다는 편하게 쓸 수 있다. 


\/-(10)

10


-\/(java.lang.Exception: 1)

java.lang.Exception: 1


\/-(a)

a



자세한 내용은 scalaz 7.2(https://github.com/scalaz/scalaz/tree/series/7.2.x)를 참고한다.



5. Validation


scalaz의 Validation도 사용할 수 있다. 


import scalaz._

def positive(i: Int): Validation[Exception, Int] = {
if (i > 0) Success(i) // <1>
else Failure(new NumberFormatException("should number > 0"))
}
println(positive(1))
println(positive(-1))


Posted by '김용환'
,