Topic은 Key-Value 형식의 메세지 구조이며, Value로 어떤 타입의 메세지도 가능(문자열, 숫자값, 객체, Json, Avro, Protobuf 등)
로그 파일과 같이 연속적으로 추가, 발생하는 데이터를 저장하는 구조
시간의 흐름에 따라 메시지가 순차적으로 물리적인 파일에 write 됨 (RDB처럼 Update 같은 개념이 없이 전부 Append한다. TSDB처럼)
하나의 Topic은 1개 이상의 Partition을 가질 수 있다.
Partition
Topic의 partition은 kafka의 병렬 성능과 가용성 기능의 핵심 요소
메시지는 병렬 성능과 가용성을 고려한 topic 내의 개별 partition에 분산 저장됨.
또한 topic의 partition 들은 단일 kafka broker 뿐만 아니라 여러 개의 kafka broker 들에 분산 저장 됨.
두번째 Broker가 죽으면 첫번째 Broker의 Partition #1이 Follower에서 Leader가 되어 정합성 보장.
Offsets
개별 파티션은 정렬되고, 변경 할 수 없는(immutable) 일련의 레코드로 구성된 로그 메시지
개별 레코드는 offset으로 불리는 일련 번호를 할당 받음
개별 Partition은 다른 파티션과 완전히 독립적임
개별 Partition 내에서 정렬되고 offset이 할당됨
Producer
Producers are clients that write events to Kafka. The producer specifies the topics they will write to and the producer controls how events are assigned to partitions within a topic. This can be done in a round-robin fashion for load balancing or it can be done according to some semantic partition function such as by the event key.
Producer는 Topic에 메세지를 보냄(메세지 write)
Producer는 성능 / 로드밸런싱 / 가용성 / 업무 정합성 등을 고려하여 어떤 브로커의 파티션으로 메세지를 보내야 할지 전략적으로 결정됨
Topic과 Value는 필수값으로 Broker에 value를 전달하면 Partition에 나누어 저장됨.
Consumer
Consumers are clients that read events from Kafka.
The only metadata retained on a per-consumer basis is the offset or position of that consumer in a topic. This offset is controlled by the consumer. Normally a consumer will advance its offset linearly as it reads records, however, because the position is controlled by the consumer it can consume records in any order. For example, a consumer can reset to an older offset to reprocess data from the past or skip ahead to the most recent record and start consuming from “now”.
This combination of features means that Kafka consumers can come and go without much impact on the cluster or on other consumers.
Consumer는 Topic에서 메세지를 읽어 들임.
여러 개의 Consumer들로 구성 될 경우 어떤 브로커의 파티션에서 메세지를 읽어들일지 전략적으로 결정함
auto.offset.reset
Consumer가 Topic에 처음 접속하여 Message를 가져올 때 가장 오래된 처음 (Broker의 message) offset 부터(earliest) 가져올 것인지, 가장 최근인 마지막 offset 부터 가져올 것 인지를 설정하는 파라미터
auto.offset.reset = ealiest : 처음 offset 부터 읽음
auto.offset.reset = latest : 마지막 offset부터 읽음 (default)
kafka-console-consumer 명령어를 사용 할 때 --from-beginning을 사용해야만 auto.offset.reset이 earlist로 지정됨
Serializer/Deserializer
Producer → Broker로 Key,Value가 전송될 때 Serialize 되어 ByteArray로 전달 된다. 그리고 Broker → Consumer는 ByteArray를 Deserialize하여 key, value를 획득한다.
ByteArray를 사용하여 전송하면 네트워크 대역폭도 잘 사용할 수 있고 압축도 된다. 자바 코드에서는 아래처럼 적용한다.
Kafka에서 기본적으로 제공하는 Serializer는 StringSerializer, ShortSerializer, IntegerSerializer, LongSerializer, DoubleSerializer, BytesSerializer가 있다. (그러나 업무에선 Custom 만들어야 할 듯)
Partitioner
메세지는 Producer를 통해 전송시 Partitioner를 통해 토픽의 어떤 파티션으로 전송되어야 할 지 미리 결정됨.
메세지 Key는 업무 로직이나 메세지 Produce/Consume시 분산 성능 영향을 고려하여 생성
Key 값을 가지지 않는 경우, 라운드 로빈, 스티키 파티션 등의 전략이 선택되어 파티션 별로 메세지가 전송 될 수 있음. → Topic이 여러 개의 파티션을 가질 때 메세지의 전송 순서가 보장되지 않은 채로 Consumer에서 읽혀질 수 있음. (전송 순서를 보장하려면 Partition을 하나로 가져야 하는데 그럼 분산시스템의 이점이 없어짐)
Key 값을 가지는 경우, 특정 key값을 가지는 메세지는 특정 파티션으로 고정되어 전송됨. → 단일 파티션 내에서 전송 순서가 보장되어 Consumer에서 읽혀짐.
$ cd ~ # home 으로 이동
$ sudo wget https://packages.confluent.io/archive/7.1/confluent-community-7.1.2.tar.gz?_ga=2.134073358.1620010104.1705371707-1047466751.1705371655&_gl=1*182lsyw*_ga*MTA0NzQ2Njc1MS4xNzA1MzcxNjU1*_ga_D2D3EGKSGD*MTcwNTM3MTY1NS4xLjEuMTcwNTM3MTgwMi42MC4wLjA.
$ sudo tar -xvf "/confluent/confluent-community-7.1.2.tar.gz?_ga=2.168151329.1620010104.1705371707-1047466751.1705371655"
환경 변수 설정
$ sudo vi ~/.bashrc
# 아래 2줄 추가 (confluent kafka 압축 해제한 폴더로)
export KAFKA_HOME=/home/tkbell/confluent-7.1.2
export PATH=.:$PATH:$KAFKA_HOME/bin
$ . .bashrc # 수정사항 반영
# 터미널 재시작 후 정상적으로 환경변수 등록 확인
$ echo $KAFKA_HOME
/home/tkbell/confluent-7.1.2
$ echo $PATH
.:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/tkbell/confluent-7.1.2/bin
home/$user 경로는 현재 자신 서버에 맞게 설정하면 된다.
Kafka 실행
기동은 Zookeeper를 먼저 실행하고 Kafka를 실행한다.
$ cd $KAFKA_HOME/etc/kafka/ # 해당 경로에 properties 파일들이 존재. 여기서 zookeeper.properties 파일과 함께 올려서 실행시킨다.
$ zookeeper-server-start ./zookeeper.properties mkdir: cannot create directory ‘/confluent/bin/../logs’: Permission denied # 아 sudo 안썼구나
[0.001s][error][logging] Error opening log file '/confluent/bin/../logs/zookeeper-gc.log': No such file or directory
[0.002s][error][logging] Initialization of output 'file=/confluent/bin/../logs/zookeeper-gc.log' using options 'filecount=10,filesize=100M' failed.
Invalid -Xlog option '-Xlog:gc*:file=/confluent/bin/../logs/zookeeper-gc.log:time,tags:filecount=10,filesize=100M', see error log for details.
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
$ sudo zookeeper-server-start ./zookeeper.properties
sudo: zookeeper-server-start: command not found # 아.....
# 그냥 쉘 스크립트를 실행하자....
$ sudo sh zookeeper-server-start $KAFKA_HOME/etc/kafka/zookeeper.properties
어차피 나중에 쉘로 실행할 거니까.. 일단 주키퍼 띄우고 그 상태에서 새 터미널을 띄워 kafka를 띄워보자.
$ cd $CONFLUENT_HOME
$ sudo sh kafka-server-start $KAFKA_HOME/etc/kafka/server.properties
...
[2024-01-16 12:07:57,039] INFO [KafkaServer id=0] started (kafka.server.KafkaServer)
[2024-01-16 12:07:57,189] INFO [BrokerToControllerChannelManager broker=0 name=alterIsr]: Recorded new controller, from now on will use broker ubuntu-20-minhalee.tkbell.gmarket.nh:9092 (id: 0 rack: null) (kafka.server.BrokerToControllerRequestThread)
[2024-01-16 12:07:57,213] INFO [BrokerToControllerChannelManager broker=0 name=forwarding]: Recorded new controller, from now on will use broker ubuntu-20-minhalee.tkbell.gmarket.nh:9092 (id: 0 rack: null) (kafka.server.BrokerToControllerRequestThread)
또 새 터미널을 띄워 topic을 만들어보자.
$ kafka-topics --bootstrap-server localhost:9092 --create --topic welcome-topic
Created topic welcome-topic.
종료할 때도 카프카를 먼저 죽이고 주키퍼를 죽이면 된다.
편하게 하게 위해서 일단 주키퍼 & 카프카 실행 쉘 스크립트를 만들어준다.
$ cd ~
$ sudo vi zoo_start.sh
sudo sh $KAFKA_HOME/bin/zookeeper-server-start $KAFKA_HOME/etc/kafka/zookeeper.properties
$ sudo vi kafka_start.sh
sudo sh $KAFKA_HOME/bin/kafka-server-start $KAFKA_HOME/etc/kafka/server.properties
$ sudo chmod +x *.sh # 누구나 실행할 수 있도록 권한 변경
$ ./zoo_start.sh
# 새 터미널 띄워서
$ ./kafka_start.sh
정상적으로 실행되면 완료!
로그 경로 변경
$ cd $KAFKA_HOME/etc/kafka
$ ls
connect-console-sink.properties connect-mirror-maker.properties server.properties
connect-console-source.properties connect-standalone.properties tools-log4j.properties
connect-distributed.properties consumer.properties trogdor.conf
connect-file-sink.properties kraft zookeeper.properties
connect-file-source.properties log4j.properties
connect-log4j.properties producer.properties
$ sudo vi server.properties
...
# A comma separated list of directories under which to store log files
log.dirs=/home/tkbell/data/kafka-logs
...
$ sudo vi zookeeper.properties
...
dataDir=/home/tkbell/data/zookeeper
...
Virtual Box를 이용해서 kafka 테스트를 진행한다면 tmp 경로가 날아가니까 다른 데로 변경해야 하는데 Tinker 사용한다면 딱히 바꿀 필요는 없다.
그래도 운영서버에 구축한다면 주로 로그를 많이 봐야 하므로 zookeeper와 kafka의 로그 경로를 home으로 변경해 준다.
카프카의 환경설정 파일은server.properties 이고 Zookeeper의 환경 설정 파일은zookeeper.properties 이다.
정상적으로 로그가 남는지 확인해 본다.
$ cd
$ kafka-topics --bootstrap-server localhost:9092 --create --topic welcome-topic # 신규 토픽 생성
Created topic welcome-topic.
$ cd ./data/kafka-logs/
$ ls
cleaner-offset-checkpoint meta.properties replication-offset-checkpoint
log-start-offset-checkpoint recovery-point-offset-checkpoint welcome-topic-0
welcome-topic-0이라는 이름으로 토픽이 생성되었다. 파티션 단위로 로그가 생겨나는 것을 확인할 수 있다.
무려 8개월만인데 그동안 회사를 이직하며 문서정리를 다 회사 wiki에 하느라 개인 블로그에 투자할 시간이 없었다..
지난 1년간 이직 후 배운게 너무 많고 공부한 것도 많아서 날잡고 쓰고 싶지만 이런 저런 핑계로 미뤄왔다.. 그러나 이 것만큼은 포스팅 해야겠다고 생각이 들어서 적어본다.
서버는 Ubuntu 20.0 LTS 깡통 기준으로 작성하였다.
Docker 설치
$ sudo apt-get install ca-certificates curl gnupg lsb-release
$ sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
$ echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
docker-ce-rootless-extras docker-scan-plugin pigz slirp4netns
Suggested packages:
aufs-tools cgroupfs-mount | cgroup-lite
The following NEW packages will be installed:
containerd.io docker-ce docker-ce-cli docker-ce-rootless-extras docker-compose-plugin docker-scan-plugin pigz slirp4netns
0 upgraded, 8 newly installed, 0 to remove and 4 not upgraded.
Need to get 74.3 kB/108 MB of archives.
After this operation, 449 MB of additional disk space will be used.
Do you want to continue? [Y/n] y
...
# 설치 확인
$ systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
Active: active (running) since Tue 2022-08-16 09:34:33 KST; 2min 34s ago
Docs: https://docs.docker.com
Main PID: 11179 (dockerd)
Tasks: 8
Memory: 34.5M
CGroup: /system.slice/docker.service
└─11179 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
# 부팅 시 자동 실행
$ sudo systemctl enable docker
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.
# 실행
$ docker-compose up -d
[+] Running 2/2
✔ Network app_default Created 0.1s
✔ Container mongodb Started 0.5s
# 확인
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bf72535cd54b dockerhub/mongo:6.0 "docker-entrypoint.s…" 5 minutes ago Up 5 minutes 0.0.0.0:20717->20717/tcp, 27017/tcp mongodb
$ docker logs mongodb
{"t":{"$date":"2023-07-27T04:05:32.295+00:00"},"s":"I", "c":"CONTROL", "id":23285, "ctx":"-","msg":"Automatically disabling TLS 1.0, to force-enable TLS 1.0 specify --sslDisabledProtocols 'none'"}
{"t":{"$date":"2023-07-27T04:05:32.298+00:00"},"s":"I", "c":"NETWORK", "id":4915701, "ctx":"main","msg":"Initialized wire specification","attr":{"spec":{"incomingExternalClient":{"minWireVersion":0,"maxWireVersion":17},"incomingInternalClient":{"minWireVersion":0,"maxWireVersion":17},"outgoing":{"minWireVersion":6,"maxWireVersion":17},"isInternalClient":true}}}
{"t":{"$date":"2023-07-27T04:05:32.300+00:00"},"s":"I", "c":"NETWORK", "id":4648601, "ctx":"main","msg":"Implicit TCP FastOpen unavailable. If TCP FastOpen is required, set tcpFastOpenServer, tcpFastOpenClient, and tcpFastOpenQueueSize."}
{"t":{"$date":"2023-07-27T04:05:32.302+00:00"},"s":"I", "c":"REPL", "id":5123008, "ctx":"main","msg":"Successfully registered PrimaryOnlyService","attr":{"service":"TenantMigrationDonorService","namespace":"config.tenantMigrationDonors"}}
{"t":{"$date":"2023-07-27T04:05:32.302+00:00"},"s":"I", "c":"REPL", "id":5123008, "ctx":"main","msg":"Successfully registered PrimaryOnlyService","attr":{"service":"TenantMigrationRecipientService","namespace":"config.tenantMigrationRecipients"}}
{"t":{"$date":"2023-07-27T04:05:32.302+00:00"},"s":"I", "c":"REPL", "id":5123008, "ctx":"main","msg":"Successfully registered PrimaryOnlyService","attr":{"service":"ShardSplitDonorService","namespace":"config.tenantSplitDonors"}}
{"t":{"$date":"2023-07-27T04:05:32.302+00:00"},"s":"I", "c":"CONTROL", "id":5945603, "ctx":"main","msg":"Multi threading initialized"}
{"t":{"$date":"2023-07-27T04:05:32.303+00:00"},"s":"I", "c":"CONTROL", "id":4615611, "ctx":"initandlisten","msg":"MongoDB starting","attr":{"pid":1,"port":27017,"dbPath":"/data/db","architecture":"64-bit","host":"bf72535cd54b"}}
{"t":{"$date":"2023-07-27T04:05:32.303+00:00"},"s":"I", "c":"CONTROL", "id":23403, "ctx":"initandlisten","msg":"Build Info","attr":{"buildInfo":{"version":"6.0.8","gitVersion":"3d84c0dd4e5d99be0d69003652313e7eaf4cdd74","openSSLVersion":"OpenSSL 3.0.2 15 Mar 2022","modules":[],"allocator":"tcmalloc","environment":{"distmod":"ubuntu2204","distarch":"x86_64","target_arch":"x86_64"}}}}
{"t":{"$date":"2023-07-27T04:05:32.303+00:00"},"s":"I", "c":"CONTROL", "id":51765, "ctx":"initandlisten","msg":"Operating System","attr":{"os":{"name":"Ubuntu","version":"22.04"}}}
{"t":{"$date":"2023-07-27T04:05:32.303+00:00"},"s":"I", "c":"CONTROL", "id":21951, "ctx":"initandlisten","msg":"Options set by command line","attr":{"options":{"net":{"bindIp":"*"},"security":{"authorization":"enabled"}}}}
{"t":{"$date":"2023-07-27T04:05:32.305+00:00"},"s":"I", "c":"STORAGE", "id":22270, "ctx":"initandlisten","msg":"Storage engine to use detected by data files","attr":{"dbpath":"/data/db","storageEngine":"wiredTiger"}}
{"t":{"$date":"2023-07-27T04:05:32.305+00:00"},"s":"I", "c":"STORAGE", "id":22297, "ctx":"initandlisten","msg":"Using the XFS filesystem is strongly recommended with the WiredTiger storage engine. See http://dochub.mongodb.org/core/prodnotes-filesystem","tags":["startupWarnings"]}
{"t":{"$date":"2023-07-27T04:05:32.306+00:00"},"s":"I", "c":"STORAGE", "id":22315, "ctx":"initandlisten","msg":"Opening WiredTiger","attr":{"config":"create,cache_size=1453M,session_max=33000,eviction=(threads_min=4,threads_max=4),config_base=false,statistics=(fast),log=(enabled=true,remove=true,path=journal,compressor=snappy),builtin_extension_config=(zstd=(compression_level=6)),file_manager=(close_idle_time=600,close_scan_interval=10,close_handle_minimum=2000),statistics_log=(wait=0),json_output=(error,message),verbose=[recovery_progress:1,checkpoint_progress:1,compact_progress:1,backup:0,checkpoint:0,compact:0,evict:0,history_store:0,recovery:0,rts:0,salvage:0,tiered:0,timestamp:0,transaction:0,verify:0,log:0],"}}
{"t":{"$date":"2023-07-27T04:05:33.303+00:00"},"s":"I", "c":"STORAGE", "id":4795906, "ctx":"initandlisten","msg":"WiredTiger opened","attr":{"durationMillis":997}}
{"t":{"$date":"2023-07-27T04:05:33.304+00:00"},"s":"I", "c":"RECOVERY", "id":23987, "ctx":"initandlisten","msg":"WiredTiger recoveryTimestamp","attr":{"recoveryTimestamp":{"$timestamp":{"t":0,"i":0}}}}
{"t":{"$date":"2023-07-27T04:05:33.319+00:00"},"s":"W", "c":"CONTROL", "id":5123300, "ctx":"initandlisten","msg":"vm.max_map_count is too low","attr":{"currentValue":65530,"recommendedMinimum":1677720,"maxConns":838860},"tags":["startupWarnings"]}
{"t":{"$date":"2023-07-27T04:05:33.322+00:00"},"s":"I", "c":"NETWORK", "id":4915702, "ctx":"initandlisten","msg":"Updated wire specification","attr":{"oldSpec":{"incomingExternalClient":{"minWireVersion":0,"maxWireVersion":17},"incomingInternalClient":{"minWireVersion":0,"maxWireVersion":17},"outgoing":{"minWireVersion":6,"maxWireVersion":17},"isInternalClient":true},"newSpec":{"incomingExternalClient":{"minWireVersion":0,"maxWireVersion":17},"incomingInternalClient":{"minWireVersion":17,"maxWireVersion":17},"outgoing":{"minWireVersion":17,"maxWireVersion":17},"isInternalClient":true}}}
{"t":{"$date":"2023-07-27T04:05:33.322+00:00"},"s":"I", "c":"REPL", "id":5853300, "ctx":"initandlisten","msg":"current featureCompatibilityVersion value","attr":{"featureCompatibilityVersion":"6.0","context":"startup"}}
{"t":{"$date":"2023-07-27T04:05:33.323+00:00"},"s":"I", "c":"STORAGE", "id":5071100, "ctx":"initandlisten","msg":"Clearing temp directory"}
{"t":{"$date":"2023-07-27T04:05:33.325+00:00"},"s":"I", "c":"CONTROL", "id":20536, "ctx":"initandlisten","msg":"Flow Control is enabled on this deployment"}
{"t":{"$date":"2023-07-27T04:05:33.325+00:00"},"s":"I", "c":"FTDC", "id":20625, "ctx":"initandlisten","msg":"Initializing full-time diagnostic data capture","attr":{"dataDirectory":"/data/db/diagnostic.data"}}
{"t":{"$date":"2023-07-27T04:05:33.330+00:00"},"s":"I", "c":"REPL", "id":6015317, "ctx":"initandlisten","msg":"Setting new configuration state","attr":{"newState":"ConfigReplicationDisabled","oldState":"ConfigPreStart"}}
{"t":{"$date":"2023-07-27T04:05:33.330+00:00"},"s":"I", "c":"STORAGE", "id":22262, "ctx":"initandlisten","msg":"Timestamp monitor starting"}
{"t":{"$date":"2023-07-27T04:05:33.333+00:00"},"s":"I", "c":"NETWORK", "id":23015, "ctx":"listener","msg":"Listening on","attr":{"address":"/tmp/mongodb-27017.sock"}}
{"t":{"$date":"2023-07-27T04:05:33.334+00:00"},"s":"I", "c":"NETWORK", "id":23015, "ctx":"listener","msg":"Listening on","attr":{"address":"0.0.0.0"}}
{"t":{"$date":"2023-07-27T04:05:33.334+00:00"},"s":"I", "c":"NETWORK", "id":23016, "ctx":"listener","msg":"Waiting for connections","attr":{"port":27017,"ssl":"off"}}
# 접속
$ docker exec -it mongodb mongosh
Current Mongosh Log ID: 64c1ef8c9f2da91f777540e2
Connecting to: mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+1.10.1
Using MongoDB: 6.0.8
Using Mongosh: 1.10.1
For mongosh info see: https://docs.mongodb.com/mongodb-shell/
To help improve our products, anonymous usage data is collected and sent to MongoDB periodically (https://www.mongodb.com/legal/privacy-policy).
You can opt-out by running the disableTelemetry() command.
test>
# admin db로 바꾸고 새 계정 생성
test> use admin
switched to db admin
admin> db.createUser({user:"minggu",pwd:"1234",roles:[{"role":"root","db":"admin"}]})
{ ok: 1 }
admin>
MongoDB 조작
#Ctrl + D로 MongoDB 종료, 한번 더 눌러서 컨테이너 종료
# 재접속
$ docker exec -it mongodb bash
root@5f64c1a55e7a:/# mongosh -u minggu -p 1234
Current Mongosh Log ID: 64c33770b43db69197467fbc
Connecting to: mongodb://<credentials>@127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+1.10.1
Using MongoDB: 6.0.8
Using Mongosh: 1.10.1
For mongosh info see: https://docs.mongodb.com/mongodb-shell/
------
The server generated these startup warnings when booting
2023-07-28T03:32:57.729+00:00: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine. See http://dochub.mongodb.org/core/prodnotes-filesystem
2023-07-28T03:32:58.747+00:00: vm.max_map_count is too low
------
# 데이터베이스 확인
test> show databases
admin 132.00 KiB
config 72.00 KiB
local 96.00 KiB
# 데이터베이스 생성
test> use exhibtionDB
switched to db exhibtionDB
exhibtionDB>
# 데이터 삽입
exhibtionDB> db.exhibitions.insert({"title":"Exhibition Test","createdBy":"minggu"})
DeprecationWarning: Collection.insert() is deprecated. Use insertOne, insertMany, or bulkWrite.
{
acknowledged: true,
insertedIds: { '0': ObjectId("64c33da9183745bf1c891027") }
}
# 데이터베이스 목록 확인
exhibtionDB> show dbs
admin 132.00 KiB
config 60.00 KiB
exhibtionDB 8.00 KiB
local 96.00 KiB
exhibtionDB>
var QueryCollection = function () {
this.collection = [];
};
QueryCollection.prototype.parseQuery = function (url) {
if (!url) {
return this;
}
var index = url.indexOf("?");
if (index < 0) {
return this;
}
var query = url.substr(index + 1);
var queryPairs = query.split("&");
for (var i = 0; i < queryPairs.length; i++) {
var pair = queryPairs[i];
var keyValue = pair.split("=");
if (keyValue.length < 2) {
continue;
}
var key = keyValue[0];
var value = keyValue[1];
if (!key || !value) {
continue;
}
this.collection[decodeURIComponent(key)] = decodeURIComponent(value);
}
return this;
};
그런데 만약 windows에서 발생한다면? 이것도 linux에서 하는 것과 크게 다르진 않는데 생소할 수 있으니 한번작업해보려고 한다.
1. netstat
터미널을 하나 열어서 작업할 건데명령 프롬프트든,파워쉘이든 본인이 원하는 터미널로 작업하면 된다.
linux나 windows나 netstat 명령어는 기본적으로 가지고 있다. (네트워크 세팅이 되어있다면)
$ netstat --help
프로토콜 통계와 현재 TCP/IP 네트워크 연결을 표시합니다.
NETSTAT [-a] [-b] [-e] [-f] [-n] [-o] [-p proto] [-r] [-s] [-t] [-x] [-y] [interval]
-a 모든 연결 및 수신 대기 포트를 표시합니다.
-b 각 연결 또는 수신 대기 포트 생성과 관련된 실행 파일을
표시합니다. 잘 알려진 실행 파일이 여러 독립 구성 요소를
호스팅할 경우 연결 또는 수신 대기 포트 생성과 관련된
구성 요소의 시퀀스가 표시됩니다.
이러한 경우에는 실행 파일 이름이 아래 [] 안에
표시되고 위에는 TCP/IP에 도달할 때까지
호출된 구성 요소가 표시됩니다. 이 옵션은 시간이
오래 걸릴 수 있으며 사용 권한이 없으면
실패합니다.
-e 이더넷 통계를 표시합니다. 이 옵션은 -s 옵션과 함께 사용할 수
있습니다.
-f 외부 주소의 FQDN(정규화된 도메인 이름)을
표시합니다.
-n 주소 및 포트 번호를 숫자 형식으로 표시합니다.
-o 각 연결의 소유자 프로세스 ID를 표시합니다.
-p proto proto로 지정한 프로토콜의 연결을 표시합니다. proto는
TCP, UDP, TCPv6 또는 UDPv6 중 하나입니다. -s 옵션과 함께
사용하여 프로토콜별 통계를 표시할 경우 proto는 IP, IPv6, ICMP,
ICMPv6, TCP, TCPv6, UDP 또는 UDPv6 중 하나입니다.
-q 모든 연결, 수신 대기 포트 및 바인딩된 비수신 대기 TCP
포트를 표시합니다. 바인딩된 비수신 대기 포트는 활성 연결과 연결되거나
연결되지 않을 수도 있습니다.
-r 라우팅 테이블을 표시합니다.
-s 프로토콜별 통계를 표시합니다. 기본적으로 IP, IPv6, ICMP,
ICMPv6, TCP, TCPv6, UDP 및 UDPv6에 대한 통계를 표시합니다.
-p 옵션을 사용하여 기본값의 일부 집합에 대한 통계만 지정할 수 있습니다.
-t 현재 연결 오프로드 상태를 표시합니다.
-x NetworkDirect 연결, 수신기 및 공유 끝점을
표시합니다.
-y 모든 연결에 대한 TCP 연결 템플릿을 표시합니다.
다른 옵션과 함께 사용할 수 없습니다.
interval 다음 화면으로 이동하기 전에 지정한 시간(초) 동안 선택한 통계를 다시 표시합니다.
통계 다시 표시를 중지하려면 <Ctrl+C>를 누르세요.
이 값을 생략하면 현재 구성 정보가
한 번만 출력됩니다.
우리는 pid를알야 내야하기 때문에 여기서 -a, -n, -o 옵션을 사용할 것이다.
2. netstat -ano
$ netstat -ano
활성 연결
프로토콜 로컬 주소 외부 주소 상태 PID
TCP 0.0.0.0:80 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 1304
TCP 0.0.0.0:445 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:623 0.0.0.0:0 LISTENING 5492
TCP 0.0.0.0:2701 0.0.0.0:0 LISTENING 5124
.....
모든 연결들 중에서 외부 주소 및 포트를 도메인이 아닌숫자 형태로PID와 함께 출력하라는 명령어이다.
기본적으로 -9 sigkill과 -15 terminate를 이용하는데 무슨 차이가 있는지는 리눅스 공부를 해보도록!
$ kill -9 11100
윈도에서는taskkill 명령어를 사용한다.
PS C:\Users\minggu92> taskkill /?
TASKKILL [/S 시스템 [/U 사용자 이름 [/P [암호]]]]
{ [/FI 필터] [/PID 프로세스 id | /IM 이미지 이름] } [/T] [/F]
설명:
이 도구는 프로세스 ID(PID) 또는 이미지 이름으로 작업을 종료하는 데
사용합니다.
매개 변수 목록:
/S 시스템 연결할 원격 시스템을 지정합니다.
/U [도메인\]사용자 명령을 실행해야 하는 사용자 컨텍스트를
지정합니다.
/P [암호] 해당 사용자 컨텍스트의 암호를 지정합니다.
생략한 경우에는 물어봅니다.
/FI 필터 작업 집합을 선택하는 필터를 적용합니다.
"*"를 사용할 수 있습니다. 예: imagename eq acme*
/PID 프로세스_ID 종료할 프로세스의 PID를 지정합니다.
TaskList를 사용하여 PID를 얻을 수 있습니다.
/IM 이미지 이름 종료할 프로세스의 이미지 이름을
지정합니다. 와일드카드 문자 '*'를 사용하여
모든 작업 또는 이미지 이름을 지정할 수 있습니다.
/T 지정된 프로세스와 그 프로세스로부터 시작된
모든 자식 프로세스를 종료합니다.
/F 프로세스를 강제로 종료하도록 지정합니다.
/? 이 도움말 메시지를 표시합니다.
필터:
필터 이름 유효한 연산자 유효한 값
----------- --------------- -------------------------
STATUS eq, ne RUNNING |
NOT RESPONDING | UNKNOWN
IMAGENAME eq, ne 이미지 이름
PID eq, ne, gt, lt, ge, le PID 값
SESSION eq, ne, gt, lt, ge, le 세션 번호.
CPUTIME eq, ne, gt, lt, ge, le CPU 시간 형식
hh:mm:ss
hh - 시간,
mm - 분, ss - 초
MEMUSAGE eq, ne, gt, lt, ge, le 메모리 사용(KB)
USERNAME eq, ne 사용자 이름([domain\]user
형식)
MODULES eq, ne DLL 이름
SERVICES eq, ne 서비스 이름
WINDOWTITLE eq, ne 창 제목
참고
----
1) /IM 스위치에 대한 와일드카드 문자 '*'는 필터가 적용될 때만
사용할 수 있습니다.
2) 원격 프로세스는 항상 강제적으로(/F) 종료될 수 있습니다.
3) 원격 컴퓨터가 지정되면 "WINDOWTITLE" 및 "STATUS" 필터는
지원되지 않습니다.
예:
TASKKILL /IM notepad.exe
TASKKILL /PID 1230 /PID 1241 /PID 1253 /T
TASKKILL /F /IM cmd.exe /T
TASKKILL /F /FI "PID ge 1000" /FI "WINDOWTITLE ne untitle*"
TASKKILL /F /FI "USERNAME eq NT AUTHORITY\SYSTEM" /IM notepad.exe
TASKKILL /S 시스템 /U domain\username /FI "USERNAME ne NT*" /IM *
TASKKILL /S 시스템 /U 사용자 이름 /P 암호 /FI "IMAGENAME eq note*"
PS C:\Users\minggu92> taskkill /pid 11100
오류: 프로세스(PID 11100)를 종료할 수 없습니다.
원인: 이 프로세스는 /F 옵션을 사용하여 강제로 종료해야 합니다.
PS C:\Users\minggu92> taskkill /pid 11100 /f
오류: 프로세스(PID 11100)를 종료할 수 없습니다.
원인: 액세스가 거부되었습니다.
--관리자모드로 실행시
PS C:\Windows\system32> taskkill /pid 11100 /f
성공: 프로세스(PID 11100)가 종료되었습니다.
Network클래스는 기기가 현재 연결된 네트워크 중 하나를 나타낸다.Network객체를 키로 사용하여ConnectivityManager와 함께 네트워크 정보를 수집하거나 네트워크에서 소켓을 결합할 수 있다. 네트워크 연결이 끊어지면Network객체는 사용이 중지되고 나중에 기기가 동일한 어플라이언스에 다시 연결되더라도 새Network객체는 새 네트워크를 나타낸다.
LinkProperties객체에는 네트워크에 설치된 DNS 서버, 로컬 IP 주소, 네트워크 경로 목록 등의 네트워크 연결 정보가 포함된다.
NetworkCapabilities객체에는 전송(Wi-Fi, 셀룰러, 블루투스) 및 네트워크에서 사용할 수 있는 기능과 같은 네트워크 속성 정보가 포함된다. 예를 들어, 객체를 쿼리 하여 네트워크가 MMS를 전송할 수 있는지, 종속 포털을 지원하는지, 데이터 전송량 제한이 있는지 확인할 수 있다.
C#에는 using keyword가 있다. C++에도 using 구문이 존재하고 사용법이 다른데 C#은 어떻게 사용하는지 보자.
두 가지 용도로 사용되는데
1. using 지시자
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Description;
특정 패키지를 삽입할 때 사용된다. java의 import, C언어의 include와 유사하다.
다음과 같이 별칭으로도 사용이 가능하다.
using System;
using tClass = ConsoleApp1.MyClass.testClass;
namespace ConsoleApp1
{
class Sample
{
static void Main(string[] args)
{
//사용하지 않을 때
ConsoleApp1.MyClass.testClass m1 = new ConsoleApp1.MyClass.testClass();
//사용했을 때
tClass m2 = new tClass();
}
}
}
namespace ConsoleApp1.MyClass
{
class testClass
{
public testClass()
{
Console.WriteLine("NewClass instantiated...");
}
}
}
이것이 무슨 말이냐면, File이나 Font 같은 관리되지 않는 리소스에 액세스 할 때는 사용 후 메모리를 Dispose 해주어야 하는데 이를 using 명령문 안에서 사용하면 자동으로 대행해준다.
일반적으로 using 구문을 사용하지 않는 메모리 해제는 try-finally 구문을 사용하여 다음과 같이 작성하여야 한다.
string manyLines = @"This is line one
This is line two
Here is line three
The penultimate line is line four
This is the final, fifth line.";
{
var reader = new StringReader(manyLines);
try
{
string? item;
do
{
item = reader.ReadLine();
Console.WriteLine(item);
} while (item != null);
}
finally
{
reader?.Dispose();
}
}
using 구문을 사용하면 다음과 같이 코드를 줄일 수 있다.
string manyLines = @"This is line one
This is line two
Here is line three
The penultimate line is line four
This is the final, fifth line.";
using (var reader = new StringReader(manyLines))
{
string? item;
do
{
item = reader.ReadLine();
Console.WriteLine(item);
} while (item != null);
}
// C# 8.0부터는 중괄호가 필요하지 않는다.
using var reader = new StringReader(manyLines);
string? item;
do
{
item = reader.ReadLine();
Console.WriteLine(item);
} while (item != null);
다음 예제와 같이 단일using문에서 한 형식의 여러 인스턴스를 선언할 수 있다. 단일 문에서 여러 변수를 선언하는 경우에는 암시적으로 형식화된 변수(var)를 사용할 수 없다.
string numbers = @"One
Two
Three
Four.";
string letters = @"A
B
C
D.";
using (StringReader left = new StringReader(numbers),
right = new StringReader(letters))
{
string? item;
do
{
item = left.ReadLine();
Console.Write(item);
Console.Write(" ");
item = right.ReadLine();
Console.WriteLine(item);
} while (item != null);
}
// C# 8.0에서 다음과 같이 사용 가능.
using StringReader left = new StringReader(numbers),
right = new StringReader(letters);
string? item;
do
{
item = left.ReadLine();
Console.Write(item);
Console.Write(" ");
item = right.ReadLine();
Console.WriteLine(item);
} while (item != null);
Process: com.ming.gu, PID: 28384
java.lang.OutOfMemoryError: Failed to allocate a 31961100 byte allocation with 16774160 free bytes and 16MB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.Bitmap.nativeCreate(Native Method)
at android.graphics.Bitmap.createBitmap(Bitmap.java:977)
at android.graphics.Bitmap.createBitmap(Bitmap.java:948)
at android.graphics.Bitmap.createBitmap(Bitmap.java:879)
at com.ming.gu.function.CustomCapturePad$Companion.rotateImage(CustomCapturePad.kt:203)
at com.ming.gu.function.CustomCapturePad.onActivityResult(CustomCapturePad.kt:173)
at com.ming.gu.document.EvidenceDocuFragment.onActivityResult(EvidenceDocuFragment.kt:240)
at androidx.fragment.app.FragmentManager$9.onActivityResult(FragmentManager.java:2889)
at androidx.fragment.app.FragmentManager$9.onActivityResult(FragmentManager.java:2869)
at androidx.activity.result.ActivityResultRegistry.doDispatch(ActivityResultRegistry.java:362)
at androidx.activity.result.ActivityResultRegistry.dispatchResult(ActivityResultRegistry.java:322)
at androidx.activity.ComponentActivity.onActivityResult(ComponentActivity.java:634)
at androidx.fragment.app.FragmentActivity.onActivityResult(FragmentActivity.java:164)
at android.app.Activity.dispatchActivityResult(Activity.java:7273)
at android.app.ActivityThread.deliverResults(ActivityThread.java:4520)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4567)
at android.app.ActivityThread.-wrap22(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1695)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6780)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1496)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1386)
흠.. 예전에 만들었던 앱에 무슨 에러가 그렇게 난다길래 보니까 OutOfMemory 에러가 발생했다.
아마 Custom pad를 만들어 사진 캡처하는 기능을 구현했었는데 이미지 처리하던 중 메모리 부족이 발생한 듯.