TypeScript 및 Azure를 사용하는 서버리스 마이크로 서비스가 포함된 간단한 채팅 앱
소개
우리 팀은 인프라를 마이그레이션하면서 서버리스 마이크로 서비스 아키텍처를 프로젝트에 적용할 계획이므로 이에 대해 배우기 시작했습니다. 그리고 아키텍처를 보여주는 최소한의 예제 앱이 많지 않은 것 같습니다. 그래서 며칠 전에 서버리스 마이크로서비스 데모로 간단한 채팅 앱을 개발하려고 했습니다. 이 기사에서는 구현의 관점에서 몇 가지 사항을 공유합니다.
다음은 repo 입니다.
참고: 이 기사에서 마이크로서비스 아키텍처에 대한 개념 설명은 건너뛰겠습니다. (이미 그것에 관한 많은 기사가 있으며 찾을 수 있습니다.) 아이디어에 대해 더 빨리 알고 싶다면 먼저 이 리소스를 읽을 수 있습니다.
앱 작동 방식
프로젝트가 정말 간단해졌습니다. 사용자는 양식에 이름을 입력하고 채팅 화면으로 이동합니다. 사용자가 채팅에 참여하면 알림이 전송됩니다. (이 앱에는 인증 및 유효성 검사 부분이 없습니다.)
다음은 스크린샷입니다.
기술 스택
프로젝트 아키텍처
다음은 프로젝트 아키텍처의 모습입니다.
이 앱은 사용자 서비스와 채팅 서비스의 두 가지 서비스로 구성됩니다.
두 서비스 모두 Azure Functions를 기반으로 합니다. 메시지 브로커(Azure Service Bus)는 사용자가 채팅에 참여하여 채팅 서비스에 알림을 보내도록 지시할 때 사용됩니다. SignalR 메시지는 새 메시지가 Cosmos DB에 저장될 때마다 Azure SignalR로 전송되며 클라이언트 앱은 메시지를 수신하고 채팅 화면에 표시되는 메시지를 업데이트합니다.
사용자 서비스
사용자 서비스(
src/functions/Users/
)를 살펴보겠습니다.사용자 서비스에는 사용자 API가 있습니다. 사용자가 채팅 화면에 들어올 때 사용자 정보를 Cosmos DB에 저장한 다음 Azure Service Bus의 NewUsersQueue라는 큐에 메시지를 보내 채팅 서비스에서 채팅에 참여한 사람을 파악하고 알림을 보낼 수 있도록 합니다.
user/index.ts
import { AzureFunction, Context, HttpRequest } from "@azure/functions";
import { ServiceBusClient } from "@azure/service-bus";
import { connectDB } from "../db";
import { User } from "../models/user";
const httpTrigger: AzureFunction = async (
context: Context,
req: HttpRequest
): Promise<void> => {
const sbClient = new ServiceBusClient(process.env["SERVICE_BUS_CONNECTION_STRING"]);
try {
await connectDB();
switch (req.method) {
case "POST":
if (req?.body?.name) {
const user = User.build({ name: req.body.name });
const savedUser = await user.save();
const sender = sbClient.createSender("NewUsersQueue");
const message = {
body: JSON.stringify(savedUser),
};
await sender.sendMessages(message);
await sender.close();
context.res = {
body: savedUser,
};
} else {
throw "Invalid parameter";
}
break;
default:
throw `${req.method} is not allowed`;
}
} catch (err) {
context.log(`Error: ${err}`);
context.res = {
status: 500,
body: err,
};
} finally {
await sbClient.close();
}
};
export default httpTrigger;
채팅 서비스
다음으로 채팅 서비스(
src/functions/Chat/
)를 살펴보겠습니다.채팅 서비스에는 메시지 API, 협상 기능 및 newUserNotification 기능이 있습니다.
메시지 API는 GET 요청으로 Cosmos DB에서 메시지 문서를 검색하고 메시지 문서를 Cosmos DB에 저장한 다음 POST 요청으로 newMessage 이벤트가 있는 SignalR 메시지를 Azure SignalR로 보냅니다.
message/index.ts
import { AzureFunction, Context, HttpRequest } from "@azure/functions";
import { ReadPreference } from "mongodb";
import { connectDB } from "../db";
import { Message } from "../models/message";
const httpTrigger: AzureFunction = async (
context: Context,
req: HttpRequest
): Promise<void> => {
try {
await connectDB();
switch (req.method) {
case "GET":
const query = Message.find({}).read(ReadPreference.NEAREST);
const messages = await query.exec();
context.res = {
body: messages,
};
break;
case "POST":
const { uid, from, body } = req?.body;
if (uid && from && body) {
const message = Message.build({ uid: uid, from: from, body: body });
const savedMessage = await message.save();
context.bindings.signalRMessages = [
{
target: "newMessage",
arguments: [savedMessage],
},
];
context.res = {
body: savedMessage,
};
} else {
throw "Invalid parameter";
}
break;
default:
throw `${req.method} is not allowed`;
}
} catch (err) {
context.log(`Error: ${JSON.stringify(err)}`);
context.res = {
status: 500,
body: err,
};
}
};
export default httpTrigger;
협상 함수는 클라이언트 앱이 Azure SignalR에 연결할 수 있도록 액세스 토큰 및 연결 정보를 반환합니다.
negotiate/index.ts
import { AzureFunction, Context, HttpRequest } from "@azure/functions";
const httpTrigger: AzureFunction = async (
context: Context,
req: HttpRequest,
connectionInfo: any
): Promise<void> => {
context.res.body = connectionInfo;
};
export default httpTrigger;
newUserNotification 함수는 Azure Service Bus의 NewUsersQueue 메시지에 의해 트리거됩니다. 함수가 트리거된 후 클라이언트 앱의 알림처럼 보이는 메시지 문서를 Cosmos DB에 저장한 다음 newMessage 이벤트와 함께 SignalR 메시지를 Azure SignalR로 보냅니다.
newUserNotification/index.ts
import { AzureFunction, Context } from "@azure/functions";
import { connectDB } from "../db";
import { Message } from "../models/message";
const serviceBusQueueTrigger: AzureFunction = async (
context: Context,
mySbMsg: any
): Promise<void> => {
try {
await connectDB();
const newUser = JSON.parse(mySbMsg);
const message = Message.build({
from: "",
body: `${newUser.name} joined the chat.`,
});
const result = await message.save();
context.bindings.signalRMessages = [{
target: "newMessage",
arguments: [result]
}];
} catch (err) {
context.log(`Error: ${JSON.stringify(err)}`);
}
};
export default serviceBusQueueTrigger;
프런트엔드
마지막으로 React 클라이언트 앱(
src/frontend/
)을 살펴보겠습니다.api
폴더 안에 있는 파일은 서비스에 대한 API 호출을 실행하는 역할을 합니다.components/Chat/index.tsx
파일에서 SignalR 클라이언트 라이브러리를 사용한 구현이 있고 Azure SignalR에서 newMessage 이벤트로 메시지를 수신하는 것을 볼 수 있습니다.const connectSignalR = async (): Promise<void> => {
try {
const signalRInfo = await negotiateAPI.post();
if (signalRInfo) {
const options = {
accessTokenFactory: () => signalRInfo.accessToken,
};
connection = new signalR.HubConnectionBuilder()
.withUrl(signalRInfo.url, options)
.configureLogging(signalR.LogLevel.None)
.build();
connection.on("newMessage", (msg: Message) => {
setMessages((prev) => [...prev, msg]);
});
connection.start();
}
} catch (err) {
console.log(err);
}
};
결론
실제 응용 프로그램의 경우 아키텍처가 훨씬 더 복잡해야 하지만 이 데모 응용 프로그램이 몇 가지 기본 개념을 이해하는 데 도움이 되기를 바랍니다.
repo 페이지에 프로젝트 설정 가이드를 작성했으므로 이 프로젝트를 로컬에서 실행하려면 this을 참조하십시오.
언제든지 피드백을 주세요!
Reference
이 문제에 관하여(TypeScript 및 Azure를 사용하는 서버리스 마이크로 서비스가 포함된 간단한 채팅 앱), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/spiderhand/simple-chat-app-with-serverless-microservices-using-typescript-and-azure-3bk5텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)