| modified | Saturday 16 May 2026 |
|---|
临时记录的文字。
+++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++
不要期望第一遍阅读能看懂所有内容,不要期望第一遍阅读就解答心中的所有疑问。
王立铭:重度kindle用户与科普写作爱好者
浙江大学生命科学研究院教授王立铭。
在《知识分子》推出的“科学家与书”这个新栏目中,每期我们将会邀请一位科学家,回答若干关于阅读和书的问题。
大堆的“休闲”书,大堆的“严肃”书。
成功学鸡汤文一类的书我是绝对不看的,绝对不看的书也就谈不上“不喜欢”。
1go env -w GO111MODULE='on'
2go env -w GOSUMDB=sum.golang.google.cn
3go env -w GOPROXY=https://goproxy.cn,direct
Java
性能分析工具可以帮助开发人员分析应用程序的性能瓶颈、内存使用情况和线程问题。
以下是一些常用的性能分析命令和工具汇总:
JVM Process Status Tool。查看运行中的 Java 进程的状态信息,包括进程 ID
和主类名。可用于迅速了解系统上运行中的 Java 应用程序。
语法
1jps [ options ] [ hostid ]
其中,options 是可选的命令行选项,hostid是可选的 RMI 注册表主机 ID。
下面是一些常用的选项:
-q:只输出进程ID,不包括类名和JAR文件名。
-m:输出传递给 main 方法的参数。
-l:输出主类全名,或者对于JAR文件,输出JAR文件路径。
-v:输出传递给 JVM 的参数。
示例
假设您有一个正在运行的Java应用程序,其主类为com.example.MyApp,并且它的进程ID为12345。
下面是一些使用jps命令的示例:
查看所有 Java 进程的进程 ID 和主类名:
1$ jps 12345 com.example.MyApp 67890 sun.tools.jps.Jps
在上面的输出中,12345 是您的Java应用程序的进程 ID,com.example.MyApp
是应用程序的主类名。
只输出 Java 进程的进程 ID:
1$ jps -q 12345 67890
输出 Java 进程的进程 ID 和传递给 main 方法的参数:
1$ jps -m 12345 com.example.MyApp arg1 arg2 67890 sun.tools.jps.Jps -m
输出 Java 进程的进程 ID 和主类全名:
1$ jps -l 12345 com.example.MyApp 67890 sun.tools.jps.Jps
jinfo 是 Java 命令行工具,用于查看和修改正在运行的 Java 进程的 Java
虚拟机(JVM)参数和系统属性。使用 jinfo,可以动态地检查和更改 Java
应用程序的配置信息,而无需停止应用程序。
在很多情况下,Java 应用程序不会指定所有的 JVM 参数,开发人员可能不知道某一个具体
JVM 参数的默认值。在这种情况下,jinfo 就能很方便地查看 JVM 参数的当前值。
语法
1jinfo [options] pid
其中,options 可以是以下选项之一:
-flags:打印指定 JVM 的参数值。
-sysprops:打印Java虚拟机的系统属性。
-flag name:打印指定名称的标志参数的值。
-flag [+|-]name:打印或设置指定名称的布尔标志参数的值。
pid 是正在运行的Java进程的进程ID。
示例
示例1:查看标志参数:
1$ jinfo -flags 12345 Attaching to process ID 12345, please wait... Debugger
2attached successfully. Server compiler detected. JVM version is 11.0.12+7
3Non-default VM flags: -XX:CICompilerCount=3 -XX:InitialHeapSize=268435456
4-XX:MaxHeapSize=4294967296 -XX:MaxNewSize=1431306240
5-XX:MinHeapDeltaBytes=524288 -XX:NewSize=89128960 -XX:OldSize=179306496
6-XX:+UseCompressedClassPointers -XX:+UseCompressedOops
7-XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC Command line:
8-XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296
9-XX:MaxNewSize=1431306240 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=89128960
10-XX:OldSize=179306496 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
11-XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC
12
13VM Flags: -XX:CICompilerCount=3 -XX:InitialHeapSize=268435456
14-XX:MaxHeapSize=4294967296 -XX:MaxNewSize=1431306240
15-XX:MinHeapDeltaBytes=524288 -XX:NewSize=89128960 -XX:OldSize=179306496
16-XX:+UseCompressedClassPointers -XX:+UseCompressedOops
17-XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC
示例 2:查看系统属性:
1$ jinfo -sysprops 12345 Attaching to process ID 12345, please wait... Debugger
2attached successfully. Server compiler detected. JVM version is 11.0.12+7
3Non-default VM flags: -XX:CICompilerCount=3 -XX:InitialHeapSize=268435456
4-XX:MaxHeapSize=4294967296 -XX:MaxNewSize=1431306240
5-XX:MinHeapDeltaBytes=524288 -XX:NewSize=89128960 -XX:OldSize=179306496
6-XX:+UseCompressedClassPointers -XX:+UseCompressedOops
7-XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC Command line:
8-XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296
9-XX:MaxNewSize=1431306240 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=89128960
10-XX:OldSize=179306496 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
11-XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC
12
13Property settings: awt.toolkit = sun.awt.windows.WToolkit java.class.path =
14/path/to/your/application.jar ...
示例 3:查看特定标志参数的值:
1$ jinfo -flag UseG1GC 12345 Attaching to process ID 12345, please wait...
2Debugger attached successfully. Server compiler detected. JVM version is
311.0.12+7 Non-default VM flags: -XX:CICompilerCount=3
4-XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296
5-XX:MaxNewSize=1431306240 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=89128960
6-XX:OldSize=179306496 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
7-XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC Command line:
8-XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296
9-XX:MaxNewSize=1431306240 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=89128960
10-XX:OldSize=179306496 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
11-XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC
12
13UseG1GC=true
示例 4:修改特定标志参数的值:
1$ jinfo -flag +PrintGCDetails 12345 Attaching to process ID 12345, please
2wait... Debugger attached successfully. Server compiler detected. JVM version is
311.0.12+7 Non-default VM flags: -XX:CICompilerCount=3
4-XX:InitialHeapSize=268435456
jstack(Java Stack Trace)命令用于生成 Java
进程的线程转储信息。它可以显示每个线程的堆栈跟踪,帮助您找出可能的死锁、死循环和线程问题。
语法
1jstack [ options ] pid
其中,options 是命令选项,pid 是目标 Java 进程的进程 ID。
以下是一些常用的命令选项:
-F:强制生成线程堆栈,即使Java进程没有响应。
-m:除了线程堆栈,还包括每个线程的本地(本地方法)信息。
-l:除了线程堆栈,还包括锁信息。
-h:显示帮助信息。
示例
使用 jstack 命令分析线程问题。
设一个 Java 应用程序,出现了死锁的情况。使用 jstack命令来分析线程问题。
首先,找到目标 Java 进程的进程 ID(pid),可以使用
jps命令来查看正在运行的Java进程:
jps 假设您找到了要分析的 Java 进程的进程 ID 为 12345,使用
jstack命令来生成线程堆栈信息:
1jstack 12345
命令会输出每个线程的调用栈信息,您可以在输出中查找线程状态、锁信息等。
设Java应用程序中有两个线程,一个正在等待锁,而另一个持有该锁。这可能导致死锁。通过运行
jstack 命令,您可以获得类似以下的输出:
"Thread-1" #10 prio=5 os_prio=0 tid=0x00007f36e8001000 nid=0x5303 waiting for
monitor entry [0x00007f36e3100000] java.lang.Thread.State: BLOCKED (on object
monitor) at com.example.MyClass.method1(MyClass.java:50) - waiting to lock
<0x00000000e3033c88> (a java.lang.Object) at
com.example.MyClass.run(MyClass.java:100)
"Thread-2" #11 prio=5 os_prio=0 tid=0x00007f36e8001800 nid=0x5304 waiting on
condition [0x00007f36e300f000] java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method) at
com.example.MyClass.method2(MyClass.java:75) at
com.example.MyClass.run(MyClass.java:120)
在这个示例中,Thread-1正在等待获取一个锁,而 Thread-2在持有锁的线程中等待。
这可能是一个死锁的迹象,您可以根据这些信息来分析并解决线程问题。
jmap(Java Memory Map)命令用于生成 Java进程的内存映射信息。
它提供了堆的详细信息,包括
Java堆内存、对象统计和内存使用情况的详细信息,可以帮助开发人员分析内存泄漏、内存使用情况等问题。
语法
1jmap [options] <pid>
其中,options 是一些可选的命令选项,<pid> 是 Java进程的进程 ID。
常用的 jmap 命令选项包括:
-heap: 显示Java堆内存使用情况。
-histo: 显示Java堆内存中的对象统计信息。
-dump:
hprof),
-finalizerinfo: 显示等待终结者队列中的对象。
-F: 在无法连接到进程时,强制执行转储操作。
4.2 示例 示例1:使用jmap命令分析 Java 堆内存
假设您有一个 Java 应用程序正在运行,您想要分析其 Java
堆内存使用情况,以便查找内存泄漏问题。您可以使用以下命令:
jmap -heap
会输出 Java 堆内存的使用情况,包括堆的大小、已使用内存、空闲内存等。
示例2:使用jmap命令生成堆内存转储文件
假设您怀疑 Java 应用程序存在内存泄漏,您可以使用 jmap
命令生成堆内存转储文件,以便后续分析。以下是一个示例:
jmap -dump:format=b,file=heapdump.bin
表示将转储以二进制格式保存,file=heapdump.bin 指定转储文件名。您可以将
替换为实际的 Java 进程 ID。运行此命令后,jmap 会生成一个名为 heapdump.bin
的转储文件,您可以使用其他工具进行分析。
注意事项:
使用jmap命令时,建议在测试或开发环境中进行,避免在生产环境中使用,因为生成堆内存转储文件可能会影响应用程序的性能。
jmap命令可能需要JVM的调试权限,因此确保您有足够的权限来运行该命令。
转储文件可能会相当大,特别是在内存使用量较大的情况下。确保您有足够的磁盘空间来存储转储文件。
总之,jmap 命令是一个有用的工具,可以帮助开发人员分析 Java
应用程序的内存使用情况,查找内存泄漏问题,并生成堆内存转储文件以进行后续分析。
jconsole(Java Monitoring and Management Console)是 Java
自带的监控和管理控制台,它提供了一个图形化界面,允许您监视和管理正在运行的 Java
应用程序的性能和资源使用情况。
jconsole是一个非常有用的工具,可以帮助开发人员识别问题、进行性能调优和监控Java应用程序。
5.1 主要功能
实时监控: 可以实时监控 Java
应用程序的内存使用情况、线程状态、垃圾回收、类加载等。
堆内存分析: 提供了对堆内存的监控和分析,可以查看对象数量、内存占用等信息。
线程分析: 可以帮助您检查线程的状态、堆栈跟踪和 CPU
使用情况,帮助您发现死锁和性能问题。
垃圾回收分析: 提供了垃圾回收的详细信息,可以查看垃圾回收的频率和效果。
MBean管理: 可以连接到 MBean(管理bean),允许您管理和监控应用程序的 MBean。
5.2 示例
以下是使用 jconsole 的示例,以监控一个正在运行的 Java 应用程序:
启动您的 Java 应用程序。假设您的应用程序是一个简单的 Java 程序,例如:
1public class MyApp { public static void main(String[] args) { while (true) {
2System.out.println("Running..."); try { Thread.sleep(1000); } catch
3(InterruptedException e) { e.printStackTrace(); } } } }
打开终端并运行以下命令,启动 jconsole:
1jconsole
在 jconsole 界面中,您可以看到一个列表,显示了正在运行的 Java
进程。选择您的应用程序进程并点击“连接”。
jconsole:新建连接
在 jconsole
的不同选项卡中,您可以查看内存使用情况、线程状态、垃圾回收信息等。例如,您可以在“内存”选项卡中查看堆内存使用情况。
jconsole:内存
在“线程”选项卡中,您可以查看每个线程的状态、堆栈跟踪等信息,以帮助您识别潜在的线程问题。
jconsole:线程
jconsole 是一个强大的 Java
性能监控和管理工具,提供了丰富的功能来监视和分析应用程序的性能和资源使用情况。通过
jconsole,开发人员可以轻松地识别问题、分析性能瓶颈,以及进行调优,从而提升Java应用程序的性能和效率。
Java VisualVM(Java Visual Monitoring and Troubleshooting
Tool)是一款功能强大的多合一故障诊断和性能监控的图形化工具,它集成了多种性能统计工具的功能,使用它可以代替jmap、jstack
甚至 jconsole。
它是JDK的一部分,可以帮助您实时监控应用程序的各种指标,如内存使用、线程情况、垃圾回收等,以便发现和解决性能问题。
6.1 主要功能
实时监控:
允许您实时监控Java应用程序的运行状态。您可以查看内存使用情况、线程状态、CPU利用率等指标。
内存分析:
提供了内存分析工具,可以帮助您识别内存泄漏和对象分配情况。您可以查看堆内存的内容,分析对象引用关系。
线程分析: 可以监控和分析应用程序中的线程状态,帮助您发现死锁、线程争用等问题。
垃圾回收分析:
工具可以显示垃圾回收的详细信息,帮助您确定垃圾回收的类型、频率和影响。
CPU分析: 可以帮助您分析 CPU 利用率高的原因,找出耗费 CPU 资源的部分。
多种插件: 支持各种插件,扩展了其功能。您可以安装插件来支持不同的 Java
应用程序和特定的性能分析需求。
6.2 示例 监控内存和 CPU 使用。
启动 Java VisualVM: 您可以在JDK的 bin 目录中找到jvisualvm.exe(Windows)或
jvisualvm(Linux/macOS)并运行它。
1jvisualvm
连接到应用程序: 在 Java
VisualVM中,点击的“远程”按钮,然后在“远程”面板中添加要连接的远程或本地Java进程。
监控性能:
连接到应用程序后,您可以查看实时性能监控信息,包括内存、线程、CPU使用等。您还可以选择不同的监视选项,如“监视”、“内存”、“线程”等标签。
jvisualvm:监控
进行内存分析:
在“抽样器”标签中,您可以生成并分析堆转储,查看对象引用关系、占用内存的对象等。
jvisualvm:抽样器
线程分析和垃圾回收分析:
在“线程”和“监视”标签中,您可以监控线程状态、识别死锁,以及查看垃圾回收行为。
jvisualvm:线程
总之,Java
VisualVM是一个强大的性能分析工具,可以帮助您监控、分析和优化Java应用程序的性能。通过实时监控、内存分析、线程分析等功能,您可以更深入地了解应用程序的运行情况,并解决性能问题。
Java性能分析是确保应用程序效率的关键步骤。掌握这些工具,有助于更好地管理和优化Java应用程序。
不同类型的环境变量,如系统环境变量、进程级环境变量、Java启动参数设置的系统属性以及Spring
Boot配置文件中的环境变量,之间存在优先级差异。本文详细介绍这些环境变量的优先级,以帮助开发人员更好地配置和管理Java应用程序。
系统环境变量
系统环境变量是操作系统层面的配置项,全局可见。Java应用程序会继承这些系统环境变量,但不会直接使用。
这一层级的环境变量通常用于设置操作系统的全局配置,而不是针对某个具体的Java应用。
示例: 设在操作系统中设置了一个名为
JAVA_HOME的系统环境变量,指向Java的安装目录。
尽管Java应用程序继承了这个变量,但它并不直接用于应用程序的特定配置。
进程级环境变量
进程级环境变量是在Java进程启动时设置的,可在启动脚本或命令行中指定。
这一层级的环境变量会覆盖系统环境变量,但只在Java进程的生命周期内有效。
进程级环境变量的优势在于可以根据应用程序的不同运行环境进行定制化配置。
示例:通过启动脚本或命令行参数 -D 设置Java进程的环境变量,例如:
1java -Dspring.profiles.active=dev -jar myapp.jar
这里的
spring.profiles.active是一个进程级环境变量,用于指定Spring应用程序的激活配置文件。
2.3 Java启动参数 (-D参数) 通过 -D
参数可以在启动Java进程时设置Java系统属性。这些属性可以通过 System.getProperty()
方法在Java应用程序中获取。Java启动参数设置的系统属性优先级较高,通常用于指定应用程序的一些关键配置。
示例:
1java -Dserver.port=8080 -jar myapp.jar
在这个例子中,server.port
是一个Java系统属性,它会覆盖进程级环境变量和系统环境变量中的同名属性。
2.4 Spring Boot配置文件中的环境变量 在Spring Boot应用程序的配置文件中,可以使用
${}
语法引用环境变量。这样的环境变量可以是系统环境变量、进程级环境变量,甚至是Java启动参数设置的系统属性。Spring
Boot配置文件中的环境变量通常用于配置应用程序的各种属性,如数据库连接、端口号等。
示例:
server: port: ${SERVER_PORT:8080} 在这个例子中,如果环境变量 SERVER_PORT
存在,则使用该值,否则默认使用 8080。
Java应用中各类环境变量的优先级总结如下:
Java启动参数设置的系统属性(-D参数):优先级最高。 进程级环境变量:次高优先级。
系统环境变量:优先级最低。 Spring
Boot配置文件中的环境变量:介于进程级环境变量和系统环境变量之间,可通过动态设置或默认值实现更灵活的配置。
动态切换配置文件:通过Java启动参数设置系统属性,可以实现在不同环境中动态切换配置文件,提高灵活性。
保护敏感信息:避免将敏感信息硬编码在代码中,通过环境变量传递,可以在不同环境中轻松更改这些信息。
日志级别控制:使用Java启动参数设置系统属性,可以在运行时动态调整日志级别,有助于排查问题。
多环境配置:利用Spring
Boot配置文件中的环境变量,可以为不同环境提供特定的配置,如数据库连接信息、服务端口等。
Golang 的多架构编译功能依赖于两个关键的环境变量:GOOS 和 GOARCH。其中,GOOS
表示目标操作系统,而 GOARCH 表示目标架构。通过设置这两个环境变量,我们可以告诉
Go 编译器在编译过程中要生成的目标平台。
编写代码:准备好你的 Go 代码,例如 main.go 文件,其中包含了你的应用程序逻辑。
设置环境变量:在开始编译之前,首先需要设置 GOOS 和 GOARCH 环境变量。例如,要为
Linux 64 位编译,可以使用命令 export GOOS=linux 和 export GOARCH=amd64。
编译:使用 go build 命令进行编译,同时指定目标操作系统和架构。例如,要在 CentOS
amd64 上为 Linux 64 位编译,可以使用命令 GOOS=linux GOARCH=amd64 go build -o
hello-linux-amd64 main.go。
验证编译结果:编译完成后,可以验证生成的可执行文件是否能够在目标平台上正常运行。
多架构编译 以下是一个示例,你可以使用环境变量 GOOS 和 GOARCH
来为不同的平台编译你的程序,并生成适用于不同操作系统和架构的可执行文件。
1package main
2
3import "fmt"
4
5func main() { fmt.Println("Hello, World!") }
使用以下命令,我们可以将这个Go 程序编译为多个目标平台的可执行文件:
1# 编译为 Linux 64 位可执行文件
2
3GOOS=linux GOARCH=amd64 go build -o hello-linux-amd64 main.go
4
5# 编译为 Windows 64 位可执行文件
6
7GOOS=windows GOARCH=amd64 go build -o hello-windows-amd64.exe main.go
8
9# 编译为 ARM 64 位可执行文件
10
11GOOS=linux GOARCH=arm64 go build -o hello-linux-arm64 main.go
12
13# 编译为 macOS 64 位可执行文件
14
15GOOS=darwin GOARCH=amd64 go build -o hello-darwin-amd64 main.go
通过以上命令,我们可以在不同的操作系统和架构上编译出相应的可执行文件,使得我们的应用程序能够在各种环境中运行。
编写多架构编译脚本
为了方便编译多个架构,可以编写一个脚本来自动编译多个目标平台。
创建 build.sh 脚本:
1#!/bin/bash
2
3OUTPUT_DIR="build" PLATFORMS=("linux/amd64" "linux/arm64" "windows/amd64"
4"darwin/amd64")
5
6mkdir -p $OUTPUT_DIR
7
8for PLATFORM in "${PLATFORMS[@]}"; do
9 OS=$(echo $PLATFORM | cut -d'/' -f1)
10 ARCH=$(echo $PLATFORM | cut -d'/' -f2)
11 OUTPUT_NAME=$OUTPUT_DIR/hello-$OS-$ARCH
12
13 if [ $OS = "windows" ]; then
14 OUTPUT_NAME+='.exe'
15 fi
16
17 echo "Building for $OS/$ARCH..."
18 GOOS=$OS GOARCH=$ARCH go build -o $OUTPUT_NAME main.go
19
20done
使脚本可执行:
1chmod +x build.sh
运行脚本:
1./build.sh
这个脚本会在 build 目录中生成多个目标平台的可执行文件。
对于一些架构,例如 ARM,你可能需要安装特定的交叉编译工具链。
当发行版仓库不提供 gcc-arm-linux-gnu 和 gcc-aarch64-linux-gnu
包时,可以从开发者网站(如 ARM 官方或 Linaro)下载预编译的工具链。
下载 Linaro 64 位 ARM 工具链
1wget https://releases.linaro.org/components/toolchain/binaries/latest-7/aarch64-linux-gnu/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz
解压并安装工具链
1sudo tar -C /usr/local -xvf
2gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz
将工具链添加到系统环境变量
1echo "export
2PATH=$PATH:/usr/local/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin" >>
3~/.bashrc source ~/.bashrc
验证工具链安装
安装完成后,可以通过以下命令验证工具链是否安装成功。
1aarch64-linux-gnu-gcc --version
使用 ARM 工具链进行交叉编译
安装完成并验证工具链后,你可以使用这些工具链为 ARM 平台进行交叉编译。例如:
1GOARCH=arm64 GOOS=linux CC=aarch64-linux-gnu-gcc go build -o hello-arm64 main.go
Docker提供了一种方便的方法来进行多平台构建。可以使用 Docker 的 Buildx
插件来构建不同平台的 Docker 镜像。
安装 Docker 和 Buildx
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes docker
buildx create --use
创建 Dockerfile
创建一个简单的 Dockerfile:
1FROM golang:1.18 AS builder
2
3WORKDIR /app
4
5COPY . .
6
7RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /hello
1FROM alpine:latest
2
3COPY --from=builder /hello /hello
4
5CMD ["/hello"] 构建多平台 Docker 镜像
6
7docker buildx build --platform linux/amd64,linux/arm64 -t hello:latest --push .
JDK 17 是继 JDK 8 后的首个 LTS 版本,支持模块化、Records等新特性,移除了部分旧
API(如 javax.servlet)。
生态兼容:微服务、云原生场景下,JDK 17 的容器化支持更优。
Java EE 移交 Eclipse 基金会后更名为 Jakarta EE,包名从 javax.* 改为 jakarta.*。
微服务的概念,最早是 Martin Fowler 和 James Lewis 在 2014 年共同提出的。
Martin Fowler 在《Microservices》一文中写到:
In short, the microservice architectural style is an approach todeveloping a
single application as a suite of small services, each running inits own process
and communicating with lightweight mechanisms, often an HTTPresource API. These
services are built around business capabilities andindependently deployable by
fully automated deployment machinery. There is abare minimum of centralized
management of these services, which may be writtenin different programming
languages and use different data storage technologies.
-- Martin Fowler
将单体应用拆分为一组微小的服务,每个微小的服务单独运行,服务间可通过如RESTful
API
这样轻量级的接口交互,这些服务以业务能力为核心,用自动化部署机制独立部署。这些服务可以用不同语言开发,用不同技术来存储数据。
微服务架构的特性:
系统变得越来越复杂,传统单体系统已经无法再支撑。
传统的单体系统往往需要企业耗费几个月乃至几年,才能达到上线标准。这给小公司的前进带来了瓶颈,没人敢轻易研发、重构一个新的产品。
1.模块服务化
单体系统,多人协作开发时,往往会存在因代码、设计思路等差异而相互影响,相互等待,系统的庞大也给后期维护带来诸多不便。
微服务让系统更加轻量化,便于多人协同开发而互不依赖。
2.独立部署,灵活扩展
单体架构以整个系统为单位部署,而微服务以每一个独立服务为单位进行部署。
各个服务都是独立部署,可以根据各自服务的特点进行适当调整,即:根据服务的吞吐量、压力等不同的指标,分别给出不同的部署方案(部署策略),使得资源更加充分合理的使用。
3.资源的有效隔离
每一个微服务拥有自己独立的数据源,假如微服务
A想要读写微服务B的数据库,只能调用微服务 B对外暴露的接口来完成。
有效避免了服务之间争用数据库和缓存资源所带来的问题。
如果采用 Docker 部署,则每一个微服务实例在
Docker容器上运行,更加完美的实现了服务器资源(内存、CPU 资源等)的有效隔离 。
4.多语言,多选择
微服务对开发语言的选择就没有统一的要求,完全可以根据企业技术人员情况,不同模块的特点来选择不同的开发语言,让开发变得更加多样化。
5.团队组织架构的灵活
传统的研发组织架构是水平架构,前端有前端的团队,后端有后端的团队,DBA有 DBA
的团队,测试有测试的团队。
微服务架构的设计思想对团队的划分有了一定的影响,使得团队组织架构的划分更倾向于垂直架构。
当然,上述这种垂直划分只是一个理想的架构,实际在企业中并不会把团队组织架构拆分得这么绝对。
6.组件/框架多样化、成熟化
伴随着微服务出现,不断膨胀,各类技术组件、框架应用而生,为我们的开发降低了成本,加快了项目的开发周期。这些组件/框架纷纷落地投产,变得更加的稳定成熟。Spring
Cloud 家族就是一类典型的代表,后续文章将在详细介绍在微服务中的技术选型。
正因为微服务上述这些特性,使得在微服务的影响下,各类项目顺势崛起,为各类中小型软件公司带来了希望。
Philip Guo 的创作原则
One of my main hobbies is writing articles on this website (and more recently,
recording podcasts and vlogs). One heuristic I use when deciding what to write
is to ask myself:
Will at least 100 people care about this topic three years from now?
The following don’t qualify, so I rarely write about them:
commentary on news events or fads reactions to someone else’s article personal
notes that only my friends would care about Finally, if I see many people
writing about a topic, then I avoid it unless I have a drastically different
perspective.
开源的低代码/零代码开发平台。
它的思路:(1)接入数据库,(2)配置界面,所见即所得,(3)配置插件(权限插件、SSO
登录插件、打印插件、文档插件等)。
heuristic
qualify
commentary
fads reactions
drastically
perspective
ironical
indispensable
lay down
reliance
aggravate
trade-off
obscure
irssi
procedure
hiccup
nonexistent
Nope
Easy-peasy
odd
demise
Irssi
glanced
obfuscated
somehow
Whew
hindsight
missed out
boot you out
snippet
hacky
frankly
robust
trick
sleeve
cloak
obfuscate
anonymous
IP 黑洞:目前无解,但仅对部分服务黑洞,如谷歌系(谷歌、推特、YouTube 等)
DNS 污染:为域名返回一个假的 IP。使用 hosts 文件强制指定域名对应
ip或者使用加密的 DNS(DoH、DNS 签名等)
HTTP 劫持:流量未加密的,天然的中间人直接篡改(如:重定向到 404
页面、劫持到反诈页面等)。可以使用 HTTPS 连接规避,但可能会遇到 SNI 阻断
SNI 阻断:在客户端与服务器建立加密连接前,客户端会发送 Client Hello
报文,而这个报文是明文,并且一般都会携带 server_name
,中间人可以知道你要访问哪个网站,对不在白名单(如:discord.com)的域名进行阻断。因为server_name
实际上是一个扩展,并不强制,你可以不发送它来规避 SNI 阻断
全文摘录自,最近沈向洋博士在 X-Talk 上的演讲,
你不可能做所有的事情
做start-up 和带一个小孩子。孩子生出来了以后,你就没有办法去 Get rid
of(摆脱),所以你只能 Get rid of startup.
在自己的专业一定要做得很深
如果你不在某一个方向做到足够深的话,大家就记不住你。
一定要把故事讲好
做 presentation,一张 slide上面不应该超过七行字,多了以后大家看不清楚也搞不懂
一定要有一个远大的目标
一定要有目标,一定要清楚自己最后要追求什么。
Difficult, not Impossible
「Control the controllable, observe the observable, leave the rest alone」.
把握可控的,留心可见的,余下顺其自然。
一个一个项目加起来
Jim
Gray:「我从来不担心这个问题,到底是在研究院,还是在产品部门。你要选择你哪一个项目,你一生到最后的话,实际上就是你做过哪几个projects.」
职业生涯到了一定地步以后,大家就看你做过哪几个projects。
一定要明白自己前进的方向
A good philosophy to live by at work is to “always be quitting”. No, don’t be
constantly thinking of leaving your job 😱. But act as if you might leave on
short notice 😎.
在工作中,一个好的生活哲学是“总是准备离职”。不,不是让你一直想着离开你的工作😱。但是要表现得好像你可能会在短时间内离开😎。
“making yourself replaceable”; “deprecating yourself”; “automating yourself out
of your job”. You might have heard these more-popular names. /
“让自己可以被替代”;“让自己过时”;“把自己从工作中自动化出去”。
The key lies in NOT being indispensable. If you are, you’ll be stuck at your
specific job for as long as that job is relevant with little chance to
disconnect (no vacations, no growth). And when (not if) the job becomes
unnecessary, so will your position. /
关键在于不要让自己变得不可或缺。否则的话,你会在你的特定工作中停滞不前,只要那个工作还有意义,你就很难脱身(没有假期,没有成长)。而一旦(这不是假设)工作变得不必要时,你的位置也会变得不必要。
Paradoxically, by being disposable, you free yourself. You make it easier for
yourself to grow into a higher-level role and you make it easier for yourself to
change the projects you work on.
矛盾的是,通过让自己变得可有可无,你解放了自己。你让自己更容易成长为一个更高级别的角色,你让自己更容易改变你工作的项目。
📕 Document your knowledge. Every time someone asks you a question, they are
highlighting a gap in the documentation. Take the chance to write the answer
down (in a document, bug, code comment—wherever) so that the next person doesn’t
need YOU. / 📕
记录你的知识。每次有人问你一个问题,他们都在突出文档中的一个缺口。抓住机会把答案写下来(在文档、bug、代码注释——无论哪里),这样下一个人就不需要你了。
🏁 Document your long-term plans. People should know what’s coming up in your
projects and/or team by looking at those plans, not by relying on you to tell
them “in real time”. Plan a few months ahead so, if you leave, your peers won’t
be lost from day one. / 🏁
记录你的长期计划。人们应该通过查看这些计划,而不是依赖你“实时”告诉他们,来了解你的项目和/或团队即将发生什么。提前几个月计划,这样,如果你离开,你的同事们从第一天开始就不会迷失。
🤝 Document your meetings. Keep (public, within the team) notes for all meetings
you attend, listing who was there, what was discussed, and any conclusions.
Reference those notes from design documents. Your replacement will need these to
catch up. / 🤝
记录你的会议。保留你参加的所有会议的(公开的,团队内的)笔记,列出谁在那里,讨论了什么,以及任何结论。从设计文档中引用这些笔记。你的替代者需要这些来赶上。
🚶♂️ Bring others to meetings. If not a 1-on-1 and you are the only person from
your team attending a meeting, involve someone else. Different perspectives are
useful, but more importantly, you are avoiding becoming the only point of
contact. / 🚶♂️
把其他人带到会议上。如果不是一对一的,而你是你的团队中唯一参加会议的人,那么就让别人参与进来。不同的观点是有用的,但更重要的是,你避免了成为唯一的联系点。
👩🔧 Train people around you. The goal is for them to be independent (what is
usually considered “seniority” in a typical engineering ladder). Familiarize
them with the plans and technologies and make sure they know how to use the
documentation. / 👩🔧
培训你周围的人。目标是让他们独立(这通常被认为是典型的工程阶梯中的“资深”)。让他们熟悉计划和技术,并确保他们知道如何使用文档。
👩🎓 Identify and train your replacement. In the same vein as training others, to
switch roles you’ll need to replace yourself. Identify who that replacement
might be and actively and continuously coach them. / 👩🎓
识别并培训你的替代者。与培训他人一样,要换角色,你需要替换自己。确定可能的替代者是谁,并积极持续地指导他们。
🔑 Give power to the people. Trust them to do the right thing. If you are in a
leadership position, don’t make it so people come to you asking for permission.
Let them make their own choices. Guide them so that their choices are based on
the right data. / 🔑
给人民权力。相信他们会做正确的事情。如果你处于领导地位,不要让人们来找你寻求许可。让他们做出自己的选择。指导他们,使他们的选择基于正确的数据。
📧 Do not make yourself the point of contact. Establish mailing lists or other
forms of communication that can accommodate other people, and then grow those
groups. (The exception is when management needs names for accountability.) / 📧
不要让自己成为联系点。建立可以容纳其他人的邮件列表或其他形式的通信,然后发展这些群体。(例外情况是当管理层需要负责人的名字时。)
👨💼 Delegate. Once you have given power to others, included them in groups and
meetings, and documented your knowledge, they’ll be ready to take work from you.
Delegate work that can make them grow and focus on the things only you can do. /
👨💼
委派。一旦你给了他人权力,让他们参加了小组和会议,并记录了你的知识,他们就准备好从你那里接手工作了。委派可以让他们成长的工作,并专注于只有你能做的事情。
🏫 Always be learning. Take the chance to grow your knowledge in any area you
are interested in, and keep it fun. Bonus points if that area aligns with the
future path you want to take. / 🏫
始终在学习。抓住机会在你感兴趣的任何领域增长你的知识,并保持乐趣。如果那个领域与你想走的未来道路一致,那就更好了。
Note that nothing here implies abdicating responsibility. You still have to be
responsible for all the projects and teams you own, and you have to be for as
long as you are in your role. This is important because this responsibility is
what will open up new gates. /
注意,这里没有任何东西暗示放弃责任。你仍然必须对你拥有的所有项目和团队负责,只要你还在你的角色中,你就必须负责。这一点很重要,因为这个责任是打开新大门的关键。
Orange Pi AI Pro 开发板是香橙派联合华为精心打造的高性能 AI
开发板,其搭载了昇腾AI 处理器,可提供 8TOPS INT8 的计算能力,内存有 8GB 和
16GB两种版本。可以实现图像、视频等多种数据分析与推理计算,可用于教育、机器人、无人机等场景。
易用性:
文档详细,容易上手。安装方便,配置简单。接口很丰富,有千兆网口、USB、Type-C、WiFi、蓝牙等。
散热能力:
在长时间运行下,OrangePi的温度会略有上升。建议使用更大的散热片或风扇进行辅助散热,以确保系统稳定运行。
噪音:
OrangePi本身没有活动部件,在正常运行时是完全静音的。如果使用外部风扇,噪音水平取决于风扇的质量和速度。
NFS(Network File
System)是分布式文件系统协议,允许用户通过网络在不同主机间共享文件和目录。它适用于局域网环境,常用于服务器集群、数据共享等场景。
NFS默认不加密传输数据,公网环境建议使用VPN或SSH隧道保护通信。
安装NFS服务 根据 Linux 发行版选择命令:
Ubuntu/Debian
1sudo apt update && sudo apt install nfs-kernel-server
创建共享目录
1sudo mkdir -p /mnt/nfs_share
2sudo chown nobody:nogroup /mnt/nfs_share # 设置权限(根据需要调整)
配置NFS导出规则 编辑配置文件/etc/exports,定义共享目录及访问权限:
# 语法:<共享目录> <客户端IP/网段>(权限选项)
/mnt/nfs_share 192.168.1.0/24(rw,sync,no_subtree_check)
常用权限选项:
rw:读写权限。 ro:只读权限。 sync:同步写入磁盘(数据安全性高)。
async:异步写入(性能更好,但可能丢失数据)。
no_root_squash:允许客户端root用户保留权限(谨慎使用)。
示例:
/mnt/nfs_share *(rw,sync,no_root_squash)
生效配置并启动服务
1sudo exportfs -a
重新加载 exports 配置
1sudo systemctl start nfs-server # 启动服务 sudo
2
3systemctl enable nfs-server # 设置开机自启 5. 防火墙配置 开放 NFS
相关端口(NFSv4 默认使用 TCP 2049):
1sudo ufw allow 2049/tcp # Ubuntu
安装NFS客户端工具
Ubuntu/Debian
1sudo apt install nfs-common
创建本地挂载点
1sudo mkdir -p /mnt/nfs_client
手动挂载NFS共享
1sudo mount -t nfs <服务端IP>:/mnt/nfs_share /mnt/nfs_client
示例:
1sudo mount -t nfs 192.168.1.100:/mnt/nfs_share /mnt/nfs_client
自动挂载(重启生效) 编辑 /etc/fstab 文件,添加以下行:
<服务端IP>:/mnt/nfs_share /mnt/nfs_client nfs defaults 0 0
示例:
192.168.1.100:/mnt/nfs_share /mnt/nfs_client nfs defaults 0 0
1df -h | grep nfs
查看挂载状态
1touch /mnt/nfs_client/test.txt # 测试读写权限
指定NFS版本 挂载时强制使用 NFSv4:
1sudo mount -t nfs -o vers=4 192.168.1.100:/mnt/nfs_share /mnt/nfs_client
用户身份映射 在服务端 /etc/exports 中配置 anonuid 和 anongid:
/mnt/nfs_share 192.168.1.0/24(rw,sync,all_squash,anonuid=1000,anongid=1000)
查看NFS共享状态
服务端:
showmount -e localhost # 查看已导出的共享目录
客户端:
showmount -e <服务端IP>
日志排查
服务端日志:/var/log/syslog(Ubuntu)。 客户端日志:dmesg | grep nfs。
权限被拒绝(Permission Denied)
检查服务端 /etc/exports 的权限配置。 确保客户端用户对挂载点有访问权限。
连接超时或无法访问
确认防火墙已放行NFS端口(尤其是NFSv3需要额外开放 rpcbind 端口)。
使用 rpcinfo -p <服务端IP> 检查RPC服务状态。
挂载后文件属主显示为nobody
在服务端配置 no_all_squash 或指定 anonuid/anongid。
NFS 是
Linux环境中高效的文件共享解决方案。通过配置服务端导出规则、客户端挂载目录,并合理设置权限与防火墙规则,即可实现稳定可靠的网络存储。对于生产环境,建议结合Kerberos增强安全性,并定期监控NFS性能。
IP 黑洞:目前无解,但仅对部分服务黑洞,如谷歌系(谷歌、推特、YouTube 等)
DNS 污染:为域名返回一个假的 IP。使用 hosts 文件强制指定域名对应 ip
或者使用加密的 DNS(DoH、DNS 签名等)
HTTP 劫持:因为流量不是加密的,GFW
作为天然的中间人可以直接进行篡改(如:重定向到 404
页面、劫持到反诈页面等)。可以使用 HTTPS 连接规避,但你可能会遇到 SNI 阻断
SNI 阻断:在客户端与服务器建立加密连接前,客户端会发送 Client Hello
报文,而这个报文是明文,并且一般都会携带 server_name ,GFW
可以知道你要访问哪个网站,对不在白名单(如:discord.com)的域名进行阻断。因为
server_name 实际上是一个扩展,并不强制,你可以不发送它来规避 SNI 阻断
当然,上述这种垂直划分只是一个理想的架构,实际在企业中并不会把团队组织架构拆分得这么绝对。
6.组件/框架多样化、成熟化
伴随着微服务出现,不断膨胀,各类技术组件、框架应用而生,为我们的开发降低了成本,加快了项目的开发周期。这些组件/框架纷纷落地投产,变得更加的稳定成熟。Spring
Cloud 家族就是一类典型的代表,后续文章将在详细介绍在微服务中的技术选型。
正因为微服务上述这些特性,使得在微服务的影响下,各类项目顺势崛起,为各类中小型软件公司带来了希望。
杀手锏,tcpioneer#
影视飓风2024/10/9 补档:清晰度不如4年前!视频变糊是你的错觉吗?
磁力链接:magnet:?xt=urn:btih:ffb877b0e3e964cb307048542faa22571f515245
凯恩斯交叉
对于西方经济学,可分为:微观经济学、宏观经济学、计量经济学、数学分析。
在推荐材料上,老师给我们了推荐
曼昆的《经济学原理》上下册
国内的教材喜欢写得精辟,国外的教材喜欢写得通俗。
前者作为教科书,需要老师带着解释;后者像一本故事书,学生自己读也能津津有味,故事书的篇幅当然会更多了!
这里也体现了老师和大师的差距,大师能够站在更高的视角,却用更质朴的语言去解释一件事情,可以提供不同的角度。
而普通学者尚未达到那样的境界,所以只能从自身专业的角度来谈问题,不考虑衔接的过程。
这可能就是常识水平差异,或者说知识的诅咒。
《乌合之众》。要保持思考和判断,不要轻易地相信某个人的话都是对的。
德国电影《浪潮》。要保持思考和判断,不要轻易地相信某个人的话都是对的。
文档中就应该提供为零基础用户服务的教程,这是用户基数最大的群体。毕竟高级用户不是凭空产生的。
Pytorch最初的教程是核心开发者 Soumith Chintala 自己写的,如今由 Stanford 的
Justin Johnson 进行了补充。
人的思维一旦变复杂,很难还原到初学者视角,我们容易把自己的常识误以为是别人也已经掌握的知识,从而在写教程的时候写出很抽象的内容。
获取教程受众的反馈很重要。国人比较谦虚,能力越强的人越是喜欢自己把问题研究清楚,等到视野开阔理解能力变强后,就算教程存在着瑕疵,也被过往经验自动完善掉,很难意识到是教程本身存在着不合理的地方。
怕没讲明白学生却不好意思说,宁可自己花额外的时间去消化,这种情况应该被视作潜在的教学事故。
不是所有人都是天生的教育工作者,Andrew 在 Linkedin
上有一篇很热门的文章,讲的是如何通过刻意练习,让自己成为更受学生欢迎的老师。
一定要使用自己熟悉的教学材料,而不是用大家都觉得好的材料去给学生做临时翻译,一是没有原汁原味的体验,二是容易让其他人对教学材料和原始作者形成抵触和误解。
在学习新知识的初期,建立基本的直觉和浓厚的兴趣是重中之重,至于如何启发思考,把握内容的节奏感和分寸感,则是更进阶的话题。
从学习阶段进入使用阶段后后,教程的使命就结束了,用户指南和 API
参考成为了文档中经常被光顾的地方。
指南的写法,和教程还真有挺大的区别。
用户通常只会用到软件中 15
%的功能,而不同类型的用户使用的往往是同一个软件中那不同的 15% 部分。
如果用户需要的那
15%无法在某一个软件中(或者以插件的形式)提供,自然地就会去寻找满足需求的同类替代品
一个用户在刚接触到新产品时,学习的是最简单的功能,界面的友好程度、功能上手的难易程度等会直接决定用户的留存。
但是由于对不同的用户这15%是不同的部分,因此需要分别提供对应的入口。
用户的属性不是一成不变的,作为软件供应商,还要思考如何帮助用户发展自我,从初级萌新转变为高级资深玩家。
这个时候,提供不同入口的教程尤其重要。
写文档本质上是在为解放自己的生产力进行铺垫 ——
聪明的程序员都懂得如何偷懒,巴不得能用脚本尽可能地去自动化一切工作。
我们要教会使用者如何去用我们开发出来的东西,因此教程必不可少。
我们要实现一个东西,因此需要找人讨论实现方案,提供 Proposal / Prototype
实现。其中存在着信息的收集、同步、讨论、达成一致决策的过程,如果不采取任何形式的总结,将来很有可能需要向其他的想要了解技术细节的人解释“当初为什么要这样做(不那样做)”,;而人的记忆不是永远可靠的,我们无法保证将来总是能回忆起曾经的各种决定。一旦产生元老级别的人员流动,很多未来得及记录的信息就像青春岁月一样飘散在风里了。
我们要读懂别人的实现,碰到了困难,然后跑过去问实现者。对方可能不一定有空,有问必答最终会占据掉原本用来安排进行其它工作的时间;当然你也可以询问其他了解这块的人,但口口相传的方式更加无法保障信息的准确性,我们需要制定流程规范,目的是为了提升整体的工作效率,口头约定并不具有约束效果;
随着工作经验的丰富,我们开始指导实习生的日常工作。一些我们觉得属于常识的东西,可能对于新人来说就是全新的事物,尤其是限定于项目中的一些细节,毕竟一些坑可能永远不会在其它项目中遇见。
文档的另外一个作用是服务于项目本身的开发者,可我们是那么地讨厌写文档,又那么地讨厌别人的项目没好好写文档。这时候不得不对比一下“直接交流”这种形式,很多人都会以口头交流更高效为由拒绝写文档。诚然,相较于打字聊天这种形式,面对面的交流可以和帮助我们保持专注和高效。但如果是频繁地解释已经实现的东西,其长期效益未必高过写文档。毕竟不是所有人都是语言大师,交谈过程中废话的比例有时候会比预想的多得多,想要强行地让别人兼容自己的思维模式,亦或者是做到对他人的兼容,都是极端困难的,需要大量的练习。如果不在正式的交流开始之前拉齐双方的认知,可能聊着聊着发现原来不在同一个频道。
很多时候在一场对话中,我们可能会觉得
“怎么讲了这么久,对方还是不明白?是不是理解能力有问题啊?”
,而对方可能早就在内心吐槽
“这个人讲的是什么东西啊?根本没把东西讲清楚,是不是表达能力有问题啊?他是不是根本不知道我问的是什么?”,总之锅肯定是对方的就没问题啦!与人相处是值得一生去探究的话题,而对话时进行的一些假设其实是我们与生俱来的弱点,对他人抱有严格的期待,而容易忽视掉自己身上不足的部分。不欢而散的情况也是有可能产生的,这其实还算好,更糟糕的情况是为了避免尴尬而不懂装懂,误认为这次交流是有效的,结果又要用更多的交流来填坑,我们真的做到科学高效了吗?
我倾向于去寻找并阅读他/她对外输出的内容,比如邮件、演讲、讲座、论文、博客、歌单、视频等等,尝试去构筑对方的认知体系,习惯他/她的表达方式,看能否找到融合点。
我们在讨论技术问题的时候可以交流双方对同一篇文档的理解是否一致,不一致的话则求同存异,同时文档的内容也能够更新。
每次提到产品设计时,我都会和人安利Robin Williams 的 《The Non-Designer’s Design
Book》,时刻注意并尝试练习满足“亲密性”、“对齐“、“重复“和“对比”四大原则。
在文档中体现的比较明显的地方是:我们会经常用到一些视觉样式元素,比如
note/warning 来避免过多的重复,形成局部的对比,否则用户读起来会很累。
我还想要强调一下已知常见文档形式的局限性,以及现代化文档的发展趋势。
由于历史原因,很多文档要求以纯文本的形式进行提供,这样对文本编辑器友好。但在一些概念解释的情景下,往往是一图胜千言的,用更加新颖的媒体手段进行创作和表达。
一种内容表达载体的形式是视频,比较常见的做法是在一篇教程中,标题下方是一个嵌入的视频,紧接着是对应的文字脚本或者内容总结。一些不专业的细节处理往往会往视频中引入过多的噪声,降低信噪比,加大用户的认知成本。
糟糕的音质比糟糕的画面更容易让人失去耐心。音频后期技巧包括去除口水音、去除齿音等技巧。
如果是实拍视频,还需要考虑到摄影方面的一些细节。比如一些代码演示的视频中,如果出现了敲错代码,或者是讲着讲着发现思路不清晰甚至讲错的情况,请务必重新录制,不要让观众因为你的失误而付出代价。
视频画面的内容,3Blue1Brown 的风格是我目前最推荐的用来解释抽象概念的途径。
另外,将来的人们在发布内容时,一定会做更多在交互性上面的尝试,正如
https://distill.pub/ 现在所做的。
我们可以用一张表来表示体系完整的文档应该覆盖到哪些情景:
学习阶段 使用阶段 实践步骤 教程 🤔 指南 📖 理论知识 解释 📝 参考 📚
教程(Tutorial):指导读者通过一系列步骤完成项目(或有意义的练习)的课程;
指南(Guide):引导读者完成解决常见问题所需步骤的指南(How-to 系列);
参考(Reference):用于 API 查询和浏览,方便网络引擎检索的百科全书;
解释(Explaination):对于特定主题、特性的概念性说明。
收集一些临时文字。
pen 钢笔
pencil 铅笔
ballpen 圆珠笔
eraser 橡皮,黑板擦
ruler 格尺
penci-box 铅笔盒
sharpener 卷笔刀
draft “草稿”,指方案、计划、报告、合同等文字草稿。
例句:
My secretary has typed out the first draft of the report, but I must revise it
before I submit it to the conference.
我的秘书已经打印好报告的第一稿,但我还必须在提交给会议之前把它修改一下。
The chairman went over the draft of the report and make some corrections on it.
主席审阅了报告草案并作了几次修改。
A draft of the new rule is set to be approved by the Federal Reserve Board and
put out for public comment within weeks.
一份新规草案将在几周内获得美联储董事会通过,并公开征求意见。
We will give you a definite reply after we have studied the details of the draft
contract.
我们研究合同草本的细节后会给你们一个肯定答复。
script 指剧本、电影脚本、广播稿,讲话稿。
例句:
Two writers collaborated on the script for the movie.
两位作家合作编写了这部电影的剧本。
During certain scenes of the play there isn't any script and the actors just
improvise.
在戏剧的某些场景中,没有剧本,演员们只是即兴发挥。
It's a great idea for a show but the script lacks a little sparkle.
这是一个很棒的想法,但讲稿缺乏一点亮点。
The producer disliked the script and demanded a rewrite.
制片人不喜欢这个剧本,要求重写。
Have you got the script for the listening in class?
你拿到课堂听力的原稿了吗?
manuscript 手稿、原稿;手写本,手抄本。
例句:
He sent the 400-page manuscript to his publisher.
他将400页的手稿寄给了他的出版商。
I hunted down the manuscript in the British Museum.
我在大英博物馆找到了这份手稿。
It is thought that the maunscript is the work of a monk and dates from the
twelfth century.
那部手抄本被认为出自12世纪一位僧侣之手。
sketch “草图”、指绘略图、素描或写生。
例句:
The report contains several sketches of the new device and a verbal description.
这份报告包括几份新装置草图和一段文字描述。
The artists usually make several sketches before the final painting.
艺术家在最终作画前经常会先画几张草图。
He drew a rough sketch for us to show how we can get the destination.
他为我们画了一幅草图,标明我们如何才能到达目的地。
He did a sketch of the ballet dancers.
他为这些芭蕾舞者画了一张素描。
She drew a sketch map of the area to show us the way.
她画了一幅该地区的草图给我们指路。
如果只是记录自己的生活,可能不会选择公开自己的动态,而是使用私有化部署的NAS
应用。
分享自己的想法吧,太保守显得平庸,太激进显得危险,也有可能成为日后被党同伐异、口诛笔伐的材料,实在是如履薄冰。
如果你的创作内容十有八九是可以完全由人工智能生成的,那就值得考量考量该不该在这部分内容上投入时间和精力了。
在不遥远的将来,基于人工智能的问答将成为我们日常生活的一部分
—— 人人都能成为拥有贾维斯的托尼·斯塔克(钢铁侠)!
LFS 的原理是:在 Git仓库中只存储指针文件,真正的文件存储在 LFS
服务器上,按需获取。
version https://git-lfs.github.com/spec/v1
oid sha256:4cac19622fc3ada9c0fdeadb33f88f367b541f38b89102a3f1261ac81fd5bcb5
size 84977953
“博客”(又称“部落格”) 一词来自于英文“blog” ,最初 Jorn Barger
在1997年12月17日提出了 “weblog” 的概念,即“网页”(web) 与 “记录”(log) 的组合;
而后由 Peter Merholz 开玩笑地拆分为“我们”(we) 与 blog 的组合, 后续术语 blogger
的出现,使这一概念慢慢被普及和接受。
网站有APP无可比拟的便捷性,访客可随时随地浏览所需信息,只需一个浏览器,无需在手机里安装各大臃肿不堪的APP,
网站具有自由平等的特性,任何人都可以建立一个自己的网站
网站的内容可通过搜索引擎搜索到,节省了获取信息的时间成本,现在各大公司却“有意”封闭自己的平台,限制搜索引擎,形成孤岛,获取信息的难度大大增加。
什么是骆驼玩法?
对于打印到纸张上的文字而言,正文字体大小多少合适?