开发者博客 – IT技术 尽在开发者博客

开发者博客 – 科技是第一生产力


  • 首页

  • 归档

  • 搜索

敏感信息前端加密的意义和实现方案

发表于 2024-04-27

引言

在传统安全模型中,数据传输往往依赖于HTTPS(SSL/TLS)协议来保证数据的安全性,但在网络通信并不总是安全的。为了保护数据的安全性,前端加密尤为重要,因为前端是用户与应用程序之间的桥梁,承担着用户输入的传输和展示任务。

如果前端未对敏感信息进行加密传输,则可能会被恶意第三方截取、篡改或窃取;而且未加密的明文信息也会被记录在日志或日志数据库中,甚至在某些系统中用户密码都是明文存储,这种将用户信息直接暴露在外行为是十分危险的,给别有用心之人带来很多可乘之机。

本篇文章将介绍前端加密的意义、加密的原理、常用的加密方式以及如何在Java中实现这些加密方式。

原理

加密的核心原理是通过一定的数学算法对原始数据进行变换,生成不易被破解的密文。常见的加密算法包括对称加密和非对称加密:

  • 对称加密: 对称加密使用相同的密钥对数据进行加解密,加解密的过程是可逆的。常见的对称加密算法有DES、AES等。对称加密的优点是加解密速度快,但需要保证密钥的安全传输,否则容易被破解
  • 非对称加密: 非对称加密使用一对密钥使用公钥加密,使用私钥解密。加密数据只能通过与公钥相对应的私钥解密,公钥或非对应私钥无法解密。常见的非对称加密算法有RSA等。非对称加密的优点是安全性高,但加解密速度相对较慢

加密算法的安全性取决于密钥的长度和算法的复杂性,越复杂的算法和越长的密钥越难以被破解,加密的过程通常包括以下几个步骤:

  • 密钥生成:对称加密需要生成密钥,非对称加密需要生成一对公钥和私钥
  • 加密:使用密钥对原始数据进行加密,生成密文
  • 解密:使用密钥对密文进行解密,恢复原始数据

常用方式

在前端开发中,常用的加密方式有以下几种:

  • AES对称加密:Java中javax.crypto包提供的Cipher类实现AES(高级加密标准:Advanced Encryption Standard)对称加密,在前端加密中被广泛使用
  • RSA非对称加密:Java中java.security包提供的KeyPairGenerator类和javax.crypto包提供的Cipher类实现RSA非对称加密,适用于加密小量数据和数字签名
  • 哈希算法:将任意长度数据应设为固定长度数据,但加密后数据不可逆,一般用于验证数据的完整性和可靠性,常用有MD5和SHA-256

实现

接下来,给出Java中使用AES和RSA加密算法对系统必备登录功能密码明文进行加解密处理示例

AES加密

后端在前端每次请求都生成一份一次性密钥(key)及初始向量(iv),密钥长度可以是16字节、24字节或32字节,初始向量长度必须是16字节

将key和iv进行存储,并使用安全技术(如Https)传给前端,前端根据本次获取到的key及iv对密码进行加密处理,并将用户名和加密后密码传给后端接口

1
2
3
4
5
6
7
php复制代码function encryptWithAes(key, iv, plainText){
return CryptoJS.AES.encrypt(plainText, CryptoJS.enc.Utf8.parse(key), {
iv: CryptoJS.enc.Utf8.parse(iv),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}).toString();
}

后端接口获取到前端登录账号和密码信息,使用Java中javax.crypto包提供的Cipher类,根据已存储的key和iv对密码进行解密操作,并将明文返回,再根据后端密码规则对账号和密码进行验证

1
2
3
4
5
6
7
8
9
ini复制代码public static String decrypt(String encryptedData) throws Exception {
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
// 解密数据
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
return new String(decryptedBytes, StandardCharsets.UTF_8);
}

RSA加密

首先,在后端Java中使用java.security包提供的KeyPairGenerator类生成公钥和私钥,将私钥进行存储,并将公钥返回给前端

1
2
3
4
5
ini复制代码KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(KEY_SIZE);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();

前端拿到公钥后,使用JSEncrypt和公钥对密码进行加密,并将加密后的密码传到后端进行密码校验

1
2
3
4
5
scss复制代码function encryptWithPublicKey(publicKey, password) {
var encryptor = new JSEncrypt();
encryptor.setPublicKey(publicKey);
return encryptor.encrypt(password);
}

后端登录接口获取前端账号和密码信息,使用Java中javax.crypto包提供的Cipher类,根据已存储的私钥对加密后密码进行解密操作,并返回明文数据,再根据后端密码规则对账号和密码进行验证

1
2
3
4
5
6
7
8
9
10
11
12
ini复制代码public static String decryptWithPrivate(String encryptText, String privateKey) throws Exception {
if (StringUtils.isBlank(encryptText)) {
return null;
}
Provider provider = new BouncyCastleProvider();
Security.addProvider(provider);
Cipher ci = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
ci.init(Cipher.DECRYPT_MODE, keyFactory.generatePrivate(keySpec));
return new String(ci.doFinal(Base64.getDecoder().decode(encryptText.getBytes())));
}

下图展示出登录及修改密码操作数据库日志,圈出来部分未进行加密处理,可以很清楚的看到登录密码明文信息,而加密后信息就很难看出密码内容
数据库日志

对比

使用AES加密任然需要将key和iv通过接口传给前端,若在传输过程中窃取到key和iv同样可以直接将加密后数据解密出来;而RSA只需要将公钥传给前端,即使公钥被窃取也无法对加密后数据进行解密处理,相比AES安全性更高

总结

加密是一种保护数据安全的手段,将原始数据转换为密文,增加数据在传输和存储过程中被窃取、篡改或破解的难度,保护用户数据的安全性,提升系统的安全性和可信度。在互联网应用中,加密可以提供以下几方面的保护:

  • 保护数据隐私:用户个人信息、账号密码等敏感数据在网络上传输过程中,可能被黑客截获而导致隐私泄露。加密可以有效保护这些数据,即使被截获也无法被解读
  • 防止数据篡改:数据在传输过程中,可能会被第三方篡改,从而导致数据的真实性受到破坏。可以通过数字签名等加密手段验证数据的完整性,防止数据被篡改
  • 抵御中间人攻击:中间人攻击是一种常见的网络攻击方式,黑客通过伪装成通信双方之一来窃取数据。加密可以防止中间人窃取敏感信息,保障通信安全
  • 减少日志风险:使用密文数据对敏感信息进行操作,即使后端日志泄露也不会直接暴露用户敏感信息,能一定程度保障用户隐私

点击查看更多文章

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Phi-2:小型语言模型令人惊人的能力 导语 概览 Phi-

发表于 2024-04-27

导语

phi-系列模型是微软研究团队推出的轻量级人工智能模型,旨在实现“小而精”的目标,能够实现在低功耗设备上例如智能手机和平板电脑上部署运行。截止目前,已经发布到了phi-3模型,本系列博客将沿着最初的phi-1到phi-1.5,再到phi-2和phi-3模型展开介绍,本文介绍phi-2模型。

  • 标题:Phi-2: The surprising power of small language models
  • 链接:www.microsoft.com/en-us/resea…

image.png

概览

过去的几个月中,微软研究院的机器学习基础团队发布了一套名为“Phi”的小语言模型(Small Language Models,SLMs),在各种基准测试中取得了显著的性能。包括1.3B参数的Phi-1,在现有的SLMs中实现了Python编码的最先进性能(具体来说是在HumanEval和MBPP基准测试中)。和扩展到常识推理和语言理解的1.3B参数模型Phi-1.5,其性能可与大5倍的模型相媲美。

