构建和部署微服务应用可能会非常具有挑战性。 这通常是因为,使用传统框架时,需要花费大量精力来设置和管理必要的基础设施。设置好之后,还需要确保服务正常运作、安全和可扩展,并且能够彼此连接和沟通。
Encore.ts 是一个开源的 TypeScript 框架,解决了这些问题并简化了整个过程。 它让你能够构建强大的分布式系统,其中大部分工作会自动处理。
在这篇文章中,你将学习如何使用Encore.ts构建微服务,并将微服务部署到你的AWS账户中的Kubernetes集群。我们将展示Encore Cloud,一个Encore的托管DevOps自动化平台,如何自动化部署过程,从设置Kubernetes集群,配置所有必要的IAM策略及其他资源,直至你的微服务部署完成。
前提条件本教程的代码在这里可以找到这里,欢迎克隆并跟着一起做微服务,一起学习。
安装 Encore CLI 来启动你的本地开发环境:
brew install encoredev/tap/encore
(Mac 操作系统)curl -L https://encore.dev/install.sh | bash
(Linux 系统)iwr https://encore.dev/install.ps1 | iex
(iwr
表示使用 PowerShell 下载文件,iex
表示执行下载的脚本)(Windows 操作系统)我们用Encore.ts先创建一个微服务。我们将构建一个简单的博客微服务来展示,展示如何用“Encore.ts”部署微服务。
运行以下命令以创建一个新的Encore.ts应用程序:
执行 encore app 创建 blog-microservices
全屏模式,退出全屏
下面的命令将提示您为应用程序及其项目模板选择语言。应类似于下方的截图所示选择。
现在进入到项目文件夹并使用以下命令运行你的应用程序:
cd blog-microservices && encore run
进入全屏 退出全屏
下面的命令会在您的浏览器页面中打开API。
我们现在来看我们这个微服务应用的文件结构。在你的项目中创建如下文件结构。
blog-microservices/ ├── encore.app ├── posts-service/ │ ├── encore.service.ts // 服务定义 │ ├── posts.ts // API接口 │ └── migrations/ │ └── 1_create_posts.up.sql // 创建posts的迁移文件 └── comments-service/ ├── encore.service.ts // 服务定义 ├── comments.ts // API接口 └── migrations/ └── 1_create_comments.up.sql // 创建comments的迁移文件
进入全屏模式,退出全屏模式
在这个项目结构中,我们有两个微服务,分别是posts-service
和comments-service
。posts-service
处理与帖子相关的所有逻辑和功能,例如创建新帖子、获取帖子信息、更新和删除帖子。而comments-service
则处理所有与评论相关的内容。这样,你的应用就被解耦了,你可以轻松独立地管理每个服务。
在创建了应用和项目文件之后,我们现在来创建数据库和模式。
在你的 posts-service/posts.ts
文件里添加如下代码以设置帖子数据库。
import { SQLDatabase } from "encore.dev/storage/sqldb"; // 数据库初始化 const db = new SQLDatabase("comments", { migrations: "./迁移", });
全屏 退出全屏
然后将你的posts-service/migrations/1_create_posts.up.sql
文件更新为,添加以下代码来定义posts
表结构:
-- 下面的SQL语句是用来创建一个名为posts的表,这个表存储了文章的相关信息。 CREATE TABLE posts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid() -- 文章ID,主键,使用随机生成的UUID title TEXT NOT NULL, -- 文章标题,不能为空 content TEXT NOT NULL, -- 文章内容,不能为空 author_name TEXT NOT NULL, -- 作者姓名,不能为空 created_at TIMESTAMP NOT NULL DEFAULT NOW() -- 创建时间,不能为空,使用当前时间 );
全屏,退出全屏
上述代码将创建一个包含如下字段的文章表。
id
: 一个随机生成的唯一标识,用于区分每个帖子title
: 每个帖子的标题content
: 博客内容author_name
: 发帖者的用户名created_at
: 发帖时间接下来,在你的 comments-service/posts.ts
文件中添加以下代码,以创建一个评论数据表:
import { SQLDatabase } from "encore.dev/storage/sqldb"; // 设置数据库 const db = new SQLDatabase("posts", { // 表名:posts migrations: "./migrations", });
全屏显示 退出全屏
更新 comments-service/migrations/1_create_comments.up.sql
文件来创建 comments
表结构。
CREATE TABLE comments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), post_id UUID NOT NULL, 文本 TEXT NOT NULL, 作者名 TEXT NOT NULL, 创建时间戳 TIMESTAMP NOT NULL DEFAULT NOW(), FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE );
点击全屏模式, 点击退出全屏
上述代码将创建一个评论表格,包含以下字段:
id
: 一个随机生成的 ID,用于唯一标识每个评论。post_id
: 关联的帖子 ID,指向帖子表。content
: 帖子的实际评论内容。author_name
: 评论者的名称。created_at
: 评论创建时间。现在更新你的 posts-service/posts
文件,为帖子服务微服务创建 REST API 服务。我们将添加一些端点,用于创建帖子、获取所有帖子以及根据 ID 获取帖子。
//... import { api, Query } from "encore.dev/api"; import { Topic } from "encore.dev/pubsub"; //... // 接口定义 interface PostEvent { id: string; title: string; authorName: string; action: "created" | "updated" | "deleted"; } export const postCreatedTopic = new Topic<PostEvent>("post-created", { deliveryGuarantee: "at-least-once", }); interface Post { id: string; title: string; content: string; authorName: string; createdAt: Date; } interface CreatePostRequest { title: string; content: string; authorName: string; } interface ListPostsRequest { limit?: Query<number>; offset?: Query<number>; } interface ListPostsResponse { posts: Post[]; total: number; } export const createPost = api( { method: "POST", path: "/posts", expose: true, }, async (req: CreatePostRequest): Promise<Post> => { const post = await db.queryRow<Post>` INSERT INTO posts (title, content, author_name) VALUES (${req.title}, ${req.content}, ${req.authorName}) RETURNING id, title, content, author_name as "authorName", created_at as "createdAt" `; await postCreatedTopic.发布({ id: post?.id as string, title: post?.title as string, authorName: post?.authorName as string, action: "created", }); return post as Post; } ); export const getPost = api( { method: "GET", path: "/posts/:id", expose: true, }, async (params: { id: string }): Promise<Post> => { return (await db.queryRow<Post>` SELECT id, title, content, author_name as "authorName", created_at as "createdAt" FROM posts WHERE id = ${params.id} `) as Post; } ); export const listPosts = api( { method: "GET", path: "/posts", expose: true, }, async (params: ListPostsRequest): Promise<ListPostsResponse> => { const limit = params.limit || 10; const offset = params.offset || 0; // 计算总数 const totalResult = await db.queryRow<{ count: string }>` SELECT COUNT(*) as count FROM posts `; const total = parseInt(totalResult?.count || "0"); // 获取分页的帖子 const posts = await db.query<Post>` SELECT id, title, content, author_name as "authorName", created_at as "createdAt" FROM posts ORDER BY created_at DESC LIMIT ${limit} OFFSET ${offset} `; const result: Post[] = []; for await (const post of posts) { result.push(post); } return { posts: result, total, }; } );
进入全屏 退出全屏
在上面的代码示例中,我们定义了一系列的 TypeScript 接口和 API 路由,用来管理帖子的服务。PostEvent
接口用于塑造发送到消息主题(postCreatedTopic
)的消息,这个主题用于记录帖子的创建、更新和删除等操作。Post
接口主要定义了博客帖子的结构,而 CreatePostRequest
、ListPostsRequest
和 ListPostsResponse
则提供了服务端点请求和响应处理的类型。我们创建了三条路由:createPost
用于将新帖子添加到数据库,并发布相关事件到主题;getPost
通过 ID 获取特定帖子;listPosts
提供分页帖子数据。
接下来,更新 comments-service/posts
以创建评论服务的 REST API。
//... import { api, Query } from "encore.dev/api"; //... // 类型 interface Comment { id: string; postId: string; content: string; authorName: string; createdAt: Date; } interface CreateCommentRequest { postId: string; content: string; authorName: string; } interface ListCommentsRequest { limit?: Query<number>; offset?: Query<number>; postId: string; } interface ListCommentsResponse { comments: Comment[]; } // 获取评论列表 export const listComments = api( { method: "GET", path: "/comments/:postId", expose: true, }, async (params: ListCommentsRequest): Promise<ListCommentsResponse> => { const limit = params.limit || 10; const offset = params.offset || 0; const comments = await db.query<Comment>` SELECT id, post_id as "postId", content, author_name as "authorName", created_at as "createdAt" FROM comments WHERE post_id = ${params.postId} ORDER BY created_at DESC LIMIT ${limit} OFFSET ${offset} `; const result = (await comments).map(comment => comment); return { comments: result }; } );
切换到全屏 退出全屏
在这个代码里,我们定义了接口和API路由来管理博客文章的评论。Comment
接口定义了每个评论的数据模型,包括如postId
和createdAt
这样的字段。CreateCommentRequest
、ListCommentsRequest
和ListCommentsResponse
接口则结构化了请求和响应,确保端点间交互的类型安全。我们实现了一个listComments
功能,用来获取指定文章的分页评论。
首先,我们需要在Encore中将每个微服务定义为一个服务。
在 posts-service/encore.service.ts
文件中添加以下代码:
import { Service } from "encore.dev/service"; // 导入Service类,并创建一个名为"posts"的服务实例 export default new Service("posts");
全屏 退出全屏
请在 comments-service/encore.service.ts
文件中添加以下代码,以实现评论功能:
import { Service } from "encore.dev/service"; export default new Service("评论");
切换到全屏,退出全屏
接下来,要在 comments
服务中调用在 posts
服务中的这些端点,只需如下所示在 comments
服务中导入这些端点。
import { posts } from "~encore/clients";
点击全屏/退出全屏
现在你可以像调用普通函数一样调用 comments
服务的各个端点,如下所示:
// API export const createComment = api( { method: "POST", path: "/comments", expose: true, }, async (req: CreateCommentRequest): Promise<Comment> => { // 检查帖子是否存在 const post = await posts.getPost({ id: req.postId as string }); if (!post) { throw new Error("未找到帖子"); } return (await db.queryRow<Comment>` INSERT INTO comments (post_id, content, author_name) VALUES (${req.postId}, ${req.content}, ${req.authorName}) RETURNING id, post_id as "postId", content, author_name as "authorName", created_at as "createdAt" `) as Comment; } );
切换到全屏模式 退出全屏模式
在这里,我们创建了一个名为 createComment
的功能,它允许用户给现有的帖子添加评论(在验证该帖子存在后),我们导入了帖子服务,并使用它来调用 getPost
方法以检查用户想评论的帖子是否存在。
现在我们来测试一下微服务的API路由。回到你的API测试工具,测试一下你的端点。
部署到 Kubernetes:既然你已经成功运行了微服务应用,让我们使用Encore Cloud(Encore的DevOps自动化管理托管服务)自动将其部署到你的AWS(亚马逊网络服务)账户中的Kubernetes集群中。
我们将部署您的微服务应用到一个全新的Kubernetes集群中。您可以在那里找到将微服务部署到现有Kubernetes集群的指南文档:点击这里。
运行命令以部署应用程序:
git add -A . git commit -m '第一次部署' git push encore # 再次或特定项目/环境名
全屏(点击进入/退出)
将你的Encore.ts微服务部署到Kubernetes集群的第一步是将你的云账号(如AWS或GCP)连接到Encore Cloud中的应用程序。在这个教程中,我们将使用例如亚马逊网络服务(AWS)作为例子。按照以下步骤将你的AWS账号连接到Encore Cloud中的应用。
选择您的应用
在身份和访问管理(IAM)控制台中转到 创建角色 页面。
选择 另一个 AWS 账户 作为账户类型。
复制并粘贴您的 账户 ID 从 Encore Cloud。
选择 要求提供外部 ID 选项。
复制您的 外部 ID 从 Encore Cloud 并粘贴到 AWS 的 外部 ID 字段。
将 管理员访问 权限策略附加到该角色(Encore 代表您创建资源所需)。
输入角色名称和描述,然后点击 创建角色 按钮。
现在已经成功连接了 AWS 控制台到你的 Encore.ts 云,让我们继续创建一个新的环境来部署微服务到 Kubernetes。接下来,请按照以下步骤操作:
选择 AWS 作为云服务提供商。
一旦您设置了这些配置,点击创建按钮以启动新的环境。Encore将根据您的环境配置在Kubernetes上部署基础设施。
创建了环境,如下图所示:
在您的应用程序正在部署时,您可以在 Encore Cloud 仪表板中查看部署状态和环境详情。您也可以通过 kubectl
命令行工具查看您的 Kubernetes 集群。
这次教程里,你学会了如何将你的 Encore.ts 微服务应用程序构建和部署到 Kubernetes(通常简称 K8s)。
我们首先了解了Encore.ts是什么,以及它是如何帮助开发人员解决构建和部署微服务过程中遇到的挑战。
然后我们搭建了一个微服务应用,并将其部署到了AWS上的Kubernetes集群,使用了Encore Cloud平台。
既然你知道它是怎么运作的,也许你可以试着给这个应用添加一些新功能。
相关链接: