0%

shuttle 平台简介

shuttle 是一个 PaaS 平台,用于部署 rust 后端服务,支持多个流行的 rust web 框架,如 axum,actix-web、rocket 等。作为对比的话,类似于 heroku(python) 和 deno deploy 平台。

部署 shuttle 服务

将一个现有的 rust web 服务改造并部署到 shuttle 平台上非常简单。拿 axum 框架距离只需要在 cargo.toml 中添加 shuttle-runtime 与 shuttle-axum 依赖,并简单地修改 main 函数即可。

1
2
3
4
5
6
7
#[shuttle_runtime::main]
async fn axum() -> shuttle_axum::ShuttleAxum {

let router = Router::new().route("/", get(hello_world));

Ok(router.into())
}

然后通过命令行工具即可将服务部署到 shuttle 平台上。

1
2
3
4
# cargo install cargo-shuttle ## 安装cargo shuttle
# cargo shuttle login ## 登录shuttle
# cargp shuttle project start ## 创建项目
cargo shuttle deploy # 部署服务

实践

花了一点时间,我将之前开发的二维码生成服务qr-web-rs部署到了 shuttle 平台上,可以通过以下两个链接访问。

总结

shuttle 平台体验相当不错,并且有不少的免费额度,可以用于个人 rust 服务的部署。

前缀树(Trie 树,也称为字典树、单词查找树)是一种树形数据结构,用于高效地存储和检索字符串集合中的键。前缀树的主要优势在于能够快速地查找具有相同前缀的字符串,并且对于大量的字符串集合,它可以提供较高的检索效率。

前缀树的应用非常广泛,包括:

  • 字符串检索:通过前缀树可以快速查找是否存在某个字符串,或者查找具有相同前缀的所有字- 符串。
  • 自动完成:前缀树可以用于实现自动完成功能,根据用户输入的前缀提供可能的建议。
  • IP 路由:在路由表中,前缀树用于快速匹配最长前缀。

前缀树可以通过多种方式实现,在 Python 中最简单且直观的方式是用嵌套的 dict 实现。

首先定义一下前缀树的接口,应该包括 insert、search 和 startswith 三个方法。

1
2
3
4
5
6
7
8
9
10
trie = Trie()
trie.insert("app")
trie.insert("apple")
trie.insert("banana")
assert trie.startswith("a")
assert trie.startswith("ba")
assert not trie.startswith("c")
assert trie.search("app")
assert not trie.search("appl")
assert trie.search("apple")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Trie:
def __init__(self):
self.root = {-1: False}

def insert(self, word: str):
parent = self.root
for ch in word:
node = parent.get(ch, {-1: False})
parent[ch] = node
parent = node
parent[-1] = True

def __find(self, word:str):
node = self.root
for ch in word:
if ch not in node:
return None
node = node[ch]
return node

def search(self, word: str) -> bool:
node = self.__find(word)
return node is not None and node[-1]

def startswith(self, prefix: str) -> bool:
node = self.__find(prefix)
return node is not None
Read more »

我在最近的前端项目中用到了 vite 与 swc 作为构建工具和 JavaScript 编译器,二者都比较年轻,但提供更优秀的前端开发体验。当下前端生态的新选手层出不穷,本文就简单介绍一下最近几年前端生态涌现的诸多新兴的技术与工具。

Javascript 运行时

Deno

Deno是一个使用 rust 开发,基于 V8 引擎的简单、现代、安全的 Javascript 和 TypeScript 运行时。
我在 2021 年的时候写过一篇文章介绍deno。最近两年 Deno 的发展比较迅速,引入了一些新的 API 与能力,提高了稳定性以及与 Node.js 的兼容性。
顺便提一下Deno Deploy平台,可以方便地部署 Deno 项目为 severless 应用。

Bun

Bun是一个使用 zig 语言开发的基于 JavaScriptCore 引擎的全功能 JavaScript 和 TypeScript 工具集。

JavaScript 编译器与打包器

SWC