本文介绍Phi-2,一个2.7B参数的语言模型,展示了出色的推理和语言理解能力,在少于13B参数的基础语言模型中表现出最先进的性能。在复杂的基准测试中,Phi-2匹配或超过了大25倍的模型,这要归功于模型扩展和训练数据策划方面的新创新。Phi-2以其紧凑的尺寸成为研究人员的理想试验模型,包括对机械性可解释性、安全性改进或对各种任务进行微调实验。在Azure AI Studio模型目录中提供了Phi-2,以促进语言模型的研究和开发。

image.png

Phi-2的关键亮点

语言模型规模的大幅增加至数千亿参数已经解锁了一系列新兴能力,重新定义了自然语言处理的格局。一个问题仍然存在,即是否可以通过战略选择训练数据,例如数据选择,在较小的规模上实现这种新兴能力。

Phi系列模型的工作旨在通过训练实现与规模更大的模型相当的SLMs来回答这个问题(尽管仍远未达到前沿模型)。本文通过Phi-2打破传统语言模型扩展规律的关键见解有两个:

首先,训练数据质量对模型性能至关重要。几十年来这一点已经为人所知,但本文将这一见解发挥到了极致,着重于“教科书质量”的数据,延续了之前的工作“Textbook is all you need”。本文的训练数据混合包含了专门创建的合成数据集,用于教导模型常识推理和一般知识,包括科学、日常活动和心理理论等内容。进一步通过精心筛选的网络数据增强训练语料库,这些数据根据教育价值和内容质量进行了过滤。其次,使用创新技术进行扩展,从1.3B参数模型Phi-1.5开始,并将其知识嵌入到2.7B参数的Phi-2中。这种规模化的知识传递不仅加速了训练收敛,而且在Phi-2的基准分数中显示出明显提升。

image.png

训练细节

Phi-2是基于Transformer的模型,使用下一个单词预测目标,通过对多次经过的合成和网络数据集进行了NLP和编码的混合训练,共计1.4T词元(token)。训练耗时14天,使用96个A100 GPU。Phi-2是一个基础模型,尚未通过人类反馈的强化学习对齐(RLHF),也没有进行指令微调。尽管如此,与经过对齐的现有开源模型相比,本文观察到在毒性和偏见方面表现更好(参见图3)。这与在Phi-1.5中观察到的情况一致,这归功于作者量身定制的数据策划技术,请查阅之前的技术报告以了解更多详情。

image.png

Phi-2 评估

下面总结了Phi-2在学术基准测试中与流行语言模型的性能对比情况。基准测试涵盖了几个类别,包括大型基准测试(BBH)(3-shot与CoT)、常识推理(PIQA、WinoGrande、ARC、SIQA)、语言理解(HellaSwag、OpenBookQA、MMLU(5-shot)、SQuADv2(2-shot)、BoolQ)、数学(GSM8k(8-shot))和编码(HumanEval、MBPP(3-shot))。

仅有2.7B参数的Phi-2在各种综合基准测试中超越了Mistral和Llama-2模型(其参数分别为7B和13B)。值得注意的是,与体积大25倍的Llama-2-70B模型相比,Phi-2在多步推理任务上(即编码和数学)表现更佳。此外,尽管体积较小,Phi-2在性能上与最近宣布的Google Gemini Nano 2相匹配或表现更好。

当然,模型评估面临一些挑战,许多公开基准测试可能泄漏到训练数据中。对于Phi-1,作者进行了广泛的净化研究以排除这种可能性,可以在第一篇报告中找到。作者相信,评判语言模型的最佳方式是在具体用例上进行测试。遵循这一精神,本文还使用了几个微软内部专有数据集和任务对Phi-2进行了评估,再次将其与Mistral和Llama-2进行了比较。可以观察到类似的趋势,即在平均水平上,Phi-2优于Mistral-7B,而后者优于Llama-2模型(7B、13B和70B)。

image.png

image.png

除了这些基准测试之外,本文还对研究界常用的提示进行了广泛测试。观察到的行为符合在基准测试结果的预期。例如,本文测试了用于探究模型解决物理问题能力的提示,最近用于评估Gemini Ultra模型的能力,并获得了以下结果:

image.png

image.png

扩大规模的最佳实践(Best Practices to Scale up)

在扩大规模时,作者首先给出了phi-1模型上的一些实验结果:

image.png

可以看到,训练的次数越长,模型性能越好,但这样就越花费时间。为此作者尝试在大模型中重用已经训练好的小模型权重,但因此也会面临一个挑战:如何把小模型的权重扩展到大模型的维度上?

作者尝试了一个之前研究提出的方式,即层数上使用下图的公式将层数进行映射:

image.png

在维度上,则保留那些已有的权重作为新的大权重参数矩阵的一部分,剩下的部分进行随机初始化即可,如下图所示:

image.png

经过这样的重用实验后,得到实验结果如下:

image.png

另一种有效的参数继承方式是平铺(Tiling),示意如下:

image.png

得到的最终性能表现如下:

image.png

image.png

总结

一个良好的、通用的SLM可以通过以下方式实现:

  • 与传统的网络数据相对比,生成和利用具有“教科书质量”的数据;
  • 吸收扩展规模的最佳实践,以增强整体性能。

参考

  1. The Surprising Power of Small Language Models, Mojan Javaheripi, Microsoft Research, nips.cc/media/neuri…

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Kotlin的成员扩展函数和implicit receive

发表于 2024-04-27

概念

implicit receiver: 隐式的接收器或者接收者,接受函数的调用和属性的访问。

在java中这个隐式的接收者其实就是this。而kotlin对它进行了一些关键性的拓展,在拓展的同时,为了方便描述和沟通,给它起了专属的名字:implicit receiver。

比如在Java中,可以隐式的调用内部类和外部类的成员变量,kotlin同理。

kotlin特殊的点在于,他可以直接通过函数来嵌套新的this。

例如kotlin协程的CoroutineScope就利用了这种概念,可以理解成提供了一个CoroutineScope的环境。

image.png

成员扩展函数

现在定义这样一个类IntMultiplier,它的内部有个成员变量,同时这个成员变量又是Int的扩展函数

1
2
3
kotlin复制代码class IntMultiplier(val time: Int = 2) {
fun Int.multiply() = this * time
}

我们是无法直接调用它的。所以我们要想办法创造一个IntMultiplier的环境,才能去使用这个扩展函数。

这里写了一个runAsOuter的函数,传进去一个函数类型的参数block,又设置block的receiver类型是IntMultiplier,这样就强行插入了一个IntMultiplier的this。

1
2
3
4
5
6
7
kotlin复制代码class IntMultiplier(val time: Int = 2) {
fun Int.multiply() = this * time

fun runAsOuter(block: IntMultiplier.() -> Unit) {
block()
}
}

这样就可以通过这个函数来进行调用了。

1
2
3
4
5
6
7
kotlin复制代码fun main() {
val multiplier = IntMultiplier()

multiplier.runAsOuter {
3.multiply()
}
}

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

介绍riverpod中的refinvalidate、ref

发表于 2024-04-27

解释riverpod中的ref.invalidate

在 Riverpod 中,ref.invalidate 是一个非常有用的方法,它允许你手动标记一个 provider 为无效(或 “dirty”),从而触发它的刷新或重新计算。这个特性在某些场景下特别有用,比如当你知道由于外部因素(例如,数据库更新、文件变化、网络请求等)导致 provider 的数据已经过时,需要重新获取或计算数据时。

使用 ref.invalidate

ref.invalidate 方法接受一个 provider 作为参数。当调用此方法时,Riverpod 会将指定的 provider 标记为无效,并在下次尝试读取该 provider 时重新执行它的创建函数或异步请求,从而获取最新的数据。

