2024年9月

本篇是 Python 系列教程第 18 篇,更多内容敬请访问我的 Python 合集

1 打开文件

通常使用内置的
open(文件路径, 模式, encoding="utf-8")
函数。

  • 文件路径:可以是相对路径或绝对路径。
  • 模式:(可选)决定了文件打开后如何处理文件。
  • encoding:(可选)编码方式。

常见的模式有:

  • 'r' (默认):只读模式,如果文件不存在,则会引发 FileNotFoundError。
  • 'w':写入模式,如果文件存在则会清空内容再写入,如果文件不存在则创建新文件。
  • 'a':追加模式,在文件末尾追加内容,如果文件不存在则创建新文件。
  • 'b':二进制模式,用于处理二进制文件(如图像、音频等)。
  • '+':更新模式,用于同时进行读写操作。

可以将这些模式组合起来使用,例如:

  • 'rb':以二进制模式读取文件。
  • 'wb':以二进制模式写入文件。
  • 'ab':以二进制模式追加内容到文件。
  • 'r+':读写模式,可以读取和写入文件。
  • 'w+':写入并读取模式,先写入后读取。
  • 'a+':追加并读取模式,先追加后读取。

2 文件读取

如果
open()
函数执行成功,会返回一个文件对象。后续可以对这个对象进行读取或写入操作。

f = open('./data.txt', 'r', encoding='utf-8')

文件对象有个
read()
方法,会一次性读取文件里的所有内容,并返回字符串格式。

实例:

file = open('data.txt', 'r', encoding="utf-8")
print(file.read())

注意:

  • 如果调用过
    read()
    后再次调用,会返回空,因为程序会记录文件被读取的位置,第一次
    read()
    时已经读到文件末尾,第二次
    read()
    后面就没有内容了。
  • 大文件也不适合用
    read()
    ,因为会一次性读取文件的所有内容,可能挤爆内存。

上面说大文件不适合用
read()
,那有别的替换方案吗?当然有,解决方案就是一次性只读取部分内容,如:


  • read()
    传一个数字参数如
    read(1024)
    ,表示一次读多少字节,下次再调用
    read(1024)
    时就会继续从上次的结束位置读取。
  • 使用
    readline()
    ,此方法一次只会读取一行内容,是根据换行符来判断本行结尾的,而且换行符也会被当做内容的一部分被读取。

上面的两种方式可搭配
while
循环来用,另外还有一个
readlines()
方法,会一次性返回所有行,组成一个字符串列表,一般搭配
for
循环使用。

3 文件写入

调用
open()
函数时第二个参数传
w

a
就可以进行文件写入操作。这里说明一点,如果第二个参数是
r
,且文件不存在就会报
FileNotFoundError
错误,但是
w

a
就不会报错,它会自动创建一个文件。

在打开或创建文件之后就可以调用文件对象的
write()
方法进行写入操作了。

示例:

with open('data.txt', 'w', encoding="utf-8") as file:
    file.write("hello ")
    file.write("python")

data.txt内容:

hello python

注意
write()
方法并不会自动换行,需要手动添加换行符,如把代码改成
file.write("hello\n")
,data.txt就会变成:

hello
python

如果我们使用
w
模式,文件就只能写入不能读取,如果我们想先读取再写入,可以用
r+
模式

4 关闭文件

文件操作完毕后,文件对象需要调用一个
close()
方法关闭文件释放资源。

每次文件操作完毕后都应该关闭文件,但是有可能会粗心忘记关闭,怎么办呢?我们可以在调用
open()
函数打开文件的时候使用
with
关键字,然后用
as
指定读取到的文件,这样在
with
代码块执行完后文件就会被自动关闭了,实例:

# 使用 with 语句来读取文件
with open('data.txt', 'r', encoding='utf-8') as file:
    for line in file.readlines():
        print(line.strip())

前言

.NET 8 的发布,微软推出了官方免费且开源的 Blazor UI 组件库 —— Fluent UI Blazor。

组件库提供了Web应用程序所需的工具,确保应用程序能够与 Microsoft 产品保持一致的外观和感觉。Fluent UI Blazor 不仅包含了对微软官方 Fluent UI Web Components 的封装,还提供了额外的组件和功能,简化开发流程,增强用户体验。

本文将介绍 Fluent UI Blazor 的特点和优势,以及帮助大家如何快速上手,利用这一强大的工具集来提升 Blazor 项目。

项目介绍

Blazor 是一个利用 C# 语言的强大框架,专门用于搭建交互式的 Web 应用程序。结合 Microsoft 的 Fluent UI 库,我们可以轻松创建既时尚又响应迅速的用户界面。