SWC是一个可扩展的基于 Rust 的下一代快速 Web 开发工具,同时支持 JavaScript 的编译与打包。Deno 和 Next.js 都使用了 SWC 作为编译器。

esbuild

esbuild是一个极快的 web 打包工具,使用 go 语言编写。

bun bundler

bun 对 JavaScript 模块的打包有原生的支持(官方的 benchmark 中比 esbuild 要快很多)。

Node.js 版本管理工具

volta

volta是一个使用 Rust 开发的新兴的 Node.js 版本管理工具。我目前正在使用 volta 管理 Node.js 版本,可以简单地根据项目切换 Node.js(包括 npm 与 yarn)的版本,安装不同版本的 Node.js 也很快。

fnm

fnm是一个快速、简单的 Node.js 版本管理工具,使用 Rust 开发。

Node.js 包管理工具

yarn v2+

新版本yarn做了很多改进,比如使用 Plug'n'Play 模式进行依赖项解析,提高性能与可靠性;支持零安装模式;对 monorepo 的支持更好等。

pnpm

pnpm是一个快速、节省空间的包管理器。

构建工具

parcel

parcel是一个快速、零配置的 Web 应用程序打包工具,支持 JavaScript、TypeScript、CSS、HTML、JSON 等文件的打包。parcel 2.0 版本使用了 swc 作为编译器,速度快了很多。

vite

vite是一个新兴的前端构建工具,在开发时使用 HMR 技术实现秒级的热更新,编译时使用 esbuild 和 Rollup 进行编译与打包,速度非常快。vite 也是 vue3 的官方构建工具。

tuborpack

tuborpack是一个使用 rust 开发的针对 JavaScript 和 TypeScript 优化的渐进式打包工具,由 Webpack 的原作者开发。

前端框架(React.js 生态)

next.js

next.js是一个 React 框架,提供了 SSR、SSG、CSR 等多种渲染模式,同时提供了很多优秀的功能,比如图片优化、代码分割、预取、预加载、路由、静态资源优化等。

Remix

Remix是一个全栈 web 框架,支持 SSR 与 CSR。

最近因工作需要,需要开发一个后台管理系统。考虑到开发效率和技术成熟度,决定使用 React 和 Ant Design 的技术栈。

之前我一直使用 create-react-app 来创建 React 项目,但是 create-react-app 已经很久没有更新了,新版的react 文档也不再推荐使用 create-react-app,而是推荐使用更成熟的框架,比如 next.js 与 remix。next.js 与 remix 都是比较大的 SSR 框架,对于简单的后台管理系统来说,可能有点大材小用。

作为一个新兴的前端构建工具,vite 的成熟度日益提升,对于 React.js 的支持也已经很完善,所以我决定使用 vite 来开发这个后台管理系统。

Read more »

最近获取到了腾讯云的的 Serveeless Stable Difussion 的内测资格,部署一个无服务器版本的 SD Web UI,总体体验比较良好。

部署流程

获取内测资格

首先需要获取内测资格,可以通过这个链接申请。

创建 Serverless 应用

访问Serverless 应用控制台,点击“新建应用”。

create serverless app

选择应用模板

在应用市场分类下选择“AI 绘画(Stable Diffusion)应用”模板,填写应用名称,点击“下一步。”。

choose app template

应用配置

填写应用名称,选择地域,点击“完成”,等待应用创建完成。

cofig app

deploy log

体验 SD Web UI

serverless 应用创建完成后,可以在应用详情页查看应用的访问地址。

app url

sd web ui

图片生成速度还是很快的,不弱于 Google Colab 的体验。以下是使用默认提示词和配置生成的赛博朋克主题的城堡图片。

sample image

总结

在公有云 GPU 机器价格高昂的环境下,使用 serverless 无服务器应用部署 AI 应用或许是一个不错的选择。

Python 是一门动态类型语言,没有编译器对变量类型正确性的检查与保证,这也意味着经常需要在运行时对变量的类型进行校验,尤其是在后端接口开发中,毕竟前端传入的数据往往是不可控的。