这里是一个简单的使用示例:

1
2
3
4
5
6
Dart复制代码final myProvider = Provider((ref) => 'Initial data');

void someFunction(WidgetRef ref) {
// 在某个事件或条件触发时,使 myProvider 无效
ref.invalidate(myProvider);
}

在上面的示例中,当 someFunction 被调用时,myProvider 被标记为无效。这意味着下次当你或你的应用尝试读取 myProvider 时,它的值将会被重新计算。

应用场景

ref.invalidate 方法在需要主动刷新或更新数据时非常有用。以下是一些常见的使用场景:

  • 外部数据变化:当你知道提供给 provider 的数据源(如数据库、文件系统或远程服务器)已经改变,而 provider 需要更新其数据以反映这些变化时。
  • 用户操作:用户执行了某个操作(如点击按钮)需要刷新数据或视图。
  • 定时更新:在特定时间间隔后需要更新数据,虽然这种情况可以使用其他方法实现,但在某些特定逻辑下 ref.invalidate 可以作为一种选择。

注意事项

  • 使用 ref.invalidate 时,需要谨慎考虑其对性能的影响,因为频繁地使 providers 无效并重新获取数据可能会导致性能问题,特别是当数据获取包含昂贵的操作(如网络请求)时。
  • 在使用 ref.invalidate 时,考虑其对应用状态管理的影响。确保应用的逻辑和用户体验不会因为数据的频繁更新而受到负面影响。

总之,ref.invalidate 是 Riverpod 提供的一个强大的工具,允许开发者在适当的时机手动触发 provider 的更新,以确保应用中的数据保持最新。

如果我们调用invalidate(provider),但是并没有执行read(provider),会发生什么呢

当你调用 ref.invalidate(provider) 标记一个 provider 为无效时,但之后并没有执行 ref.read(provider) 或其他会触发 provider 读取的操作(比如监听 provider 的状态),那么以下情况会发生:

  1. 延迟执行:provider 实际上并不会立即重新执行或刷新其数据。invalidate 方法仅仅标记了 provider 需要在下一次读取时刷新,所以如果没有后续的读取操作,provider 的创建方法或加载逻辑不会被立即触发。
  2. 等待触发:被标记为无效的 provider 会保持在这个状态,直到有操作尝试读取它。这意味着,只有当你的应用(或测试代码)再次请求这个 provider 的数据时,Riverpod 才会重新执行 provider 的构建函数或相关的异步操作来获取新的数据。
  3. 无立即影响:因为没有立即执行读取,所以应用的 UI 或状态也不会立即发生变化。任何依赖于该 provider 的 widgets 或逻辑都不会更新,直到 provider 被实际读取。
  4. 优化性能:这种延迟执行的行为有助于优化应用性能,避免不必要的数据加载和状态更新,特别是在数据变化频繁或更新成本较高的场景中。

示例

假设你有一个通过网络请求获取用户信息的 FutureProvider:

1
2
3
Dart复制代码final userProvider = FutureProvider<User>((ref) async {
return fetchUserData();
});

如果你执行了 ref.invalidate(userProvider) 但之后没有读取 userProvider,则 fetchUserData 方法不会被立即调用。只有在后续某个点通过 ref.watch(userProvider)、ref.read(userProvider) 或 widget 使用这个 provider 时,fetchUserData 才会被触发,从而加载最新的用户数据。

总的来说,ref.invalidate(provider) 是一个标记操作,真正的数据更新和逻辑执行需要等到下一次对该 provider 的有效读取发生时才会进行。这种设计有助于减少不必要的操作,让数据更新更加高效和可控。

请解释ref.refresh

ref.refresh 是 Riverpod 中的一个方法,用于立即重新执行一个 provider,并获取其最新值,而不管以前的状态如何。与 ref.invalidate 相比,ref.invalidate 只是标记一个 provider 在下次读取时需要刷新,而 ref.refresh 则会立即触发这个操作。这意味着 ref.refresh 会强制执行 provider 的创建函数或异步操作,无论当前 provider 是否被标记为无效,或它的数据是否被认为是最新的。

使用 ref.refresh

ref.refresh 通常用在需要立即更新数据的场景,例如用户触发了一个操作来刷新数据,或者你知道数据源已经变化,需要立即反映这个变化到应用中。通过调用 ref.refresh,你可以确保应用立即获取到最新的数据状态。

示例

假设你有一个用于获取用户信息的 FutureProvider:

1
2
3
Dart复制代码final userProvider = FutureProvider<User>((ref) async {
return fetchUserData();
});

如果你想在用户点击一个按钮时立即刷新用户信息,可以这样做:

1
2
3
4
5
6
Dart复制代码ElevatedButton(
onPressed: () {
ref.refresh(userProvider);
},
child: Text("Refresh User Data"),
);

当 ref.refresh(userProvider) 被调用时,Riverpod 会立即重新执行 fetchUserData() 函数,无论之前的异步操作是否已经完成或者数据是否被认为是最新的。这将导致与 userProvider 相关的 widgets 根据新获取的数据更新 UI。

注意事项

  • 使用 ref.refresh 会立即触发数据的重新加载,这可能会引起相关 UI 的快速重建。在某些情况下,如果不希望用户体验到界面的频繁重绘,需要谨慎使用。
  • 对于依赖其他 provider 数据的 provider,使用 ref.refresh 也会导致依赖的 provider 被重新计算。
  • 与 ref.invalidate 相比,ref.refresh 更为积极地确保数据的即时更新,但也可能带来更多的网络请求或计算,影响性能。合理安排刷新时机对于保持应用性能和用户体验是非常重要的。

总之,ref.refresh 是一个强大的工具,能够帮助你控制和管理应用的数据状态,确保用户总是看到最新的信息。正确使用这个方法可以显著提升应用的响应性和实用性。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

具备什么样的技能才能成为架构师

发表于 2024-04-27

在过去的二十年里,技术界和软件开发方法经历了剧烈的变化和发展。新技术的出现和开发实践的进步使得软件项目变得越来越复杂,但同时也更加强大和灵活。在这样一个快速演变的环境中,软件架构师的作用日益显著,成为项目成功的关键因素。他们不仅是技术决策的制定者,也是团队之间协作的桥梁,确保技术解决方案既能满足当前需求,又具备长远的视野。

特别是在当今强调敏捷性和团队自治的文化背景下,架构师的角色并没有因为敏捷实践的推广而变得边缘化。相反,他们在协助团队快速适应变化、保持技术路线清晰和前瞻性方面扮演着至关重要的角色。在前几节中,我详细探讨了架构师的重要性,以及他们在适应敏捷开发模式中的关键作用。

本节的重点转向架构师本人,探讨了成为一名成功架构师所需的关键特质和技能(如图1所示)。尽管成为一名出色的架构师需要广泛的知识和技能,但基于个人经验,我将重点介绍几项被广泛认为最为关键的技能。这些技能希望能帮助读者深入了解架构师的核心能力,以及如何培养这些能力以适应不断变化的技术领域。

图1

1、简化能力

大多数架构图和规范都因设计而变得复杂。建筑师有目的地通过认为复杂性来展示他们的能力来做到这一点。然而,事实恰恰相反——复杂的架构无助于软件开发。架构必须逐步构建,以便新接触问题空间的人能够轻松理解。

我使用一种称为级别的技术,从级别 0 开始逐步构建系统。在大多数情况下,L0 是框图,L1 是解决方案图,L2 是技术和供应商产品。这种方法有助于解释复杂的问题并将其分解为模块。读或听的人确实理解,因为架构构建层层递进。