Microsoft Fluent UI Blazor 组件库专为 ASP.NET Core Blazor 应用程序设计,适用于 .NET 8 Blazor 项目。

若使用的是 .NET 6 或 7,请选用名为 Microsoft.Fast.Components.FluentUI 的 v3 版本。

Fluent UI Blazor 是微软官方推出的 UI 组件库,帮助我们搭建符合 Fluent 设计风格的应用程序,赋予应用现代 Microsoft 产品的外观和感觉。

该库不仅包含了微软官方 Fluent UI Web Components 的封装,还提供了额外的支持和组件,以便更好地利用 Fluent 设计系统,简化 Fluent UI 的集成过程。

项目特点

Blazor 是一个使用 .NET 框架和 C# 编程语言的 UI 框架,采用 Razor 语法来构建 Web 应用程序。

Blazor 支持开发单页应用(SPA)和 Web 服务,通过编译后的 C# 代码直接操作 HTML DOM,从而减少了对 JavaScript 的依赖。

其目标是让开发人员能够使用熟悉的 C# 语言编写 Web 应用程序,从而提高开发效率并降低学习成本。

  • Fluent 设计
    :组件遵循 Microsoft 的 Fluent 设计系统,提供现代且统一的用户界面。
  • 易于使用
    :通过 dotnet 模板或手动安装 NuGet 包,可以轻松集成 Fluent UI Blazor 组件到项目中。
  • 可定制性
    :基于 FAST(Adaptive UI)技术构建的组件支持高度定制和个人化,同时自动保持可访问性标准。
  • 附加包
    :提供了包含 Fluent UI 系统图标和表情符号的额外包,增强应用的视觉效果

项目源码

通过GitHub地址下载项目源码,选择不同版本的分支,进行Fluent UI Blazor 组件的学习和查阅,具体如下图所示:

项目使用

1、 创建 Blazor 项目

首先创建一个新的 Blazor Server 或 Blazor WebAssembly 项目,添加对应的 NuGet 包,然后将Fluent UI 集成到 Blazor。

2、创建一个示例数据源

在数据驱动的应用程序中,连接数据源是必不可少的步骤。

示例将使用内存中的数据服务来模拟数据源。创建一个 Services 文件夹,并添加 DataService.cs 文件,定义数据模型和获取数据的方法:

namespaceBlazorAppDemo.Services
{
/// <summary> ///数据服务/// </summary> public classDataService
{
/// <summary> ///产品数据/// </summary> private readonly List<Product> Products = new()
{
//模拟产品数据 new() { Id = 1, Name = "DotNet技术匠01", CreateTime =DateTime.Now },new() { Id = 2, Name = "DotNet技术匠02", CreateTime =DateTime.Now },new() { Id = 3, Name = "DotNet技术匠03", CreateTime =DateTime.Now },new() { Id = 4, Name = "DotNet技术匠04", CreateTime =DateTime.Now },new() { Id = 5, Name = "DotNet技术匠05", CreateTime =DateTime.Now }
};
/// <summary> ///获取产品列表/// </summary> /// <returns></returns> public List<Product>GetProducts()
{
returnProducts;
}
}
/// <summary> ///产品实体/// </summary> public classProduct
{
/// <summary> ///产品ID/// </summary> public int Id { get; set; }
/// <summary> ///产品名称/// </summary> public string Name { get; set; }
/// <summary> ///创建时间/// </summary> public DateTime? CreateTime { get; set; }
}
}

3、注册服务

在Program.cs 中注册服务,以便在应用中使用:

builder.Services.AddSingleton<DataService>();
builder.Services.AddSingleton
<IKeyCodeService, KeyCodeService>();

4、使用 Fluent UI 组件创建 UI

初始化的数据源已经设置好,接下来是创建用户界面。

打开 Pages 文件夹中的 Index.razor 文件,并替换其内容:

@page "/"
@using BlazorAppDemo.Services
@using Microsoft.Fast.Components.FluentUI
@inject DataService DataService
<FluentCard> <h3>产品列表</h3
<FluentStack
> <FluentDataGridItems="@products.AsQueryable()"TGridItem="Product"> <PropertyColumnProperty="@(p => p.Id)"Sortable="true" /> <PropertyColumnProperty="@(p => p.Name)"Sortable="true" /> <PropertyColumnProperty="@(p => p.CreateTime)"Format="yyyy-MM-dd hh:mm:ss"Sortable="true" /> </FluentDataGrid> </FluentStack> </FluentCard>
@code {
private List
<Product>products;

protected override void OnInitialized()
{
// 初始化时加载产品数据
products = DataService.GetProducts();
}
}

5、运行程序效果

