채팅서버 cassandra 연동하기 - DevCamp2Flame/FlameTalk_Server GitHub Wiki
flame 팀 백엔드 다롬, 수연의 채팅 서버 페어 프로그래밍
목차
- docker 설치
- docker cassandra 컨테이너 실행 및 데이터 생성
- spring boot - cassandra 연동하기
- cassandra repository test
1. docker 설치
[Windows 10] Docker 설치 완벽 가이드(Home 포함)
2. docker cassandra 컨테이너 실행 및 데이터 생성
Docker Hub Cassandra 공식 도커 이미지로 컨테이너 띄우기(feat. cqlsh 실행)
// 이미지 pull
docker pull cassandra
// 컨테이너 실행
docker container run --name chat_cassandra -p 9042:9042 -d cassandra:latest
// cassandra 접속
docker exec -it chat_cassandra cqlsh
이후 실행, 중지는 docker GUI 에서 컨트롤 가능합니다.
Connection error: ('Unable to connect to any servers', {'127.0.0.1:9042': ConnectionRefusedError(111, "Tried connecting to [('127.0.0.1', 9042)]. Last error: Connection refused")})
이런 에러가 난다면,
> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e4bcb2539bf7 cassandra:latest "docker-entrypoint.s…" About a minute ago Up About a minute 7000-7001/tcp, 7199/tcp, 9160/tcp, 0.0.0.0:9042->9042/tcp chat_cassandra
> docker exec -it e4bcb2539bf7 cqlsh
Connected to Test Cluster at 127.0.0.1:9042
[cqlsh 6.0.0 | Cassandra 4.0.1 | CQL spec 3.4.5 | Native protocol v5]
Use HELP for help.
cqlsh>
docker ps 명령어로 현재 실행하고 있는 container의 id 로 cqlsh 를 접속합니다.
2-1. Keyspace, table, data 만들기
keyspace 는 RDB의 스키마 같은 존재이고 table, data는 같은 개념입니다.
Keyspace 생성
// 기존 동일한 이름의 Keyspace 가 존재한다면 삭제
drop keyspace flametalk;
// Keyspace 생성
CREATE KEYSPACE IF NOT EXISTS flametalk WITH replication =
{'class':'NetworkTopologyStrategy','datacenter1':1};
// Keyspace 접속
use flametalk;
Keyspace 이름을 flametalk 로 지정하고 NetworkTopology 전략을 선택하며 replica 1개로 설정합니다. 1개 이상 설정하면 오류로 실행이 되지 않습니다!
Cassandra Container DB NoNodeAvailableException
- Docker로 Cassandra을 설치하였을 때, spring 에서 발생하는 node issue 문제 → NetworkTopologyStrategy, datacenter1 을 선택한 이유
table 생성
메시지 데이터 형식 : 채팅서버 구현정보 정리 의 채팅 메시지 데이터 참고
- message_id
- message_type
- sender_id
- nickname
- room_id
- contents
- file_url
- created_at
CREATE TABLE IF NOT EXISTS message (message_id text, message_type text, sender_id text, nickname text, room_id text, contents text, file_url blob, created_at timestamp, PRIMARY KEY (message_id));
data 생성
insert into message (message_id, message_type, sender_id, nickname, room_id, contents, created_at) values ('1', 'TALK', '1', 'darom', '1', 'hi', '2015-05-03 13:30:54.234');
insert into message (message_id, message_type, sender_id, nickname, room_id, file_url, created_at) values ('1', 'TALK', '1', 'darom', '1', 'url', '2015-05-03 13:30:54.234');
3. spring boot - cassandra 연동하기
SpringBoot - cassandra CRUD API 예제
application.yml
spring:
data:
cassandra:
port: 9042
contact-points: 127.0.0.1
schemaAction: CREATE_IF_NOT_EXISTS
keyspace-name: flametalk
local-datacenter: datacenter1
docker 로 띄웠기 때문에 contact-points 에 'localhost' 가 아니라 docker ip 주소를 작성해야 함 or 127.0.0.1
[윈도우 Docker IP 주소]
C:\Windows\System32\drivers\etc 폴더 - hosts 파일 (메모장으로 열기) - Add by Docker Desktop 찾기
CassandraConfig.java
@Configuration
@EnableCassandraRepositories(basePackages = { "com.devcamp.flametalk.domain" })
public class CassandraConfig {
@Bean
public CqlSessionFactoryBean session() {
CqlSessionFactoryBean session = new CqlSessionFactoryBean();
session.setContactPoints("127.0.0.1");
session.setKeyspaceName("flametalk");
session.setLocalDatacenter("datacenter1");
session.setPort(9042);
return session;
}
@Bean
public SessionFactoryFactoryBean sessionFactory(CqlSession session, CassandraConverter converter) {
SessionFactoryFactoryBean sessionFactory = new SessionFactoryFactoryBean();
sessionFactory.setSession(session);
sessionFactory.setConverter(converter);
sessionFactory.setSchemaAction(SchemaAction.NONE);
return sessionFactory;
}
@Bean
public CassandraMappingContext mappingContext(CqlSession cqlSession) {
CassandraMappingContext mappingContext = new CassandraMappingContext();
mappingContext.setUserTypeResolver(new SimpleUserTypeResolver(cqlSession));
return mappingContext;
}
@Bean
public CassandraConverter converter(CassandraMappingContext mappingContext) {
return new MappingCassandraConverter(mappingContext);
}
@Bean
public CassandraOperations cassandraTemplate(SessionFactory sessionFactory, CassandraConverter converter) {
return new CassandraTemplate(sessionFactory, converter);
}
}
[주의] basePackages 에 repository 위치를 잘 작성해야 한다!
Message.java
@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
@Table("message")
public class Message {
@Column
@PrimaryKey
private String message_id;
@Column
private String message_type;
@Column
private String sender_id;
@Column
private String nickname;
@Column
private String room_id;
@Column
private String contents;
@Column
private String file_url;
@Column
private LocalDateTime created_at;
}
MessageRepository.java
public interface MessageRepository extends CassandraRepository<Message, String> {
}
여기까지 완성하면, 연결은 성공! 아래서 테스트를 진행합니다.
4. cassandra repository test
MessageRepositoryTest.java
@SpringBootTest({"spring.data.cassandra.port=9042",
"spring.data.cassandra.keyspace-name=flametalk"})
@ExtendWith(SpringExtension.class)
class MessageRepositoryTest {
@Autowired
private MessageRepository messageRepository;
@Test
void save() {
// given
String messageId = "1";
String messageType = "ENTER";
String senderId = "2";
String nickname = "darom";
String roomId = "3";
String contents = "hihi";
LocalDateTime date = LocalDateTime.now();
Message message = Message.builder()
.message_id(messageId)
.message_type(messageType)
.sender_id(senderId)
.nickname(nickname)
.room_id(roomId)
.contents(contents)
.created_at(date)
.build();
// when
Message save = messageRepository.save(message);
// then
assertEquals(save.getMessage_id(), messageId);
}
}
테스트를 실행하면 이미 넣어놨던 데이터가 아닌 새로운 데이터가 테이블에 추가됐음을 알 수 있다.
> select * from message;
마치며
docker도 nosql-cassandra도 test도 어느 하나 익숙한 것 없고 심지어 처음 사용해보는 것이 대부분인데, 어떻게든 해내겠다는 집념으로 테스트까지 성공했다는 것이 너무 감격스럽다 ㅜㅜ......
오늘 채팅 서버에 cassandra를 연동하기 위해 열어본 크롬창만 100개가 넘을 것 같다.😢
그래도 정말 다행인 점은 cassandra cqls가 sql 문과 거의 같다는 것이 한 줄기의 빛 같았다.
앞으로도 서로가 서로의 단점을 보완해주며 채팅 서버 완성까지 달려봅시다~!! 오늘도 수고 많으셨습니다.❤