在讨论成为一个成功的架构师需要哪些技能时,有一个特别值得关注的能力——简化复杂性。我们常见的问题是,许多架构设计图和规范文档因为试图展现深奥的技术细节而变得异常复杂和难以理解。有时,这种复杂性似乎是有意为之,以显示架构师的专业能力。但实际上,这种复杂的架构设计并不利于软件项目的开发和理解。一个有效的架构,应该是可以被项目团队成员逐步理解的,尤其是那些刚刚接触到项目的新成员。

针对这一挑战,我采纳并推荐一种逐层递进的方法来构建和解释系统架构。这种方法从一个高度概括的视图开始,即级别0(L0),这一级别通常以框图的形式出现,旨在提供一个大致的概览。接下来,级别1(L1)进一步细化为解决方案图,展现了系统的关键组件及其相互作用。最后,级别2(L2)深入到具体的技术选择和供应商产品,详细说明了实现细节。

使用这种层次分明的方法,我们不仅能够有效地将复杂的架构问题拆解为更小、更易管理的单元,而且确保了信息的接收者能够通过逐层深入的方式,逐步建立对整个系统的理解。这种方法强调了架构师的简化能力,不是通过增加不必要的复杂性来展示技术深度,而是通过清晰、有条理的递进,使得复杂的系统变得简化和可理解。

1.2、视觉思维

在深入讨论架构师必备技能的过程中,我们不得不强调视觉思维的重要性。架构师天生倾向于利用视觉手段进行沟通和想法共享,这使得掌握如何通过图形模型表达思维成为了他们的基本技能之一。为了高效地使用这些视觉工具,架构师必须培养出一种能够将复杂概念直观化的思维模式。这种视觉化的方法体现在他们使用白板、记事本进行讨论,甚至在紧急情况下,在草稿纸上画出草图的习惯上。

我的个人经历恰好反映了视觉思维在职业生涯发展中的价值。从青少年时期开始绘制的七龙珠卡通,到在大学期间利用视觉笔记帮助记忆课程内容,这些经历早期就培养了我对视觉表达的喜爱和能力。当我作为一名软件专业人士进入工业界后,这种能力转化为了用草图来沟通复杂的软件概念。

这种视觉思维的能力允许架构师使用各种架构友好的符号和表示法,从基本的块图、流程图到UML和非标准的自定义图形,以清晰和引人入胜的方式传达复杂的架构理念。这不仅使我能够更有效地与同事和社区成员沟通,而且通过这种直观的方法,架构的复杂性变得更易于管理和理解。视觉思维不仅是架构师个人技能的重要组成部分,也是推动软件开发领域知识共享和创新的强大工具。

1.3、技术绘图

在讨论架构师所需技能的时,技术绘图能力不可或缺,架构师借助于技术图表来展示他们的视觉思维,而这些图表远非简单的美术作品,它们是技术信息的视觉传达。因此,架构师们需要不仅学会基本的技术制图技巧,而且还要掌握如何精确绘制几何图形、有效利用图表的空间布局以及提高图表的整体可读性。此外,为了确保所绘制图表的风格一致性和信息的真实性,搭建一个包含配色方案和图标库的媒体工具包也显得非常重要。

从个人经历来看,我们在高中期间就已经接触并使用流程图来描述业务流程,这是我早期接触技术绘图的经历之一。随着成为一名软件架构师,技术绘图成了我表达架构设计思想的关键工具。我的同事们经常对我制作的高质量图表表示好奇,询问我所使用的工具是什么。这里,我想强调的是,真正使图表突出的不仅是工具本身,而更多是背后的思考和绘图模式。我长期使用的processon,作为一款技术绘图工具,以其易用性和高效性赢得了我的青睐。

技术绘图技能的掌握使架构师能够将复杂的技术概念以一种清晰且直观的方式呈现出来,既保证了信息的传达既有逻辑性又有条理,又确保了不同背景的读者都能轻松理解和跟进。这一能力对于在团队内部促进有效沟通,以及与外部利益相关者进行有效交流,都是至关重要的,它让技术细节的讨论变得更加高效、明确,极大地提升了整个项目团队的工作效率和成果的质量。

1.4、系统思维

我们直奔主题:架构师的工作本质上是解决一系列复杂问题的过程。为了有效应对这些挑战,架构师必须装备自己以一种关键的能力——系统思维。这种思维方式要求架构师具备一个双重视角:一方面,需要有能力从宏观上把握和理解整个系统的结构和功能;另一方面,还要能够深入至系统的每一个构成部分,洞察每个组件的细节和作用。简言之,系统思维不仅为架构师在设计和实现预期的结果及目标提供了坚实的基础,它也是帮助他们在保持对大局的把握的同时,精细管理细节的关键。

通过将系统架构师的角色与帮助公司提升业务效率及降低运营成本的目标相结合,我深刻体会到了系统思维能力的重要性。视企业为一个复杂的自适应系统(CAS),超越了我对技术的传统理解,让我以一种更全面、更综合的视角来审视和构思架构。这种方法不仅加深了我对业务流程和需求的理解,而且促进了我与同事们的紧密合作。我们共同设计出的架构方案,旨在服务业务目标的同时,提升运营效率,从而为公司创造了更大的价值。

系统思维是架构师设计高效、有效系统架构不可或缺的基石。它不只是一个技术技能,更是一个跨学科、跨部门合作、优化业务流程的强大工具。通过不断地培养和应用这种思维方式,架构师能够为企业的发展贡献出自己的力量,带来持续的、长远的价值。

1.5、市场洞察

架构师需要了解的是架构师的职责不仅仅局限于设计和实施技术解决方案,更重要的是,他们需要具备敏锐的市场洞察力。这意味着架构师必须密切关注行业中的最新动态——包括技术的快速进步、新模式的出现以及最佳实践的采用。这样做可以使他们应用最前沿的方法来构建精确而高效的架构规划,不仅能够迅速产生价值,而且能够着眼未来,引领潮流。

科技行业的快速发展给架构师带来了不小的挑战,要紧跟这样的发展步伐,需要持续的学习和适应。就我个人而言,我采取了多种策略来保持与行业进步的步调一致。这包括在掘金、CSDN、今日头条、GitHub以及知乎等平台上关注那些在行业中具有影响力的人物,他们的见解和分享往往是洞察行业趋势的窗口。此外,我还订阅了一系列高质量的技术新闻资讯,并积极参与线上和线下的技术论坛,参加相关的技术活动,观看最新的教学视频。我还广泛阅读书籍和文章,以获得更深入的知识和理解。另一个我特别重视的是教学的力量——通过选择性地教授某些技术领域的课程,不仅加深了我对这些领域的理解,同时也为技术社区做出了贡献。

市场洞察力对于架构师而言,是一项至关重要的技能。它要求架构师不停地更新自己的知识库,以确保他们设计的架构方案既能满足当前的需求,又能预见未来的挑战并为之做好准备。通过利用各种信息资源和渠道,架构师可以有效地提升自己的市场洞察力,从而在快速变化的科技环境中保持领先地位。

1.6、沟通能力

架构师在业务和技术之间扮演的独特角色——他们是连接这两个世界的桥梁。借用前新加坡智能国家研究员Gregor Hohpe的比喻,架构师就像是在大厦的结构中,通过电梯连接顶层决策者和地下机房的技术人员,确保信息的自由流通和理解。这要求架构师不仅能够理解复杂的系统,还要能够以简洁明了的方式向非技术人员解释这些系统。有效沟通的方式多样,包括但不限于口头交流、书面文档以及视觉展示。