打开浏览器,访问
https://localhost:xxxx
,我们就能够看到使用Fluent UI创建的产品列表。

UI组件展示

在线文档演示:
https://www.fluentui-blazor.net

通过访问在线文档的地址,查看每个组件的最终效果图以及组件代码的编写,可以方便我们快速查阅和学习,提高开发效率。

1、主题

2、表格

3、选择框

4、滑块

5、进度条

6、按钮

本文仅展示了部分组件,更多组件详情请访问在线文档地址进行查阅。文档中可以找到详细的组件列表、使用示例以及最佳实践,帮助大家充分利用 Fluent UI Blazor 的全部功能。

项目地址

GitHub:
https://github.com/microsoft/fluentui-blazor

在线文档:
https://www.fluentui-blazor.net

项目总结

通过结合 Blazor 的组件模型与 Fluent UI 强大的组件库,我们能够快速开发专业且现代的 Web 应用程序。

Blazor技术栈不仅简化了开发流程,还提供了高度的灵活性和可扩展性,以应对不断变化的用户体验需求。

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号
[DotNet技术匠]
社区,与其他热爱技术的同行一起交流心得,共同成长!
优秀是一种习惯,欢迎大家留言学习!

完结篇
:tarjan 求割点、点双连通分量、割边(桥)(附 40 道很好的 tarjan 题目)。

上一篇(tarjan 求强连通分量,缩点,求边双)



tarjan 求割点

还是
求强联通分量
的大致思路捏.

算法思路:

我们把图中的点分为两种: 每一个联通子图搜索开始的
根节点

其他点

判断是不是割点的方式如下:

  • 对于根节点

    记一下在跑 tarjan 过程中从这个点出发的未被搜索到的子节点的数量
    \(child\)
    ,如果
    \(child\ge 2\)
    ,那么这个点为割点,否则就不是割点;


    • 证明(很简单,建议自己先想一想为什么或者手模个图,实在不懂再看证明):

    设根节点为点
    \(u\)
    ,如果
    \(child\)
    为 1 的话,说明根节点的不同子树上的点可以不经过点
    \(u\)
    而互相到达,也就是说即使删了
    \(u\)
    点,图中所有点照样可以互相到达,则
    \(u\)
    不为割点。反之,同样也易证得为割点。

  • 对于其他点

    判断一个点
    \(u\)
    有没有一个子节点
    \(v\)
    使得
    low[v] >= dfn[u]
    ,若存在,则
    \(u\)
    为割点,否则不为割点。


    • 证明:

    因为我们在无向图中跑 tarjan 时,已经特判父节点或边的编号来避免走“回头路”了(详见上文求边双部分),所以如果满足判断条件时,说明
    \(v\)
    通过返祖边只能到达
    \(u\)
    及其子树部分,并不能到达
    \(u\)
    的祖先节点,所以若
    \(u\)
    点删去,那么
    \(v\)
    点便与
    \(u\)
    以上部分断开了,此时显然
    \(u\)
    为割点。

会判断这两类点是不是割点之后就做完了,相信大家也知道该怎么求了。看代码吧!

算法代码:

相比于求边双部分增加了数组
cut[x]
来判断
\(x\)
是不是割点,若为 true 则
\(x\)
是割点,否则不是。

void tarjan(int x, int p){
    low[x] = dfn[x] = ++th;
    s[++top] = x; int child = 0;
    for(int i=head[x]; i; i=nxt[i]){
        int y = to[i];
        if(y == p) continue;
        if(!dfn[y]){
            tarjan(y, x);
            low[x] = min(low[x], low[y]);

            if(low[y] >= dfn[x]){
                child++;
                if(p != 0 or child > 1) cut[x] = 1;
            }
        }
        else low[x] = min(low[x], dfn[y]);
    }
}

为什么
p == 0
说明
\(x\)
为根节点呢,大家肯定知道啦!

因为主函数中是这么写的:

    for(int i=1; i<=n; i++)
        if(!dfn[i]) tarjan(i, 0);



tarjan 求点双连通分量

我该先写求点双好呢还是先写求割边好呢,这俩都是需要割点的相关知识的,啊选择困难症(

但是的但是,学会求割点之后那求点双(简称 BCC)就很简单啦
啦啦小魔仙,玛卡巴卡,卡巴露露,摇身变!

算法思路:

性质:
无向连通图中割点一定属于至少两个BCC,非割点只属于一个BCC

如此图:2 号点是个割点,其他点则不是。有红、蓝两个 BCC。


有一条显然的结论:
每个点双,它在 dfs 时最先被发现的点一定是割点或者 dfs 树的树根

证明:这很显然吧?!根据割点的定义自己理解一下,不证明。算了,还是简单说一下吧:我们知道割点是 BCC 的交点,即 BCC 通过割点连接,从一个 BCC 到另一个 BCC 一定是从经过割点开始的,所以证得。

那么这条结论其实就等价于
每个 BCC 都在其最先被发现的点(一个割点或根节点)的子树中
。那么我们在上文求割点方法基础上每找到一个割点(或根节点)后,其子树(包含自己)便是一个点双连通分量了。

实现:

我们还是维护一个栈,存点,每当搜索到一个点时就将该点入栈,找到割点(就是找到一个 BCC)时将栈顶到该割点所有元素依次出栈,(但注意:割点并不出栈,因为上文已说一个割点属于两个 BCC,它还需要来更新另一个 BCC,所以先不出栈,特判就行。)那么出栈的元素以及割点就是所求的点双了。

算法演示:

如上图中,我们以 1 为根开始搜索;

搜索到 2 节点时,继续递归 2 -> 3 -> 4;发现
\(low_4 = 2 < dfn_3\)
,那么 3 号点则不是割点,回溯;


\(low_3 = dfn_2\)
,所以
\(2\)
号点是割点,那么将此时栈中从栈顶到
\(2\)
号点所有元素出栈形成点双;

此时栈从栈尾到栈顶依次是:1,2,3,4。那么便是 2,3,4 构成一个点双(但 2 还在栈中)。

继续回溯到 2 -> 1;发现 1 号点是根节点,也将栈中元素出栈(这时 1 是根节点,所以 1 也出栈),那么 1,2 就又构成了一个点双。


算法代码:

void tarjan(int x, int p){
    low[x] = dfn[x] = ++th;
    s[++top] = x;
    if(!p and !head[x]){ // 特判孤点
        BCC[++bcc].emplace_back(x);
        top--; return;
    }
    for(int i=head[x]; i; i=nxt[i]){
        int y = to[i];
        if(y == p) continue;
        if(!dfn[y]){
            tarjan(y, x);
            low[x] = min(low[x], low[y]);
            if(low[y] >= dfn[x] or !p){ //是割点或者根节点
                ++bcc;
                do BCC[bcc].emplace_back(s[top]);
                while(s[top--] != y);
                BCC[bcc].emplace_back(x);
            }
        }
        else low[x] = min(low[x], dfn[y]);
    }
}

例题:

【模板】点双连通分量

板子题练练手。注意这题需要判孤点情况。



tarjan 求割边(桥)

太简单啦!

和割点差不多,改一条:
low[y] > dfn[x]
,并且不需要特判根节点了(因为 边 != 点)。

解释:(在判边保证不走回头路的条件下)
\(low_y = dfn_x\)
时,说明不通过从
\(x -> y\)
这条路径,
\(y\)
也照样可以回到
\(x\)
节点,那么就保证从
\(y\)

\(x\)
有两条路径可走了,所以
\(x->y\)
这条路不是割边。

那么完了!

算法代码:

cut[i] = true;
表示
\(i\)
这条路为割边。

void tarjan(int x, int p){
    low[x] = dfn[x] = ++th;
    s[++top] = x;
    for(int i=head[x]; i; i=nxt[i]){
        int y = to[i];
        if(y == p) continue;
        if(!dfn[y]){
            tarjan(y, x);
            low[x] = min(low[x], low[y]);
            if(low[y] > dfn[x]){ 
                cut[i] = true;
            }
        }
        else low[x] = min(low[x], dfn[y]);
    }
}



题目练习:

这有个很好的
tarjan 题单
,从模板到进阶,题都很好,推荐给大家。

所含题目如下
P1656 炸铁路

P1455 搭配购买

P3916 图的遍历

P2835 刻录光盘

P1073 [NOIP2009 提高组] 最优贸易

P2863 [USACO06JAN]The Cow Prom S

P8436 【模板】边双连通分量

P8287 「DAOI R1」Flame

P2002 消息扩散

P2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G

P3387 【模板】缩点

P3388 【模板】割点(割顶)

P8435 【模板】点双连通分量

P1407 [国家集训队]稳定婚姻

P2194 HXY烧情侣

P2746 [USACO5.3]校园网Network of Schools

P2812 校园网络【[USACO]Network of Schools加强版】

P2941 [USACO09FEB]Surround the Islands S

P2860 [USACO06JAN]Redundant Paths G

P3398 仓鼠找 sugar

P2169 正则表达式

P3627 [APIO2009] 抢掠计划

P2656 采蘑菇

P4306 [JSOI2010]连通数

P5676 [GZOI2017]小z玩游戏

P1656 炸铁路

P1455 搭配购买

P3916 图的遍历

P2835 刻录光盘

P1073 [NOIP2009 提高组] 最优贸易

P2863 [USACO06JAN]The Cow Prom S

P8436 【模板】边双连通分量

P8287 「DAOI R1」Flame

P2002 消息扩散

P2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G

P3387 【模板】缩点

P3388 【模板】割点(割顶)

P8435 【模板】点双连通分量

P1407 [国家集训队]稳定婚姻

P2194 HXY烧情侣

P2746 [USACO5.3]校园网Network of Schools

P2812 校园网络【[USACO]Network of Schools加强版】

P2941 [USACO09FEB]Surround the Islands S

P2860 [USACO06JAN]Redundant Paths G

P3398 仓鼠找 sugar

P1262 间谍网络

P4742 [Wind Festival]Running In The Sky

P8867 [NOIP2022] 建造军营

P3469 [POI2008]BLO-Blockade

P2515 [HAOI2010]软件安装

P5058 [ZJOI2004]嗅探器

P7687 [CEOI2005] Critical Network Lines

P7924 「EVOI-RD2」旅行家

P5236 【模板】静态仙人掌

P3225 [HNOI2012]矿场搭建

P4716 【模板】最小树形图

P4126 [AHOI2009]最小割

P6335 [COCI2007-2008#1] STAZA

P4637 [SHOI2011]扫雷机器人

P5236 【模板】静态仙人掌

P4716 【模板】最小树形图

P4436 [HNOI/AHOI2018]游戏



End

历时多天,终于把这两篇 tarjan 写完了。

tarjan 都学了,那下一章包得是圆方树的啦(

\(敬请期待......\)

什么是 Locust

Locust 是一个开源的负载测试工具,用于测试网站和其他应用程序的性能。它通过编写 Python 脚本来定义虚拟用户的行为,模拟这些用户对目标系统发起请求。Locust 提供了一个直观的 Web 界面,允许用户实时监控测试进度和性能指标。

主要特性包括:

  • Python 编写测试脚本
    :使用 Python 脚本定义虚拟用户行为,方便且灵活。
  • 实时监控
    :通过 Web 界面查看实时测试结果,包括请求速率和响应时间等。
  • 分布式测试支持
    :可以在多台机器上运行虚拟用户,模拟更高负载的测试场景。

Locust 的优势

  1. 易于使用
    :由于 Locust 使用 Python 编写测试脚本,开发者可以快速上手并编写复杂的测试用例。
  2. 高性能
    :Locust 能够模拟大量并发用户,适合大规模负载测试。
  3. 实时监控和分析
    :提供详细的实时测试报告和性能指标,帮助用户快速定位性能瓶颈。
  4. 分布式测试
    :支持将测试分布到多台机器上,扩展测试能力以满足需求。
  5. 可扩展性
    :通过插件系统可以扩展 Locust 的功能,适应不同的测试需求。

如何部署 Locust

Locust 的部署过程较为简单,以下是基本的步骤:

  1. 安装 Locust


    pip install locust
  2. 编写测试脚本
    :创建一个 Python 文件(如
    locustfile.py
    ),定义虚拟用户的行为。例如:


    from locust importHttpUser, task, betweenclassWebsiteUser(HttpUser):
    wait_time
    = between(1, 5)

    @task
    defload_main_page(self):
    self.client.get(
    "/")
  3. 启动 Locust


    locust -f locustfile.py

    这将启动 Locust 并在默认端口 8089 上启动 Web 界面,您可以在浏览器中访问
    http://localhost:8089
    来配置和启动测试。

如何将 Locust 部署到 Kubernetes (K8s)

在 Kubernetes 中部署 Locust 可以利用其弹性和分布式特性,支持大规模负载测试。部署过程涉及到两个主要组件:Master 节点和 Worker 节点。下面是详细的配置步骤:

1. 创建 Dockerfile

首先,创建一个 Dockerfile,将 Locust 测试脚本直接包含在 Docker 镜像中:

#使用官方 Locust 镜像作为基础镜像
FROM locustio/locust#将 Locust 测试脚本复制到镜像中的 /mnt/locust 目录
COPY locustfile.py /mnt/locust/locustfile.py#设置默认命令,Master 和 Worker 节点会根据需要在 Kubernetes 部署时覆盖
CMD ["locust"]

2. 构建 Docker 镜像

在包含 Dockerfile 和 Locust 测试脚本(
locustfile.py
)的目录下,运行以下命令构建 Docker 镜像:

docker build -t my-locust-image .

3. 创建 Kubernetes 部署文件

a. Locust Master 部署文件
locust-master-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: locust
-master
spec:
replicas:
1selector:
matchLabels:
app: locust
role: master
template:
metadata:
labels:
app: locust
role: master
spec:
containers:
- name: locust-master
image: my
-locust-image
command: [
"locust", "--master"]
ports:
- containerPort: 8089name: http- containerPort: 5557name: communicate

b. Locust Worker 部署文件
locust-worker-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: locust
-worker
spec:
replicas:
3selector:
matchLabels:
app: locust
role: worker
template:
metadata:
labels:
app: locust
role: worker
spec:
containers:
- name: locust-worker
image: my
-locust-image
command: [
"locust", "--worker", "--master-host=locust-master"]

c. 创建 Locust Master 服务文件
locust-service.yaml

apiVersion: v1
kind: Service
metadata:
name: locust
-master
spec:
ports:
- port: 8089targetPort:8089name: http- port: 5557targetPort:5557name: communicate
selector:
app: locust
role: master

4. 部署到 Kubernetes

应用这些 Kubernetes 配置文件以部署 Locust:

kubectl apply -f locust-master-deployment.yaml
kubectl apply
-f locust-worker-deployment.yaml
kubectl apply
-f locust-service.yaml

5. 访问 Locust Web 界面

使用
kubectl port-forward
暴露 Locust Master 服务,以便访问 Locust Web 界面:

kubectl port-forward service/locust-master 8089:8089

总结

Locust 是一个功能强大的负载测试工具,提供了易用的 Python 脚本接口和实时监控功能。将 Locust 部署到 Kubernetes 中,能够利用 Kubernetes 的弹性和扩展性进行大规模负载测试。通过在 Docker 镜像中包含 Locust 测试脚本,简化了部署过程,并提高了配置的一致性和管理的便捷性。借助 Locust 和 Kubernetes,您可以有效地测试系统的性能,确保应用程序在高负载情况下的稳定性和可靠性。

前言

以我的视野来看,部长或技术总监这种岗位还是比较难竞争的,换言之,程序员的上升空间比较窄,如果想要拿到高级岗位,最好的是工作三五年后就转项目经理,然后再往上爬。
架构师倒是也能晋升高级岗位,但就效率而言,是非常低的。就我的经验而言,架构师系的高级职位通常是技术管理一手抓,但这也代表着,责任更大,也就是锅更大。
所以架构师系的高级职是非常不稳定的,非常累,非常容易坚持不下来,尤其在你结婚生子后,就更难坚持下来。
所以,选择大于努力,如果想拼命向上走,还是尽快入坑项目管理才是上策,不想拼命或者做不到拼命的,那就无所谓选择了。
就算是同样的架构师,java系架构师也相对更好,生态好意味着java系架构师就相对学的不用那么多,也就不用那么累。
举个简单例子,java架构师可以不会安卓和鸿蒙,但net架构师必须会winform和wpf。
所以,还是那句话,选择大于努力,能入坑java还是尽早入坑java,这样你会少走几年弯路,而且如果你躺平了,能让你35岁以后能继续用技术吃饭,还吃的不错的,就只有java的生态。
当然,java系还有细分的,能选大数据就走大数据,大数据是一个工作内容异常简单,工作量异常少的一个岗位,而且大家还觉得你工作内容很神秘,会比较有尊严。
就我的个人学习flink的经验而言,学习flink的难度只有学习java的难度的30%,但你的工资可能高于java,加班也会少于java。
下面回到正题,架构师备考。

架构师备考——记忆难点

其实所谓的记忆难点,就是我们的理解和扯淡描述之间的转换比较难。

原型模式(property)

通过copy对象属性,或者clone获得新对象,就是DTO转实体的那个对象属性映射。

要能通过下面定义分析出他在描述的是原型模式。
用原型实例指定创建对象的类型,并且通过拷贝这个原型来创建新的对象。运行对象在不了解创建对象的确切类以及如何创建细节的情况下创建自定义对
象。

Builder(生成器模式)

就是java里常见的builder创建对象,或者理解为注解@Builder。
注:其他语言没有这个模式,其他语言开发,就理解为构造函数创建对象即可。

要能通过下面定义分析出他在描述的是生成器模式。
将一个复杂类的表示与其构造相分离,使得相同的构建过程能够得出不同的表示。

Abstract Factory(抽象工厂模式):

通过不同的接口来获取不同对象。
注:抽象工厂模式与工厂模式的区别是,工厂模式是通过入参来获取不同的对象。这两种模式在其他语言中是随手写的代码,所以没有形成概念结构,这里要学会区分。

要能通过下面定义分析出他在描述的是抽象工厂模式。
提供一个创建一系列相关或相互依赖对象的接口而无需指定它们具体的类。

仓库风格架构

  • 集中存储:就是说数据都在一个数据库里。
  • 数据驱动:就是说数据一更新,查询该数据就马上得到最新数据,因为都在一个库。
  • 数据处理方式:就是存储到数据库
  • 扩展性:可以增加插件扩展功能。

管道过滤器风格

  • 这个主要是考察servlet的机制,所有的题型往servlet里靠即可。其他语言开发就把他像成一堆filter即可。这个一般情况是架构中的一个小环节,纯用管道过滤器做出来的组件还是很少见的。
  • 特点:数据流动,功能单一,组合灵活。
  • 扩展性:可以灵活增加filter。

数据驱动机制

数据驱动机制就是数据驱动的方法,有几种:数据库驱动应用,配置文件驱动应用,脚本语言驱动应用。这个有点IOC的感觉,控制翻转,反向以数据为中心驱动应用的表现。

页面变换表

这个是必背的,一定会有1 ~ 2空,即这1 ~ 2分是必拿的。
虚拟内存:虚拟内存就是物理内存和部分硬盘(辅助存储)组合起来,供cpu调用的。
虚拟页面:在虚拟内存系统中,程序所使用内存,被细分成多个内存块,这些内存块就叫虚拟页面。
页帧/物理块:就是虚拟内存对应的物理内存和硬盘的地址。
页面变换表:就是用于记录虚拟内存中的页帧/物理块之间的对应关系。

存在位(Present Bit):表示该页面是否当前在物理内存中。如果存在位为 1,表示该页面在物理内存中;如果为 0,表示该页面不在物理内存中,需要从辅助存储中调入。
访问位(Access Bit):记录该页面是否被访问过。操作系统可以根据访问位来决定哪些页面是最近使用过的,以便在需要换出页面时进行选择。
修改位(Modified Bit):表示该页面是否被修改过。如果修改位为 1,表示该页面在被调入物理内存后被修改过,在换出时需要写回辅助存储;如果为 0,表示该页面未被修改过,可以直接丢弃。
保护位(Protection Bit):用于指定该页面的访问权限,如只读、读写、可执行等。

页号 物理块号/页帧号 状态位 访问位
0 1 1 0
1 - 0 0
2 3 1 1
3 8 1 1

逻辑地址16位:3148H,第一位5表示虚拟页面地址,页内地址是148H,查表,3对应的页帧是8,所以转换物理地址为8148H。
如果访问1页,因为1页状态位0,所以是没有在内存的,所以要淘汰一个在内存的页,为这次访问提供位置,那么淘汰0页,因为0页在内存,且近期未被访问。

质量属性

质量属性的含义如下:
性能(响应时间),可用性(宕机恢复),可修改性(修改时间短),易用性,可靠性(容错、健壮性),安全性(验证逻辑,阻止非授权用户)

  • 提高性能——资源调度(就是负载均衡)
  • 提高可用性——心跳(考试里的可用性是专指——服务查询(nacos)的心跳体系,通过心跳上线下线服务)
  • 提高可修改性——信息隐藏(这个比较难记难理解,我猜测是他们认为一个表单修改的内容减少了,就提高了可修改性,这个要特殊背一下,有点反逻辑)
  • 易用性——这个就是用户使用难易度。
  • 可靠性——容错、健壮性。在意外或错误使用的情况下维持软件系统的功能特性的能力。
  • 提高安全性——追踪审计,就是记录日志和溯源。
    安全性在考试里应该是特指java的Security和Auth2,他这套逻辑还是挺难理解的,尤其是结合了java的控制反转的结构。(java现在搞的组件设计都是控制反转,而其他语言都是正向开发)
    非java系开发可以参考下面文章理解。
    其实,如果是我们正常的、手工搭框架,我认为即便是java架构师也会把Security和Auth2踢出局,因为他这个模式有问题,不过,考试里很多题都会参考这个出,非java系开发,这块就只能蒙了,确实不好理解。
    SpringBoot的Security和OAuth2的使用

构件类别

这个构件体系,就是spring的体系,因为整体是IOC,所以,就一个一个的构件往里注入。
由于我们大部分开发都是做应用开发的,而这种构件模式,虽然有,但相对还是比较少见的,非java开发可能工作20年也没这么开发过软件的可能性是很高的。
非java开发可以把这个构件模式想象为docker。java开发就比较幸福了,下面的定义一看,马上就可以联想到spring的各种组件了。

  • (1)独立而成熟的构件。独立而成熟的构件得到了实际运行环境的多次检验,该类构件隐藏了所有接口,用户只需用规定好的命令进行使用。例如,数据库管理系统和操作系统等。
  • (2)有限制的构件。有限制的构件提供了接口,指出了使用的条件和前提,这种构件在装配时,会产生资源冲突、覆盖等影响,在使用时需要加以测试。例如,各种面向对象程序设计语言中的基础类库等。
  • (3)适应性构件。适应性构件进行了包装或使用了接口技术,把不兼容性、资源冲突等进行了处理,可以直接使用。这种构件可以不加修改地使用在各种环境中。例如 ActiveX等。
  • (4)装配的构件。装配(assemble)的构件在安装时,已经装配在操作系统、数据库管理系统或信息系统不同层次上,使用胶水代码(gluecode)就可以进行连接使用。目前一些软件商提供的大多数软件产品都属这一类。
  • (5)可修改的构件。可修改的构件可以进行版本替换。如果对原构件修改错误、增加新功能,可以利用重新“包装”或写接口来实现构件的替换。这种构件在应用系统开发中使用得比较多。

中间件

这里考的中间件是特指kafka和redis和nacos,其他语言就用rabbitMQ和redis和consul思考即可。
连接与通信:考试中认为,中间件是客户端与服务器的连接与通信(连接与通信是中间件的一个属性,在考试里认为它是主要属性,我个人感觉这么定义是有点怪,但还是能给与一定理解)
交易管理机制保证交易一致性:这个应该指的是用redis构件的分布式锁。
负载均衡和高可用:这个指的是nacos,一个内部组件Load Balance(低版本的是Robbin)实现负载均衡,一个是心跳实现高可用。

关系模式 R(U,F)

这个主要是要记一些名词。
在 “R(U,F)” 中:
“R” 表示一个关系模式(Relation Schema)。
“U” 是属性集(Attributes Set),即关系模式中所有属性的集合。例如,一个学生关系模式可能有属性集 U = {学号,姓名,年龄,班级号} 等。
“F” 是函数依赖集(Set of Functional Dependencies),用于描述属性之间的约束关系。例如,F 中可能包含函数依赖 {学号}→{姓名}、{班级号}→{辅导员} 等。

  • 如给出关系R(U,F),U={A,B,C,D,E,F={A→BC,B→D,D→E}。
    则有【依赖传递】,A→B,B→D,D→E。
    根据依赖传递,可得新依赖关系,A→D,A→E
  • 如,U1={A,B,C)、U2={B,D,E},则分解p()。
    这个问题是问拆表,把U表拆成俩表,然后字段是U1={A,B,C)、U2={B,D,E},这个拆表,比较好理解,我们经常拆,题中的拆法,是正确的拆法,所以什么也不影响,所以【无损连接并保持函数依赖】。
    无损连接就是没丢字段。

数据库分布式模式

这个数据库模式是针对java系的mysql数据库设计的,在java项目中,有时候会使用现有的开源项目,这些项目大多数是微服务模式的。
然后这些开源项目,通常会把表拆到8到20个数据库中,因为一开始起步时表和库就过多,所以之后,随着开发,表和库的混乱就是指数倍的增加,库表越多操作就越复杂,所以一些平平无奇的项目,也会有一些和淘宝京东这种大系统一样的困难要去面对。
也因此,有一些概念,本来是要到特定的环境下才需要学习的,但在java系统,就变成了常用概念。

  • 全局外模式:
    兽语:是对分布式数据库的最高层的抽象。
    人言:全局外模式是一个图。就是是把分布式数据库看成集中数据库,集中数据库就是一个数据库。然后用一个图描述数据库结构。
  • 全局概念模式:
    兽语:是分布式数据库的整体抽象,包含了系统中全部数据的特性和逻辑结构描述分布数据库全局数据的逻辑结构,是分布式数据库的全局概念视图。
    人言:全局概念模式是一个图,分布式数据库的整体、详细的逻辑关系图。
  • 分片模式:
    兽语:描述全局数据逻辑划分的视图,是全局数据的逻辑结构根据条件的划分;每一个逻辑划分就是一个片段或称为分片。
    人言:不是图,他是拆分数据库,就是横向,纵向拆分,比如按字段拆,按日期拆。
  • 分配模式(分布模式):
    兽语:描述局部逻辑的局部物理结构是划分后的片段(或分片)的物理分配视图;是全局概念层的内容。
    人言:是一个图,就是记录上面的数据分片的逻辑关系图。
  • 局部概念层:
    兽语:由局部概念模式描述,是全局概念模式的子集;全局概念模式经逻辑划分后被分配在各局部场地上。
    人言:局部概念层是个图,分布式数据库会有多个库,这个局部概念图就是节点库的逻辑描述图。


注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!



若您觉得这篇文章还不错,请点击下方的【推荐】,非常感谢!

https://www.cnblogs.com/kiba/p/18404588