Python 3.5 引入了类型注解与 typing 模块,可以对 Python 代码进行静态类型检查,很大程度上提高了代码的可读性与可维护性,尤其是在较大的项目中。

除了静态类型检查,Python 的类型注解也可以在应用在运行时,例如 FastAPI(Pydantic) 就是利用了类型注解来进行请求参数解析、数据校验和 OpenAPI 文档生成的。

实际上 Python 标准库提供了一个简单的运行时类型检查的能力。

使用 isinstance 进行类型检查

isinstance 函数最常见的用法是判断一个对象是否是某个类型(及其子类)的实例,例如:

1
2
3
isinstance(1, int)  # True
isinstance("hello", str) # True
isinstance(None, object) # True

使用isinstance检查抽象类型

到了类型注解的时代,我们可以使用 isinstance 来检查一个对象是否实现了某个抽象接口,例如:

1
2
3
from typing import Callable, Iterable
isinstance(print, Callable) # True
isinstance([1, 2, 3], Iterable) # True

有一些遗憾的是,这里并不能为抽象类型添加范型参数(毕竟对容器的每个元素进行类型检查是一个非常耗时的事情),例如:

1
isinstance([1,2,3], list[int]) # TypeError: isinstance() argument 2 cannot be a parameterized generic

使用 isinstance 检查 typing.Protocol 自定义类型

Python 3.8 引入了 typing.Protocol 类与typing.runtime_checkable装饰器,可以用来定义类型,然后在运行时对对象进行类型检查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from typing import Protocol, runtime_checkable
from dataclasses import dataclass

@runtime_checkable
class HasName(Protocol):
name: str

def say_hello(obj: HasName) -> None:
assert isinstance(obj, HasName), "obj must have a name attribute"
print(f"Hello {obj.name}")

@dataclass
class Person:
name: str

@dataclass
class Dog:
nick: str


say_hello(Person("John")) # Hello John
say_hello(Dog("Bobby")) # AssertionError: obj must have a name attribute

总结

虽然会带来一定的性能损耗,但是运行时的类型检查在很多场景下都是必要的,isinstance 函数可以帮助我们实现这一目的。
对于更复杂的类型检查,可以借助 dataclass 或者 pydantic。
值得一提的是 pydantic 的 2.0 版本使用 rust 上线了核心的数据校验逻辑,性能上有了很大的提升。

今天在自己的 PC 上部署和体验了ChatGLM-6B的推理服务,简单记录一下流程。

web ui

ChatGLM-6B 简介

ChatGLM-6B 是一个开源的、支持中英双语的对话语言模型,基于 General Language Model (GLM) 架构,具有 62 亿参数。结合模型量化技术,用户可以在消费级的显卡上进行本地部署(INT4 量化级别下最低只需 6GB 显存)。
ChatGLM-6B 使用了和 ChatGPT 相似的技术,针对中文问答和对话进行了优化。经过约 1T 标识符的中英双语训练,辅以监督微调、反馈自助、人类反馈强化学习等技术的加持,62 亿参数的 ChatGLM-6B 已经能生成相当符合人类偏好的回答,更多信息请参考我们的博客

Read more »

晚上闲来无事,和 New Bing 就 solid.js 主题进行了一些交流,在十来轮的对话中,我对 solid.js 有了更深的了解。

编写的一个示例组件

calculator

节选的对话内容

介绍一下 solid.js

Solid.js 是一个用于创建用户界面的声明式 JavaScript 库 ¹。它受到 React 的大影响,但与 React 不同的是,它不使用虚拟 DOM,而是将模板编译为真实的 DOM 节点,并用细粒度的反应来更新它们 ⁴。你可以在你的应用中声明你的状态,并在任何地方使用它,当状态发生变化时,只有依赖于它的代码才会重新运行 ⁴。
Solid.js 的优点是性能高,语法简洁,灵活度大,开发体验好 ¹。它在许多 UI 速度和内存利用率的基准测试中都名列前茅 ¹。它还支持所有预期的现代库特性,甚至改进了一些特性以提高开发体验 ¹。

Solid.js 目前还是一个相对新的库,可能还没有 React 那么成熟和稳定。如果你想了解更多关于 Solid.js 的信息,你可以访问官方网站 ¹,或者查看教程 ² 和 GitHub 仓库 ⁴。

源: 与必应的对话, 2023/4/15
(1) SolidJS · Reactive Javascript Library. https://www.solidjs.com/.
(2) GitHub - solidjs/solid: A declarative, efficient, and flexible …. https://github.com/solidjs/solid.
(3) SolidJS · Reactive Javascript Library. https://www.solidjs.com/tutorial.
(4) 次世代の React? Solid.js について - Zenn. https://zenn.dev/nicky/articles/754f0ca74c887a.
(5) 5 Places SolidJS is not the Best - DEV Community. https://dev.to/this-is-learning/5-places-solidjs-is-not-the-best-5019.

Solid.js如何实现细粒度的反应?

Solid.js实现细粒度的反应的原理是基于观察者模式²。它使用一个全局的栈来在运行时追踪哪些信号(Signal)被读取或写入,以及哪些效果(Effect)或记忆(Memo)依赖于它们¹。当一个信号的值发生变化时,它会通知所有订阅了它的效果或记忆,并重新执行它们的函数¹。这样,只有真正需要更新的部分才会重新运行,而不是整个虚拟DOM³。

这种反应性的方式非常强大和灵活,它可以处理动态变化的依赖关系,比如通过执行不同的条件分支¹。它也可以通过多层间接性来工作,任何在跟踪范围内执行的函数也会被跟踪¹。你可以把Solid.js的反应性看作是一个自动化的事件系统,你只需要声明你的数据和视图,剩下的更新逻辑都由框架来处理。

源: 与必应的对话, 2023/4/15
(1) What is Reactivity - docs.solidjs.com. https://docs.solidjs.com/references/concepts/reactivity.
(2) SolidJS · Reactive Javascript Library. https://www.solidjs.com/guides/reactivity.
(3) Reactivity with SolidJS - Frontend Masters. https://frontendmasters.com/courses/reactivity-solidjs/.
(4) First look at fine-grained reactivity in Solid - SolidJS Source Code …. https://jser.dev/react/2023/02/26/reactivity-in-solidjs.html.
(5) SolidJS · Reactive Javascript Library. https://www.solidjs.com/?ref=workingsoftware-dev.

举个例子说明信号、效果和记忆的用法。

好的,我来给你一个简单的例子。假设我们要做一个计算器,可以输入两个数字,并显示它们的和、差、积和商。我们可以用信号来表示输入的数字,用效果来监听输入的变化并更新显示的结果,用记忆来缓存计算的结果。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { createSignal, createEffect, createMemo } from "solid-js";

const Calculator = () => {
// 创建两个信号,表示输入的数字
const [num1, setNum1] = createSignal(0);
const [num2, setNum2] = createSignal(0);

// 创建四个记忆,表示计算的结果
const sum = createMemo(() => num1() + num2());
const diff = createMemo(() => num1() - num2());
const prod = createMemo(() => num1() * num2());
const quot = createMemo(() => num1() / num2());

// 创建一个效果,当输入或结果发生变化时,打印到控制台
createEffect(() => {
console.log(`num1: ${num1()}, num2: ${num2()}`);
console.log(`sum: ${sum()}, diff: ${diff()}, prod: ${prod()}, quot: ${quot()}`);
});

// 返回一个JSX元素,包含两个输入框和四个显示结果的标签
return (
<div>
<input type="number" value={num1()} onInput={(e) => setNum1(e.target.value)} />
<input type="number" value={num2()} onInput={(e) => setNum2(e.target.value)} />
<p>Sum: {sum()}</p>
<p>Diff: {diff()}</p>
<p>Prod: {prod()}</p>
<p>Quot: {quot()}</p>
</div>
);
};
Read more »