通过在架构领域近二十年的职业生涯,我深刻体会到沟通技能的重要性,它使我逐渐成为一位能够有效传达技术理念的技术传播者。无论是在指导团队成员、参与市场营销活动,还是在担任面向客户的技术解决方案架构师和业务分析师等角色中,我都在不断地锤炼自己的沟通能力。这些经历不仅加深了我的技术理解,也提升了我与团队成员、客户及合作伙伴之间的沟通效率,促进了彼此之间的理解和协作。

沟通能力对于架构师而言,不仅是一项基本技能,更是其成功不可或缺的关键因素。通过有效的沟通,架构师能够确保业务需求和技术实施之间的顺畅对接,促进项目的顺利进行。因此,不断地学习和提高沟通技能,对于每一位架构师来说都是一项持续的任务,它将在促进团队合作、推动项目成功方面发挥至关重要的作用。

1.7、编程能力

我们需要清晰地认识到一个事实:沟通能力固然对架构师来说至关重要,但如果仅仅专注于沟通而忽视了项目的具体实施,架构师的角色就可能沦为仅存在于理论或演示文稿中的“PPT架构师”。为了避免这种情况的发生,架构师必须投身于项目实施的具体细节之中,亲自参与到日常的工作流程里。借助敏捷开发和自组织团队的现代管理框架,架构师可以与开发团队紧密合作,直接参与到软件开发的过程中,贡献自己的力量。这不仅仅意味着架构师需要有能力进行架构设计,还意味着他们需要动手编码,直接面对各种技术配置挑战。

以我的经历为例,我深切地理解到编码对于架构师角色的极端重要性。在我长达十几年的职业生涯中,我积极参与了众多编码项目,其中包括国内某领先保险公司的首个智能客服和智能外呼系统的开发,一家主业电视企业的智能推荐系统建设,以及一个模块化中间件平台的创建等。即便后来我的工作角色更多地转向了作为对外的技术顾问,我仍未放弃编码工作。我通过为产品增加新的示例、扩展点以及开发演示中所需的用例,继续为项目贡献自己的技术力量。此外,我还利用晚间时间担任技术讲师,不仅以此完成编码任务,更向那些有志于成为程序员的非专业人士传授编码知识。

对于架构师而言,编程能力不仅是一项必备技能,更是他们成功的关键。这要求架构师深入到项目的实施层面,亲自参与到编码和技术配置中去。这种实际参与不仅能够确保所设计的架构得到有效实施,同时也能够促进架构师的技术能力和实战经验的提升。通过这样的实践,架构师能够更加深入地理解开发团队的需求和面临的挑战,进而设计出更加符合实际需要的解决方案,为项目的成功奠定坚实的基础。

2、总结

说了这么多,我们主要聚焦于大型项目或产品里的架构师角色,这些项目通常需求明确,多团队合作。但实际上,不是所有的项目都符合这种模式,也不是所有的团队都需要这样的角色分工。特别是在创业团队中,情况往往大不相同。对这些团队来说,最紧迫的任务是确保项目的生存和发展。因此,他们采纳的是一种更加灵活、快速的开发哲学——敏捷开发。这种方法强调快速迭代、灵活试错,37signals出版的《Getting Real》一书便是这种思维方式的经典之作。它特别适合于那些不依赖复杂底层架构、功能较为简单、可以迅速开发原型并通过连续小迭代进行改进的项目,如Web应用和移动应用程序。

进一步讲,架构师并非技术人员的唯一职业发展路径。实际上,成为架构师要求你在技术的广度与深度上都有所建树,同时还需要掌握大量的业务知识和组织管理能力。然而,很多技术人员更愿意深入挖掘技术本身的深度,他们对于投入大量时间去了解业务逻辑和提升沟通技巧可能并不感兴趣。这样的技术人员更适合走技术专家的路线,专注于深度探索特定的技术领域,如算法设计、编程语言的深度应用、运行环境(包括虚拟机、操作系统、应用服务器、中间件)以及复杂的通信机制等。他们解决的问题为软件技术的抽象化和模式化奠定了坚实的基础,显示出技术专家路径的重要性不亚于架构师。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

我不想再翻代码找export了, 所以

发表于 2024-04-27

写前端, 还是要解决自己需求才好玩。

起因

当我快乐的CRUD的时候,我注意到了一个很没意义的操作:

  1. 当写src/view/xxxx文件时
  2. 总会跑到src/http/api/xxxx 或者 src/tools/xxx 文件, 去看看那个文件 export 的变量名
  3. 然后切回src/view/xxxx 来写 import xxx from ‘xxxx’.
  4. 重复 1-3 N次

我感到疲惫,所以我在想有没有更方便的方式。
比如文件目录中,展示这个文件的exports


然后还能直接拖拽, 拖拽时顺便带上import xxx from xxx


开整
–

于是我开始研究vscode插件的开发教程, 打开官方文档,霍,很简单嘛,也就两句命令.
code.visualstudio.com/api/get-sta…


接下来就是漫长的调研学习,不得不说vscode插件资料比较少,参考了下面的教程:

  • vscode 插件 官方教程中文翻译
  • vscode 插件 Treeview 开发教程
  • vscode Webview 完美集成 Webpack 热更新
  • 案例: 掘金一下
  • 案例: 代码提示
  • 案例: vscode+solidjs

学完发现,其实重要的就是两个逻辑:

  1. 遍历文件目录
  2. AST分析脚本

逻辑1就不说了,比较常规(常规到我直接用chatgpt生成), 大致如下:

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
javascript复制代码async getDirectory(message) {
const directory = message.data.path
const entries = await vscode.workspace.fs.readDirectory(vscode.Uri.file(directory))
const resultPromise = entries.map(async ([name, type]) => {
const filePath = path.join(directory, name)

if (this.ignore.check(filePath, message.data.root))
return
const item: FileItem = {
id: filePath,
path: filePath,
title: name,
fileType: type === vscode.FileType.Directory ? 'Directory' : 'File',
leaf: false,
root: message.data.root,
}
if (type === vscode.FileType.File) {
const extname = path.extname(filePath).replace('.', '')
item.fileExt = extname
if (!['js', 'tsx', 'jsx', 'ts'].includes(extname))
return
}
if (type === vscode.FileType.Directory)
item.children = []
return item
})

const results = await Promise.all(resultPromise)
return [0, results.filter(item => item)]
}

核心还是在逻辑2上,想了想可能有两个办法:

  1. 通过动态import,进行运行时分析
  2. 通过Bable的AST进行静态语法分析

哪么选择哪一个逻辑更好呢? (小孩子才做选择,大人我全都要 !)

  • 对于动态 import,经朋友推荐,调研使用了 antfu 大佬开源的 pkg-exports 以及 local-pkg 库。
  • 对于BabelAST,找到了以下资源学习:
    • Babel官方文档
    • AST查看工具
    • 玩转AST

大致流程就是先babel.parser,再babel.traverse,最后分析AST就完事:

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
32
33
34
js复制代码export async function solveExports(code) {
const ast = parser.parse(code, {
sourceType: 'module',
plugins: ['jsx', 'typescript', 'decorators'],
errorRecovery: true,
})
const exports: any = []
traverse(ast, {
ExportDefaultDeclaration(astPath: any) {
const returnType = solveDetail(astPath)
exports.push({
name: 'default',
type: astPath.node.declaration.type,
returnType: returnType?.type,
children: returnType?.children,
})
},
ExportNamedDeclaration(astPath: any) {
const specifiers = astPath.node?.specifiers || []
specifiers?.forEach((specifier: any) => {
exports.push({ name: specifier.exported?.name, type: specifier.exported?.type })
})
const returnType = solveDetail(astPath)
exports.push({
name: astPath.node?.declaration?.id?.name,
type: astPath.node?.declaration?.type,
returnType: returnType?.type,
children: returnType?.children,
})
},
})
return exports
}
}

最后就是漫长的踩坑,写业务逻辑和业务调试,我后续再依次更新。

  • 比如如何配置工程化
  • 比如如何用solidjs开发webview
  • 比如支持vue,调研学习了vue-compiler
  • 比如分析出export的变量类型,以方便导入import
  • 等等等等

进度

目前感觉初步解决自己的需求,已发布体验版到VSCode商店.

VScode 插件商店搜索 XMouse 即可使用(也可以点击下方链接)

marketplace.visualstudio.com/items?itemN…

image.png

未来

  • 准备进一步分析文件export,以方便更好的使用。
  • 准备集成VSCode原生文件目录的核心功能,就不用来回切换面板。
  • 等等等等,期待建议

最后

我是尘码,曾鹅厂打工5年,现裸辞自由职业家里蹲 ing.

如对 Xmouse 有兴趣,欢迎私聊or提issue

XMouse github link

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

git从入门到入土 Git使用步骤

发表于 2024-04-27

Git使用步骤

1、Git客户端(入门咯)

官网

1
http复制代码https://git-scm.com

下载页面

1
http复制代码https://git-scm.com/download/win

建议下载 64-bit Git for Windows Portable 。

下载后得到 PortableGit-2.41.0-64-bit.7z.exe 自解压文件,

将其复制到 D:/ecuter/applications 目录下,双击启动自解压。

最后将 D:\ecuter\applications\PortableGit\bin 添加环境变量的 Path 变量中。
这里我多提一嘴,如果你默认安装在C盘,那就不用配置环境变量了,在我们环境变量中,已经默认有C盘下载的目录并指向那个文件夹下的bin,bin是什么?一般来说这个文件夹存放了大量的命令,比如你要执行git add . 命令,那你的终端是不是首先要认识git命令?因此才说要有环境变量,告诉你的电脑,git这个可执行命令存放在哪里,也就是配置环境变量的过程,比如你下载D盘的git目录,git底下就有一个用于存放命令的文件夹,那么你配置环境变量,地址指向这个文件夹,电脑就知道命令在哪里,也就认识这个命令了。

配置好环境变量后可以在 命令提示符 中执行 git -v 或 git --version 查看版本信息。

我们可以通过 git --help 或 git -h 来查看 git 帮助信息,比如

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
sh复制代码C:\Users\Administrator>git -h
usage: git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
[--super-prefix=<path>] [--config-env=<name>=<envvar>]
<command> [<args>]

These are common Git commands used in various situations:

start a working area (see also: git help tutorial)
clone Clone a repository into a new directory
init Create an empty Git repository or reinitialize an existing one

work on the current change (see also: git help everyday)
add Add file contents to the index
mv Move or rename a file, a directory, or a symlink
restore Restore working tree files
rm Remove files from the working tree and from the index

examine the history and state (see also: git help revisions)
bisect Use binary search to find the commit that introduced a bug
diff Show changes between commits, commit and working tree, etc
grep Print lines matching a pattern
log Show commit logs
show Show various types of objects
status Show the working tree status

grow, mark and tweak your common history
branch List, create, or delete branches
commit Record changes to the repository
merge Join two or more development histories together
rebase Reapply commits on top of another base tip
reset Reset current HEAD to the specified state
switch Switch branches
tag Create, list, delete or verify a tag object signed with GPG

collaborate (see also: git help workflows)
fetch Download objects and refs from another repository
pull Fetch from and integrate with another repository or a local branch
push Update remote refs along with associated objects

'git help -a' and 'git help -g' list available subcommands and some
concept guides. See 'git help <command>' or 'git help <concept>'
to read about a specific subcommand or concept.
See 'git help git' for an overview of the system.

2、SSH公钥

3.1、生成公钥

在 PowerShell 或 命令提示符 或 Git Bash 中执行以下命令即可生成公钥:

1
sh复制代码ssh-keygen -t ed25519 -C "Gitee SSH Key"

Windows 用户建议使用 Windows PowerShell 或者 Git Bash,

因为在 命令提示符 下无 cat 和 ls 等命令。

详细步骤可以查看由 gitee 官网提供的帮助文档: 生成、添加 SSH 公钥

3.2、添加公钥

这里的添加公钥是在 gitee.com 添加我们刚刚生成的 公钥。

首先登录 Gitee 官网,

随后鼠标悬浮到右上角用户头像处,在下拉菜单中选择设置,

在新开启的页面左侧找到 安全设置 ,点击 SSH公钥 打开添加公钥页面。

详细步骤可以查看由 gitee 官网提供的帮助文档: 生成、添加 SSH 公钥

3.3、可信主机

将 gitee.com 添加到本地的可信主机列表中。

1
sh复制代码ssh -T git@gitee.com

在等待用户输入时,选择输入 yes 后再回车。

该步骤会在用户主目录下的 .ssh 目录中产生 known_hosts 文件。

3、远程仓库

在登录 Gitee 之后,

鼠标悬浮到右上角的 + 号图标,

点击新建仓库即可开始新建仓库操作。

4、本地仓库

我们可以通过两种方式创建本地仓库:

  • 新建一个目录,并在其中执行 git init 初始化该目录为一个本地仓库
  • 通过克隆远程仓库得到一个本地仓库

因为在 第3部分 已经在 Gitee 创建一个远程仓库,所以我们可以将其克隆到本地。

1
sh复制代码git clone git@gitee.com:malajava/spring-tutorial.git

从远程仓库克隆成功后,在本地会生成一个 spring-tutorial 目录。

此时,我们就说这个 spring-tutorial 目录就是一个本地仓库,

在其内部一定包含一个 .git 目录,该目录就是本地仓库的所有配置。

5、代码管理

5.1、状态

首先进入到某个Git仓库内,比如 spring-tutorial 仓库中:

1
cmd复制代码D:\ecuter\codes\spring-tutorial>

通过 git status 来查看该仓库的状态:

1
2
3
4
5
cmd复制代码D:\ecuter\codes\spring-tutorial>git status
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

5.2、添加

将Git本地仓库中所有改动的文件添加到Git本地仓库的暂存区:

1
cmd复制代码D:\ecuter\codes\spring-tutorial> git add .

此处的 . 表示当前目录,而当前目录D:\ecuter\codes\spring-tutorial就是当前Git仓库spring-tutorial的根目录,所以该操作意味着将当前仓库下所有改动的文件添加到暂存区。

此处“改动的文件”包括:新增的文件、被修改的文件、被删除的文件

5.3、提交

将本地Git仓库中暂存区中的内容提交到本地Git仓库:

1
sh复制代码git commit -m "注释"

比如:

1
cmd复制代码D:\ecuter\codes\spring-tutorial> git commit -m "新增了一个Java源文件"

5.4、推送

可以通过 git push 命令将本地Git仓库中已经commit的操作推送到远程仓库:

1
sh复制代码git push origin master

当然你没有配钥匙的情况下,git可能不知道你要推到哪个仓库,因此你可以通过

1
csharp复制代码git remote add origin 加上你的仓库地址

然后再去做推送操作

5.5、拉取

可以通过 git pull 命令从远程仓库抓取最新代码:

1
sh复制代码git pull origin master

6.版本回退

有的时候我们修改了文件,然后把修改好的文件提交到我们的仓库后add. -> commit ->push 后,也许我们会修改很多次这个文件,然后提交很多次,那么在仓库这个文件就会出现很多个版本,但是在我们实际工作时,不可能记得每一次我们做了什么修改因此,git作用就出现了。版本控制系统肯定有某个命令可以告诉我们历史记录,在Git中,我们用git log命令查看。如果嫌输出信息太多,看得眼花缭乱的,可以试试加上--pretty=oneline参数:,那么这些一大串的数字是什么呢?其实就是你的提交版本号,他不直接指明1234,但是每提交一个新版本,实际上Git就会把它们自动串成一条时间线。如果使用可视化工具查看Git历史,就可以更清楚地看到提交历史的时间线,为什么commit id需要用这么一大串数字表示呢?因为Git是分布式的版本控制系统,后面我们还要研究多人在同一个版本库里工作,如果大家都用1,2,3……作为版本号,那肯定就冲突了。

回退

那么我们修改了这么多个版本,如果我们发现现在这个版本不行,还是想要上一个版本怎么办呢?
首先,Git必须知道当前版本是哪个版本,在Git中,用HEAD表示当前版本,也就是最新的提交1094adb...(注意我的提交ID和你的肯定不一样),上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100。

现在,我们要把当前版本append GPL回退到上一个版本add distributed,就可以使用git reset命令:

1
2
csharp复制代码$ git reset --hard HEAD^
HEAD is now at e475afc add distributed

欸?此时已经还原到了上一个版本,注意!!!如果这个时候你后悔了,你还是想要新版本,但是此时,你用git log看此时已经不存在最新的那个版本了,你想后悔怎么办?只要你还没有把当前窗口关闭,那就还有机会,你往回拉,查看那个版本的版本号,版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。

工作区、暂存区

工作区:就是你在电脑里能看到的目录,就你的本地仓库,那个文件夹。

版本库:工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。

Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。

image.png
第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;

第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。

因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。

管理修改

git 管理的是修改而不是文件,你在本地修改了一个文件,然后你add 然后你又对这个文件进行修改,你再commit,那么此时第二次的修改不会提交到master上,因为你的暂存区存的是你第一次修改的,而工作区放的是你第二次修改的,commit是把暂存区的内容推送

撤销修改

人都会犯错,我们如果不小心在文件中添加了一些不该添加的内容,此时我们通过git status可以发现有一个文件修改了,并且git会告诉你git checkout -- file可以丢弃工作区的修改命令git checkout -- readme.txt意思就是,把readme.txt文件在工作区的修改全部撤销,这里有两种情况:

一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;

一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。

分支管理

分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。

现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
首先,我们创建dev分支,然后切换到dev分支:

1
2
css复制代码$ git checkout -b dev
Switched to a new branch 'dev'

git checkout命令加上-b参数表示创建并切换,相当于以下两条命令:

1
2
3
ruby复制代码$ git branch dev
$ git checkout dev
Switched to branch 'dev'

然后,用git branch命令查看当前分支:

1
2
3
markdown复制代码$ git branch
* dev
master

现在,我们把dev分支的工作成果合并到master分支上:

1
2
3
4
5
scss复制代码$ git merge dev
Updating d46f35e..b17d20e
Fast-forward
readme.txt | 1 +
1 file changed, 1 insertion(+)

git merge命令用于合并指定分支到当前分支。合并后,再查看readme.txt的内容,就可以看到,和dev分支的最新提交是完全一样的。`

注意到上面的Fast-forward信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。合并完成后,就可以放心地删除dev分支了:

1
ruby复制代码$ git branch -d dev

git rebase

有没有懂的同学?怎么用?什么时候用?欢迎补充

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

用不了ChatGPT?快试试免费又强大的Anthropic

发表于 2024-04-27

image.png

一、Claude 简介

Anthropic 官方: www.anthropic.com/product

Claude 是最近新开放的一款 AI 聊天机器人,是世界上最大的语言模型之一,比之前的一些模型如 GPT-3 要强大得多,因此 Claude 被认为是 ChatGPT 最有力的竞争对手。Claude 的研发公司是专注人工智能安全和研究的初创公司 Anthropic,由前 OpenAI 员工共同创立的。今年 3 月份 Anthropic 获得了谷歌 3 亿美元的投资,谷歌也因此获得其 10% 股份。

image.png

image.png

Anthropic 官网

据官方介绍,Claude 的核心模型经由训练,目标是变得有用、诚实和无害。此外 Claude 更能理解和接受自然语言,和它对话无需复杂的技巧,可以轻松得到详细且易于理解的答案。它目前有两种型号 Claude-v1 和 Claude Instant:

① Claude-v1:功能强大的模型,可以处理复杂的对话、生成创意内容和详细说明。

② Claude Instant:更快更便宜的模型,可以处理偏随意的对话,对文本进行分析和摘要以及根据文档进行问答。

支持订阅Claude的虚拟卡,点击获取

与 ChatGPT 等大型语言模型一样,Claude 的应用场景非常广泛,信息搜索、内容总结摘要、写作协助、创意生成、问答、编程这些任务它都能轻松完成。目前 Claude 已经被应用在多个知名产品中,比如知识笔记工具 Notio AI 就是用 Claude 协助用户进行智能写作,国外问答社区 Quora 也在自己的 AI 聊天应用程序 Poe 中置入了 Claude。

image.png

虽然是用英语语言训练的,但是 Claude 也能很好的理解中文等其他语言。为了测试 Claude 的中文理解及创作能力,我让它写了一首赞美设计师的诗。它的反应速度很快,几秒内就给出了内容,创作的质量也很不错,运用比喻修辞手法,还知道中英文结合使用,感觉比我厉害多了。使用过程中我发现如果聊天时间间隔太长,Claude 会自动断线,发消息它没有反应,需要刷新网页后才能重新连接上。

二、Claude 使用途径

Slack-Claude 官方网址: www.anthropic.com/claude-in-s…

Claude 已经被置入一款团队协作沟通应用 Slack 中,目前可以免费使用。具体的注册使用方法如下:

image.png

三、Claude 的不足

Claude 也依旧存在着很多和其他 AI 聊天机器人一样的缺陷,首先是它没有接入互联网,模型训练的内容来自于 2021 年春季以前,所以无法提供日期、天气、新闻等实时信息或据此提供进一步的判断。

Claude 也一直在聊天的过程中提醒用户自身存在的局限性,比如在处理高度抽象的概念、类比、隐喻方面有困难;会产生“幻觉”,编造不存在的对象和内容;不擅长处理复杂数学和推理问题;无法根据过往的聊天信息更新自身内容或纠正目前存在的问题等,所以使用 Claude 时我们还是要保持谨慎,对重要的信息要进行再次验证。

支持订阅Claude的虚拟卡,点击获取

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

轻量级代码仓库平台:CodeFever,解锁超速代码协作方式

发表于 2024-04-27

CodeFever_:_跨越传统边界,赋予团队无限协作力量 ,您的下一代开源Git服务平台 —— 精选真开源,释放新价值。

image.png

概览

CodeFever是由经验丰富的蒲公英团队经过精心研发和打磨推出的一款注重实效、高性能且极具灵活性的开源Git服务器系统。该平台摒弃了冗余繁复的设计元素,更侧重于构建一套坚固耐用、稳定可靠的底层基础架构,从而充分满足现代软件开发团队在日常工作中对版本控制、协同编程、权限配置以及自动化工作流等方面的核心需求。

在版本控制方面,CodeFever提供了直观且详尽的历史记录追溯能力,使得团队成员能够方便地查看、对比和回滚代码变更,有力保障项目的迭代演进过程有迹可循。

协同编辑功能上,CodeFever不仅允许多人同时在线编辑同一份代码,还通过分支管理机制以及全面的合并请求(Pull Request)功能,鼓励团队进行更为有序、高效的代码审阅与整合。

针对权限管理,CodeFever特别设计了一套细致入微的角色与权限模型,确保每一位团队成员都能根据其职责范围获得相应的访问和操作权限,有效避免了潜在的安全风险,同时也提高了团队间的信息透明度。

此外,CodeFever无缝对接各类流行的CI/CD工具和其他第三方服务,简化了从代码提交到最终部署的全流程管理,让自动化工作流的构建变得轻而易举。

image.png


主要功能

你可以在线体验:http://114.132.251.156/repositories

  • 一键部署

CodeFever采用简洁直观的安装部署方式,只需简单几步操作,无论是技术新手还是资深开发者,均能在短时间内搭建起专属的私有Git仓库服务器。无需复杂的环境配置和服务器设置,几分钟内就能完成从零到一的快速部署,极大地节省了团队宝贵的时间成本,使团队可以迅速投入到实质性的开发工作中去。

image.png

  • 强大协作

CodeFever深度优化了多人协作体验,多个团队成员可以实时在同一代码库中进行编辑和提交操作,大大提升了工作效率。系统内置完善的分支管理机制,使得团队可以根据不同任务或阶段创建独立的工作分支,每个分支之间互不影响。同时,CodeFever支持基于合并请求的功能,通过详细的代码审查和讨论,确保每次代码合并的质量和一致性。

image.png

  • 精细权限控制

为了确保代码资产的安全性及团队内部操作的透明公正,CodeFever建立了一套健全且灵活的角色与权限管理体系。团队管理员可以根据成员角色和职责分配不同的访问权限,包括但不限于读取、写入、合并代码等,做到权限细分至文件层级,真正实现了对代码资源的精细化管控。

image.png

  • 无缝集成

CodeFever巧妙地集成了各类主流CI/CD工具及第三方服务,如Jenkins、Travis CI、GitHub Actions等,让团队能够轻松实现从代码提交到测试、构建、部署的全程自动化。这种端到端的持续集成与交付流程不仅降低了人为错误的发生概率,还极大提高了软件开发生命周期的管理效率。

image.png

  • 轻量高效

考虑到大型代码库在处理速度和响应时间方面的挑战,CodeFever采用了先进的性能优化技术,使其在承载海量代码时仍能保持出色的运行效率。无论是克隆、拉取、推送还是搜索操作,CodeFever都能够确保操作流畅,带给开发者无延迟的优质使用体验。

image.png


信息

截至发稿概况如下:

  • 软件地址:github.com/PGYER/codef…
  • 软件协议:MIT
  • 编程语言:
语言 占比
PHP 64.7%
JavaScript 33.4%
Shell 0.6%
CSS 0.5%
HTML 0.4%
Go 0.3%
Other 0.1%
  • 收藏数量:2.6K

CodeFever以其务实的态度和强大的功能特性,正在逐步成为众多软件开发团队的理想选择。然而,在实际运用过程中,如何进一步提升CodeFever在大规模团队协作场景下的表现?又如何将更多先进的DevOps理念融入产品设计,使之更好地服务于企业的全栈开发流程?这些都是值得我们深入探讨和研究的问题,期待您加入CodeFever社区,共同推动这款开源Git服务器解决方案的发展与完善。

各位在使用 CodeFever 的过程中是否发现了什么问题?或者对 CodeFever 的功能有什么提议?热烈欢迎各位在评论区分享交流心得与见解!!!


声明:本文为辣码甄源原创,转载请标注”辣码甄源原创首发***“**并附带原文链接。***

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Poe!集齐4大 AI 聊天工具的神器,再也不同担心用不上

发表于 2024-04-27

自从 ChatGPT、New Bing、Claude等智能聊天机器人出现之后, 信息搜集、写作等工作变得前所未有的高效和便捷,ChatGPT 还可以扮演不同行业的专家,对我们在工作和生活中遇到的问题给出建议;或者扮演不同的历史人物角色和我们进行对话,让人与机器的沟通变得有趣和深刻。

由于各种原因可能有些朋友到现在都没有办法在 Open AI 官网上使用 ChatGPT,今天就为大家推荐一款 AI 聊天应用 Poe,它集成了 ChatGPT、GPT-4、Claude 等多款目前最强大的聊天机器人, 让我们轻松实现在一个平台上同时与多个不同的机器人进行对话

一、Poe 简介

网址直达: poe.com (用 Chrome 或者 Edge 浏览器打开)

Poe 是国外问答社区 Quora 推出的一款 AI 问答应用,Poe 并没有自己的大语言模型,而是将目前最强大的一些 AI 聊天机器人集合到一起,形成了一个 AI 问答互动平台,用户可以同时对多个聊天机器人提出问题,并快速获得相应的回答。

image.png

Poe 目前一共收录了 Sage、ChatGPT、GPT-4、Claude+、Claude-Instant-100k、Claude-Instant、Dragonfly 7 款聊天机器人。其中 ChatGPT 和 GPT-4 大家应该都非常熟悉了,它们是目前最强大的 2 款 AI 聊天机器人;Claude 之前我也有专门向大家介绍过,Poe 里有三个不同版本的 Claude 机器人,它们的区别如下:

① Claude+:功能强大完整,特别擅长创意写作和提供详细的回复,处理复杂困难任务的效果要明显优于 Claude-instant。

② Claude-Instant:也擅长处理创造性的写作任务,并且往往会给出更长、更深入的答案,适合对文本进行分析摘要、根据文档进行问答。在处理非英语语言时速度更快,效果也明显更好。

③ Claude-Instant-100K:可以接受 75000 个英文单词的超长上下文,这意味这我们可以发送给它数百页的资料,让它帮我们分析总结资料的内容。

点击获取支持ChatGPT、POE、Claude的虚拟卡

二、Poe 的注册

Poe 的注册十分简单,进入首页之后就是登录页面,我们可以选择手机、邮箱和 Apple ID 三种注册方法。注册完成后,就能直接与 ChatGPT 和 Claude-Instant 的机器人聊天,无需与 Open AI 或 Claude 的账号进行关联,这对没有办法注册 ChatGPT 账号的朋友来说是一种不错的解决方案。

image.png

Poe 除了网页版还有 iOS 和 Android 的 APP,如果你使用的是苹果手机,可以去应用商店搜索名称看看是否对应的程序,Android 则是需要去 Google Play 下载。

Poe 虽然一共有 7 个聊天机器人,但是能免费使用的只有前面提到的 ChatGPT、Claude-Instant 和 DragonFly 几个。没有开通会员的话,GPT-4 和 Claude+ 每天只有 1 条的免费提问额度,而 Claude-Instant-100k 则必须要付费后才能使用。

POE开通会员需要使用到虚拟卡,点击获取支持Poe的虚拟卡

微信图片_20240108105643.png

image.png

Poe 的会员收费换算成人民币大概是 150左右 元/月或 1300 元/年,比 GPT-4 的要贵一点。在 Poe 上我们可以同时和多个不同的 AI 智能机器人对话,但即使付费后也无法使用 GPT-4 的插件,所以大家可以根据自己实际需要选择。如果使用要求不高的话,Poe 里免费的 ChatGPT 和 Claude-Instant 也做够我们使用了。

除了这些 AI 聊天机器人,Poe 里还有其他各种好用的工具应用。我在它左侧工具栏的「Explore Bots」里发现了一个“Midjourney Photo Prompter”机器人,发给一段简单的描述给它,它就能返回一段详尽的文本提示词,帮助我们在 midjourney 生成更丰富精致的画面。

image.png

image.png
POE开通会员需要使用到虚拟卡,点击获取支持Poe的虚拟卡

如果你还没办法用上 ChatGPT, 可以尝试在 Poe 中使用。喜欢本期推荐的话记得点赞收藏支持一波,感谢!

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

1…678…956

开发者博客

9558 日志
1953 标签
RSS
© 2025 开发者博客
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%