0%

访问者模式简介

访问者模式是一种行为模式。

访问者模式是一种将数据操作和数据结构分离的设计模式。(觉得太抽象,可以看下面的例子)。

使用场景

  1. 对象结构比较稳定,但经常需要在此对象结构上定义新的操作。
  2. 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。

优缺点

优点

  1. 各角色职责分离,符合单一职责原则
    通过后面UML类图和示例可以看出来,Visitor、ConcreteVisitor、Element 、ObjectStructure,职责单一,各司其责。
  2. 具有优秀的扩展性
    如果需要增加新的访问者,增加实现类 ConcreteVisitor 就可以快速扩展。
  3. 使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化
    城市经典(数据结构)和跟团游游客、自驾游游客访问者(数据操作)的解耦。
  4. 灵活性

缺点

  1. 具体元素对访问者公布细节,违反了迪米特原则
    团游游客、自驾游游客需要调用具体城市的方法。
  2. 具体元素变更时导致修改成本大
    变更城市属性时,多个访问者都要修改,比如雷峰塔倒了。
  3. 违反了依赖倒置原则,为了达到“区别对待”而依赖了具体类,没有以来抽象
    访问者 visit 方法中,依赖了具体城市的具体方法。

通用模板

UML 图

角色介绍

  • Visitor:接口或者抽象类,定义了对每个 Element 访问的行为,它的参数就是被访问的元素,它的方法个数理论上与元素的个数是一样的,因此,访问者模式要求元素的类型要稳定,如果经常添加、移除元素类,必然会导致频繁地修改 Visitor 接口,如果出现这种情况,则说明不适合使用访问者模式。
  • ConcreteVisitor:具体的访问者,它需要给出对每一个元素类访问时所产生的具体行为。
    Element:元素接口或者抽象类,它定义了一个接受访问者(accept)的方法,其意义是指每一个元素都要可以被访问者访问。
  • ElementA、ElementB:具体的元素类,它提供接受访问的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
  • ObjectStructure:定义当中所提到的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素提供访问者访问。
  • client: 具体的运行代码类

通用代码

Element接口

1
2
3
4
5
6
7
8
/**
* 元素接口或者抽象类,它定义了一个接受访问者(accept)的方法,其意义是指每一个元素都要可以被访问者访问。
* */
public interface Element {

public void accept(Visitor visitor);

}

Element Concrete 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ElementA implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}

public void operation1(){
System.out.println("ElementA do operation1");
}
}

public class ElementB implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}

public void operation2(){
System.out.println("ElementB do operation2");
}
}

visitor接口

1
2
3
4
5
6
7
8
9
10
/**
* 定义了对每个 Element 访问的行为
* */
public interface Visitor {

public void visit(ElementA element1);

public void visit(ElementB element2);

}

visitor concrete

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
public class VisitorA implements Visitor {
@Override
public void visit(ElementA elementA) {
System.out.println("Visitor A start visit ElementA");
elementA.operation1();
}

@Override
public void visit(ElementB elementB) {
System.out.println("Visitor A start visit ElementB");
elementB.operation2();
}
}

public class VisitorB implements Visitor {
@Override
public void visit(ElementA elementA) {
System.out.println("Visitor B start visit ElementA");
elementA.operation1();
}

@Override
public void visit(ElementB elementB) {
System.out.println("Visitor B start visit ElementB");
elementB.operation2();
}
}

ObjectStructure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ObjectStructure {

private List<Element> elementList;

public ObjectStructure(){
elementList=new ArrayList<Element>();
elementList.add(new ElementA());
elementList.add(new ElementB());
}

public void accept(Visitor visitor){
for(Element element:elementList){
element.accept(visitor);
}
}

public void addElement(Element element){
elementList.add(element);
}

public void removeElement(Element element){
elementList.remove(element);
}
}

最后client调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Client {

private Visitor visitor;

private ObjectStructure objectStructure;

public void doVisit(){
ObjectStructure objectStructure=new ObjectStructure();
objectStructure.accept(new VisitorA());
objectStructure.accept(new VisitorB());
}

public static void main(String[] args){
Client client=new Client();
client.doVisit();
}
}

执行结果

1
2
3
4
5
6
7
8
Visitor A start visit ElementA
ElementA do operation1
Visitor A start visit ElementB
ElementB do operation2
Visitor B start visit ElementA
ElementA do operation1
Visitor B start visit ElementB
ElementB do operation2

依然很抽象,看下面的例子吧。

旅游城市例子

访问者模式,用游客访问城市经典的例子再合适不过了.

不同的游客有不同的访问行为,跟团游和自驾游或休闲游在游览景点是的行为也是完全不相同的,完美符合访问者模式中的访问者这一对象。

UML 图

角色介绍

  • City
    城市充当Element角色,各城市属性非常稳定,但是城市所属的景点可能发生轻微改造或变更,完美符合Element角色的要求。
  • HangzhouCity,HuangshanCity,BeijingCity
    三个城市的具体实现:杭州、黄山、北京,对应的是ElementConcrete
  • Tourists
    游客接口,游客可以访问城市,对应访问者模式中的Visitor角色。
  • WithTheGroupTourists,SelfDriverTourists
    游客的两个具体实现,跟团游游客和自驾游游客,对应VisitorConcrete
  • TravelPlan
    旅游计划抽象,封装了一堆城市的集合。
    对应ObjectStructure
  • TravelClient
    具体的一次旅游,对应 Client

实例代码

City

1
2
3
4
5
6
7
8
/**
* 城市,对应访问者模式中的Element元素,它定义了一个接受访问者(accept)的方法,其意义是指每一个元素都要可以被访问者访问。
* */
public interface City {

public void accept(Tourists visitor);

}

CityConcrete

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
* 北京,city的具体实现
* * 对应visitor中的ElementConcrete
* */
public class BeijingCity implements City {
@Override
public void accept(Tourists tourists) {
tourists.visitBeijing(this);
}

/**
* 旅游长城
* */
public String greatWall(){
return "长城";
}

/**
* 旅游故宫
* */
public String forbiddenCity(){
return "故宫";
}

/**
* 后海酒吧
* */
public String houhaiBar(){
return "后海酒吧";
}
}

/**
* 杭州,city的具体实现
* 对应visitor中的ElementConcrete
* */
public class HangzhouCity implements City {
@Override
public void accept(Tourists tourists) {
tourists.visitHangzhou(this);
}

/**
* 西湖
* */
public String westLake(){
return "西湖";
}

/**
* 浙江大学
* */
public String zhejiangUniversity(){
return "浙江大学";
}

/**
* 雷峰塔
* */
public String leifengPagoda(){
return "雷峰塔";
}
}

/**
* 黄山,city的具体实现
* 对应visitor中的ElementConcrete
* */
public class HuangshanCity implements City {
@Override
public void accept(Tourists tourists) {
tourists.visitHuangshan(this);
}

/**
* 黄山
* */
public String huangshan(){
return "黄山";
}
}

Tourists

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

/**
* 游客,对应访问者模式中的Visitor
* 定义了对每个 Element 访问的行为
* */
public interface Tourists {

/**
* 访问北京
* */
public void visitBeijing(BeijingCity beijingCity);

/**
* 访问杭州
* */
public void visitHangzhou(HangzhouCity hangzhouCity);

/**
* 访问黄山
* */
public void visitHuangshan(HuangshanCity huangshanCity);

}

TouristsConcrete

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/**
* 自驾游,Tourists实现,对应访问者模式中VisitorConcrete
* */
public class SelfDriveTourists implements Tourists {

@Override
public void visitBeijing(BeijingCity beijingCity) {
System.out.println(">>>>>开始自驾旅游北京");
System.out.println("第1站:"+beijingCity.greatWall());
System.out.println("第2站:"+beijingCity.forbiddenCity());
System.out.println("第3站:"+beijingCity.houhaiBar());
System.out.println(">>>>>结束自驾旅游北京");

}

@Override
public void visitHangzhou(HangzhouCity hangzhouCity) {
System.out.println(">>>>>开始自驾旅游杭州");
System.out.println("第1站:"+hangzhouCity.westLake());
System.out.println("第2站:"+hangzhouCity.leifengPagoda());
System.out.println("第3站:"+hangzhouCity.zhejiangUniversity());
System.out.println(">>>>>结束自驾旅游杭州");
}

@Override
public void visitHuangshan(HuangshanCity huangshanCity) {
System.out.println(">>>>>开始自驾旅游黄山");
System.out.println("第1站:"+huangshanCity.huangshan());
System.out.println(">>>>>结束自驾旅游黄山");
}
}

/**
* 跟团游,Tourists的实现
* 对应访问者模式中VisitorConcrete
* */
public class WithTheGroupTourists implements Tourists {
@Override
public void visitBeijing(BeijingCity beijingCity) {
System.out.println(">>>>>开始跟团旅游北京");
System.out.println("第1站:"+beijingCity.houhaiBar());
System.out.println("第2站:"+beijingCity.forbiddenCity());
System.out.println("跟团游时间太紧,无法旅游:"+beijingCity.greatWall());
System.out.println(">>>>>结束跟团旅游北京");

}

@Override
public void visitHangzhou(HangzhouCity hangzhouCity) {
System.out.println(">>>>>开始跟团旅游杭州");
System.out.println("第1站:"+hangzhouCity.westLake());
System.out.println("第2站:"+hangzhouCity.zhejiangUniversity());
System.out.println("跟团游时间太紧,无法旅游:"+hangzhouCity.leifengPagoda());
System.out.println(">>>>>结束跟团旅游杭州");
}

@Override
public void visitHuangshan(HuangshanCity huangshanCity) {
System.out.println(">>>>>开始跟团旅游黄山");
System.out.println("第1站:"+huangshanCity.huangshan());
System.out.println(">>>>>结束跟团旅游黄山");
}
}

TravelPlan

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
/**
* 旅游计划
* 对应访问者模式中的ObjectStructure
* */
public class TravelPlan {

private List<City> cityList;

public TravelPlan(){
cityList=new ArrayList<City>();
cityList.add(new BeijingCity());
cityList.add(new HangzhouCity());
cityList.add(new HuangshanCity());

}

public void travel(Tourists visitor){
for(City city:cityList){
city.accept(visitor);
}
}

public void addCity(City city){
cityList.add(city);
}

public void removeCity(City city){
cityList.remove(city);
}
}

TravelClient

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
/**
* 旅游记录
* 对应访问者模式中的Client
* */
public class TravelClient {

private Tourists visitor;

private TravelPlan travelPlan;

public void doTravel(){
TravelPlan travelPlan=new TravelPlan();
//跟团游
travelPlan.travel(new WithTheGroupTourists());
System.out.println();
System.out.println();

//自驾游
travelPlan.travel(new SelfDriveTourists());
}

public static void main(String[] args){
TravelClient travelClient=new TravelClient();
travelClient.doTravel();
}
}

执行结果

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
>>>>>开始跟团旅游北京
第1站:后海酒吧
第2站:故宫
跟团游时间太紧,无法旅游:长城
>>>>>结束跟团旅游北京
>>>>>开始跟团旅游杭州
第1站:西湖
第2站:浙江大学
跟团游时间太紧,无法旅游:雷峰塔
>>>>>结束跟团旅游杭州
>>>>>开始跟团旅游黄山
第1站:黄山
>>>>>结束跟团旅游黄山


>>>>>开始自驾旅游北京
第1站:长城
第2站:故宫
第3站:后海酒吧
>>>>>结束自驾旅游北京
>>>>>开始自驾旅游杭州
第1站:西湖
第2站:雷峰塔
第3站:浙江大学
>>>>>结束自驾旅游杭州
>>>>>开始自驾旅游黄山
第1站:黄山
>>>>>结束自驾旅游黄山

代码

以上例子代码可在个人github项目design-patterns中找到。

参考文章

访问者模式一篇就够了

背景

使用idea阅读spark 源码时,如果是第一次导入,会碰到 org.apache.spark.sql.catalyst.parser.SqlBaseParser包下相关的类无法找到的问题。
AstBuilder 继承 SqlBaseBaseVisitor,SqlBaseBaseVisitor就无法找到。

原因

org.apache.spark.sql.catalyst.parser.SqlBaseParser包下相关的代码并不是人工写的源码,而是antlr4框架自动生成的代码,因为代码没有生成,所以idea无法找到相关代码。

antlr4是什么?

  • antlr github 地址
  • antlr 官网 地址

    ANTLR (ANother Tool for Language Recognition) is a powerful parser generator for reading, processing, executing, or translating structured text or binary files. It’s widely used to build languages, tools, and frameworks. From a grammar, ANTLR generates a parser that can build parse trees and also generates a listener interface (or visitor) that makes it easy to respond to the recognition of phrases of interest.

ANTLR(另一种语言识别工具)是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件。 它广泛用于构建语言、工具和框架。 从语法中,ANTLR 生成一个可以构建解析树的解析器,还生成一个侦听器接口(或访问者),可以轻松响应对感兴趣短语的识别。

简单来说就是定义语言此法和语法的框架,比如sql的解析。

对应的同类产品有:

  • 在 C/C++ 开发环境下比较有名的 Flex、 Yacc 等;
  • 在 Java 的世 界里,词法分析工具有 JFlex,语法分析工具有 BYACC/J、 Java Cup,兼具两者的有 JavaCC 和 ANTLR等

spark使用antlr4干什么?

spark使用antlr4来解析sql语法,生成语法树,之后应用语法树生成相关逻辑树及最后的物理执行计划。

如果你对sparksql 采用antlr4 解析比较感兴趣,这里推荐
ANTLR4-SqlBase这个开源项目,该作者为 《Spakr SQL内核剖析》 一书作者剥离的Parser模块,用于查看Spark SQL语法解析SQL后生成的语法树的项目。

解决方法

使用antlr4插件生成代码,并将相关代码引入到classpath中。

1、安装antlr4插件

本人mac系统
在 intellij Idea->Preference->Plugins
下搜索 antlr4,安装插件

2、使用antlr4插件生成代码

2.1 使用antlr4语法文件SqlBase.g4生成对应的代码

找到SqlBase.g4文件,该文件在sql/catalyst/src/main/antlr4/org.apache.spark.sql.catalyst.parser下

右键,找到antlr4插件的Generate ANTLR Recognizer选项,点击
如下图所示

会在 sql/catalyst 下创建gen文件夹,生成对应的代码,如果想修改目录,可以点击configure ANTLR修改,这里直接默认的就好

生成的代码结构如下图

2.2 将相关代码导入 classpath

选中再上一步生成的gen文件,右键,找到Mark Directory as -> Sources Root,将该文件夹设置为代码跟路径,这样idea就会将该文件夹添加到classpath中并编译。

再次查看AstBuilder代码,此时红色错误已消失,代码已找到

至此,相关问题已解决。

前言

不知大家是否有这样一些场景:

手头有一些很重要的照片,充满回忆,绝对不能丢失,存到电脑里害怕有一天电脑坏掉导致照片丢失,对丢失数据的恐惧是整天困扰你。

手动把文件存到多台电脑又太麻烦。

数据同步就是解决这种烦恼的答案。本文提供了10个最佳软件选项,你可以从中选择以最小的麻烦创建文件和文件夹的备份。希望本文能帮助到你。

本人推荐Syncthing方案

最强推荐 Syncthing


最好的备份和文件同步软件下载:Syncthing ,它允许在两台或多台计算机之间实时同步文件。它涉及连续的文件同步。Syncthing 提供完全的隐私保护,让你安全地同步文件。你可以选择要存储数据的位置,或者是否要与第三方共享。你还可以决定你的数据在互联网上的传输方式。它使用 TLS 加密为你的文件提供私人存储以保密。

特征:

  • 你可以在自己的设备之间或与使用不同设备的不同人员同步任意数量的文件夹。所以,它很强大
  • Syncthing 允许使用可通过浏览器访问的强大且响应迅速的界面进行简单的配置和监控。在台式计算机上运行 Syncthing 并允许备份后,你可以将文件夹与服务器同步。因此,它是便携式的
  • 当你使用 Syncthing 时,每台机器都有一个单独的 ID。你可以通过简单地共享 ID 与你的朋友共享文件夹。这是可能的,因为 Syncthing 不需要 IP 地址或高级配置,因为它可以在 LAN 和 Internet 上运行。这使它易于使用
    官网
    文件大小: 48.5 MB
    系统支持: Linux、OS X、Windows、Android、BSD、Solaris

GoodSync

GoodSync由 Siber Systems 开发的 ,是一个帮助在两个目录之间创建备份和同步文件的程序。GoodSync 让你可以在多个计算设备上存储和保存相同版本的文件。GoodSync 确保当你在任何单个设备上修改文件时,更改也会在其他设备上同时进行。因此,从哪个设备访问文件并不重要。它还允许你在远程计算机或服务器与你的计算机之间同步文件。

GoodSync 的用途和好处
Goodsync 是一个非常流行的备份和文件同步程序,提供以下好处-

  • AES 256 位加密: 大大提高了关键数据的安全性。
  • 你的数据是 实时 传输的,因此不需要用户交互,因为备份和同步是自动和计划的。
  • 由于你的数据在块级别传播,因此备份时间、网络消耗和存储要求非常低。
  • 版本历史控制保证了最短的数据恢复时间和最高的安全性。

SyncToy


SyncToy 是 PowerToys 系列中的 Microsoft 产品。在使用 Microsoft Sync Framework 时,已使用 .NET 框架编写它。使用它时要遵循的方法是你必须创建一个左侧文件夹,即“源”文件夹。下一步是创建将作为目标的正确文件夹。

你可以选择任何文件夹作为你的源或目标文件夹,这使得产品非常用户友好。你可以自由选择 USB 闪存驱动器、网络驱动器或任何便携式硬盘驱动器上的任何文件夹。你不必只使用内部硬盘驱动器。

特征:
新的 SyncToy 2.1 带有一些改进的功能,如下所示:

  • 现在文件复制速度更快,确保更好的性能
  • 它具有更好的错误报告功能。它清楚地显示了在致命错误期间无法同步哪个文件。新版本对文件系统和瞬态网络错误的抵抗力更强
  • 文件夹对配置的自动备份。这使你可以使用备份副本替换 SyncToyDirPairs.bin。这可以帮助你解决上次保存的配置
    官网
    文件大小: 2.9 MB
    系统支持: Windows

FreeFileSync


FreeFileSync 与 Windows、macOS 和 Linux 兼容,是开源软件。你可以使用此软件创建和管理所有重要文件的备份副本。FreeFileSync 在同步文件时采用了一种聪明的方法。它能够发现源文件夹和目标文件夹之间的差异。这样,FreeFileSync 只传输最少的数据,而忽略目标文件夹中的相同数据。

特征:

有一些功能使 FreeFileSync 与 Goodsync 不同:

  • 同步结果会在你的电子邮件中通知
  • 你可以同步任意数量的文件,因为没有人为限制
  • 你可以使用 SFTP 在线访问文件
  • 你还可以使用 MTP 使用移动设备同步文件
  • 你还可以同步 Google Drive 上的文件
    官网
    文件大小: 14.7 MB
    系统支持: Windows、Linux、macOS

Duplicati


你可以使用 Duplicati 将文件存储在在线云服务上。作为备份客户端,Duplici 通过完全加密确保安全备份。备份经过加密和压缩并存储在云存储和远程文件服务器上。你的备份始终使用内置调度程序定期更新。Duplicati 也使用 AES-256 加密。

特征:

  • 你的文件和文件夹备份带有强大的 AES-256 加密。重复数据删除和增量备份可帮助你节省空间。使用内置调度程序和自动更新程序,你可以更新备份。基于 Web 的界面可帮助你在任何机器上运行备份。
  • Duplicati 甚至可以免费用于商业目的,因为它是开源的免费软件。
    强大的 GPG 或 AES-256 加密保护你的隐私
  • Duplicati 可以很好地处理网络问题。Duplicati 对备份进行定期测试,所有中断的备份都会恢复。损坏的存储系统损坏了备份。Duplicati 的这一功能有助于及时检测
  • Duplicati 可以在网络附加存储 (NAS) 上运行。它可以在任何浏览器上运行,甚至可以在手机上运行,​​因此可以从任何地方访问
    官网
    文件大小: 18.4 MB
    系统支持: Windows、macOS、Linux

Rsync


Rsync 涉及最少的数据复制,仅复制文件中已使用算法更改的那部分。它适用于远程同步,涉及远程和本地以及 Linux 和 Unix 系统上的文件的复制和同步。Rsync 今天被广泛用作一种已大大改进的复制命令。它作为备份和镜像软件非常有用。

特征:

  • 你不需要任何超级用户权限
  • 提供匿名或经过身份验证的 rsync 守护进程,非常适合镜像
  • Rsync 可以与任何透明的远程 shell 一起使用,包括 ssh 或 rsh
  • Rsync 带有 exclude 和 exclude-from 选项,使其与 GNU tar 相似
  • 延迟成本最低,因为它通过流水线进行文件传输
    官网
    系统支持: Linux和Unix

Rclone


作为一个命令行程序,Rclone 可让你在 Amazon S3、Dropbox、Google Cloud Storage 等各种平台之间同步文件和目录。Rclone 作为单个二进制文件出现,是一个 Go 程序。它是免费提供的开源软件。

特征:

  • 你的目录可以与同步模式相同
  • 检查模式可帮助你检查所有 MD5SUM
  • 能够与网络同步,例如两个独立的云端硬盘帐户
  • 你只能使用复制模式复制新的或更改的文件
  • 部分同步支持整个文件
  • 文件保留了时间戳
    官网
    文件大小: 11.2 MB
    系统支持: Windows、macOS、Linux、NetBSD、OpenBSD、Plan9、Solaris

PureSync


PureSync 由 Jumping Bytes 推出。这是一个 Windows 程序,可帮助同步文件和文件夹,同时在后台执行自动备份。它也可以在网络卷、外部硬盘驱动器和数码相机上工作。当你将数码相机中的照片导入计算机时,PureSync 会派上用场。

特征:

  • 并排视图比较
  • 自动进行同步和备份
  • 带有许多选项,但仍然是用户友好的
  • 仅用于个人用途时免费
  • 提供FTP 支持以及 NAS 支持
    你也可以考虑像 Clonezilla这样可以轻松备份 Windows PC 的软件。

官网
文件大小: 10.5 MB
系统支持: Windows

Allway Sync


Allway Sync 的工作效率非常高,允许你在所有设备上同步文件的多个副本,无论是 PC、笔记本电脑还是任何可移动驱动器或更多设备。采用真正的双向同步,Always Sync 的算法可以检测你在文件中所做的最新更改,无论是在哪个设备上。通过将数据记录在本地数据库中来确保隐私。它可以免费供个人使用,但有限制。它具有主要功能,例如-

  • 帮助你同步所有设备
  • 这是一个免费软件
  • 它有一个简单的用户界面
  • 它可以防止数据丢失
  • 它会生成一份关于同步文件夹之间差异的报告
    官网
    文件大小:9.5 MB
    系统支持:Windows 2000 及更高版本

odrive


使用 odrive,你可以像使用本地存储的文件一样使用云文件。如果你要对任何文件进行任何更改,这些更改将在云端自动更新。换句话说,在本地文件中所做的更改会自动与云文件同步。

特征:

  • 自动同步文件和文件夹
  • 对于服务器环境,有一个命令行界面
  • Odrive 可用作 Windows、Linux 和 Mac 的无头同步代理
  • 允许将所有文件和文件夹同步到计算机上的单个驱动器文件夹中
  • 确保桌面和云之间的无缝同步
    官网
    系统支持: 所有操作系统

SyncBack


SyncBack 仅备份基于文件的数据。简而言之,它只备份你创建的文件。但是,你不能克隆包含系统文件的整个驱动器。SyncBack free 是一个 32 位程序,可以使用 2GB 的 RAM。

特征:

  • 它提供对外部和内部存储的支持
  • 支持FTP协议
  • 它提供与网络 PC 或设备之间的备份同步
  • 它提供备份、镜像、基本同步和恢复
    官网
    文件大小: 29.9 MB
    系统支持: Windows

OpenDrive

OpenDrive 为个人、企业和企业使用提供云存储、备份和同步功能。凭借其对各种操作系统的支持,你可以轻松访问、管理和共享你的数据,而无需担心。

特征:

  • 毫不费力地将你的任何文件链接或嵌入云中,以便在电子邮件、网站、消息或社交网站中使用。
  • 高度安全的云存储,具有即时访问、共享和协作功能,界面简单易用。
  • 在任何地方(无论是计算机还是外部存储设备)永久或临时同步文件。
  • 将用户添加到你的 OpenDrive 帐户,让他们直接访问文件。
    官方网站

文件大小: 11 MB

支持的系统: Windows、Mac、Linux、Android、Web、iOS

DirSync Pro

DirSync Pro是一款免费且功能强大的文件和文件夹同步实用程序。DirSync 支持一个或多个文件夹内容的递归同步。此应用程序的 UI 易于使用,同时为你提供大量选项和功能。DirSync Pro 是用 Java 编写的,这就是它快速且健壮的原因。

特征-

  • 你可以通过进行增量备份来节省时间。
  • DirSync Pro 提供镜像、备份、贡献等预配置模式。
  • DirSync Pro 让你轻松安排同步任务。
  • DirSync Pro 完全免费,没有广告和商业文字。你可以不受功能限制地使用它。
    官方网站

文件大小: 2.8 MB

支持的系统: Windows、Mac OS、Linux、DOS

前言

日常生活中大家都离不开电脑
所以如何自动定时开关电脑大家一定用得上

自动开机

自动开机需要在 BIOS 里面设置
开机的时候按住 F2 或者 DEL 进入主板设置
然后选择电源选项
假如需要每天都定时开机,就选择Every Day,
如果想要在每天 6:45 开机,
就通过数字键输入06:15:00,
最后,一般按F10 进行保存,重启电脑后生效。
一般的老旧主板就是这种样子的

比较新的笔记本或者电脑主板就会有这样的图形化中文界面

自动开机已经设置好了

自动关机

这个可以提供计划任务来实现
控制面板上面搜索计划任务就可以看到
创建新的 触发器
根据需要 改为 设置 任务开始时间及每隔多久
如下图


再把 启动程序 选择自动执行的带参数的关机命令
如下图

这样就实现了 windows 定时自动开机关机功能

问题

在下载软件包时,linux软件包经常会有

  • linux_386.tar.gz
  • linux_amd64.tar.gz
  • linux_arm.tar.gz
  • linux_arm64.tar.gz
  • linux_mips.tar.gz
  • linux_mips64.tar.gz
  • linux_mips64le.tar.gz
  • linux_mipsle.tar.gz
  • linux_riscv64.tar.gz
    等选项,作为硬件一窍不通的人了,看了这么多选项两眼发黑,到底选哪个版本才是符合自己服务器的呢?

基础知识

在了解上面选项之前,我们需要先了解下 CISC 和 RISC ,这是所有分类的前置分类。

指令集架构

每一种处理器都有自己可以识别的一整套指令,称为 【指令集】
处理器执行指令时,根据不同的指令采取不同的动作,完成不同的功能,既可以改变自己内部的工作状态,也能控制其它外围电路的工作状态。

处理器 【指令集】 只是定义了硬件与软件(含OS)之间的接口、标准规范,而不定义处理器的实现

一台计算机上全部指令的集合,就是这台计算机的 指令系统

指令系统 也称 指令集,是这台计算机全部功能的体现。

而人们设计计算机首要考虑的是它拥有的功能,也就是首先要按功能档次设计指令集;然后,按指令集的要求在硬件上实现。
指令系统 不仅仅是指令的集合,还包括全部指令的指令格式、寻址方式和数据形式。

CISC

CISC(Complex Instruction Set Computer, 复杂指令系统计算机),除了RISC,任何全指令集计算机都使用的是复杂指令集计算(CISC)。

常见使用CISC的处理器主要有X86.

从计算机诞生以来,人们一直沿用CISC指令集方式。
早期的桌面软件是按CISC设计的,并一直沿续到现在。目前,桌面计算机流行的x86体系结构即使用CISC。
微处理器(CPU)厂商一直在走CISC的发展道路,包括Intel、AMD,还有其他一些现在已经更名的厂商,如TI(德州仪器)、IBM以及VIA(威盛)等。
在CISC微处理器中,程序的各条指令是按顺序串行执行的,每条指令中的各个操作也是按顺序串行执行的。
顺序执行的优点是控制简单,但计算机各部分的利用率不高,执行速度慢。
CISC架构的服务器主要以IA-32架构(Intel Architecture,英特尔架构)为主,而且多数为中低档服务器所采用。

RISC

RISC(reduced instruction set computer,精简指令集计算机)是一种执行较少类型计算机指令的微处理器。

纽约约克镇IBM研究中心的John Cocke证明,计算机中约20%的指令承担了80%的工作,他于1974年提出了RISC的概念。

目前常见使用RISC的处理器包括DEC Alpha、ARC、ARM、MIPS、PowerPC、SPARC和SuperH等。

RISC是一种执行较少类型计算机指令的微处理器,起源于80年代的MIPS主机(即RISC机),RISC机中采用的微处理器统称RISC处理器。
这样一来,它能够以更快的速度执行操作(每秒执行更多百万条指令,即MIPS)。因为计算机执行每个指令类型都需要额外的晶体管和电路元件,计算机指令集越大就会使微处理器更复杂,执行操作也会更慢。

CISC 指令集有哪些?

指令集架构 发源于(时间、干系人) 具体示例 微处理器
X86 指令集(架构) 创始者: Intel公司 X86-32 / X86-64(即 AMD64 / Intel64) 1. Intel i8086 CPU处理器(首台X86 CPU)
2. VIA CPU处理器
3. Intel Core I3/I5/I7/I9 CPU处理器
4. AMD CPU处理器

RISC 指令集 有哪些?

指令集架构 发源于(时间、干系人) 具体示例 微处理器
MIPS(Million Instructions Per Second) 指令集架构 1.创立于1998年;
2.创始者: 斯坦福大学教授John LeRoy Hennessy(RISC架构的开拓者)
MIPS I/II/III/IV/V/16/32/64 龙芯芯片
ARM(Acorn/Advanced RISC Machine) 指令集架构 1. 创立于1985年;
2. 创始者:Acorn公司、加州大学伯克利分校Sophie Wilson(指令集开发)和Steve Furber(芯片设计)2位教授(2家单位合作推出ARM1芯片,以此作为其未来个人计算机的中枢)
ARMv7a架构: ARM Cortex A8/A9
ARMv7M架构:ARM Cortex A3/A4
ARMv8架构(支持32+64位架构):ARM Cortex A32/35/53/57/72/73/77/78
1. 高通骁龙Qualcomm 810处理器
2.海思麒麟950处理器
3. 三星Exynos 4412处理器
4.Apple A系列处理器、Apple A6/A6X CPU处理器(基于Apple Swift 微架构[ARM指令集扩展的微架构])
AArch64/ARM64(ARMv8A架构的2种执行状态/执行模式之一,64位指令集架构) 1. 概念于2011年在美国加州圣克拉拉的一场技术大会“ARM TechCon 2011”的ARM公司院士、首席架构师Richard Grisenthwaite发布 .. ..
AArch32 指令集(ARMv8A架构的2种执行状态/执行模式之一,64位指令集架构) 同上 同上 同上
Power PC(Performance Optimization With Enhanced RISC – Performance Computing, 简称:PPC) 指令集 1.POWER是1991年,Apple、IBM、Motorola组成的AIM联盟所发展出的微处理器架构;
2.PowerPC是整个AIM联盟平台的一部分,并且是到目前为止唯一的一部分;
3.但苹果电脑自2005年起,将旗下电脑产品转用Intel CPU;
4.该设计是从早期的RISC架构(比如IBM 801)与MIPS架构的处理器得到灵感的
(略) 任天堂Gamecube、MPC505/821/850/860/8240/8245/8260/8560
SPARC(Scalable Processor Architecture可伸缩的处理器架构) 指令集 1987年,SUN公司与TI合作的成果 (略) ERC 32(机构: 欧洲航天航空局)
RISC-V / RISC-FIVE 指令集(Reduced Instruction Set Computer-V, 第5代精简指令集计算机) 1.创始者: Keste Asanovic教授(RISC-V基金会主席)、David Pattern教授(《量化分析》作者、RISC-V概念的提出者、Turing奖获得者)、Andrew Waterman(SiFive首席工程师、Rocket处理器作者)、Yunsup Lee(SiFive的CTO、Rocket处理器作者)
2.源于2010年,当时加州大学伯克利分校(UC Berkeley)一研究团队要设计一款CPU,为该项目选架构的时候研究团队对比了当时的ARM、MIPS、SPARC和X86等,发现:不仅这些【指令集越来越复杂】,还有很多【IP法律】问题,再加上【X86授权难以获取】,【ARM授权价格昂贵】,所以该研究团队最终决定设计一套全新的指令集。
2.于是,成立了一个四人小组,仅用了3个月的时间就完成了RISC-V指令集的开发。
3.其【目标】是:新的指令集能满足从微控制器到超级计算机等各种尺寸的处理器

如何确认下载哪个版本?

了解了上文的知识,直接在linux中查看指令集架构即可

  1. arch 命令查看

  2. file /lib/systemd/systemd查看

可以看到,笔者的服务器为x86架构,那么对应到上文的选项,应该选择 linux_amd64.tar.gz 下载。

文章参考

RISC / CISC | arm | amd | X86/i386 | aarch64

背景

本文为个人阅读Spark SQL内部剖析书籍时,记录一些名词解释,方便个人阅读理解

#SQL 转换过程

  • Relation、
    数据表
  • Filter
    过滤逻辑(age>l8)
  • Project
    列剪裁逻辑 (只涉及3列中的2列)

ROW

数据处理首先需要考虑如何表示数据 。 对于关系表来讲,通常操作的数据都是以“行”为单 位的 。 在 SparkSQL 内部实现中, InternalRow就是用来表示一行行数据的类

TreeNode

无论是逻辑计划还是物理计划,都离不开中间数据结构。在 Catalyst 中,对应的是 TreeNode体系 。 TreeNode类是 SparkSQL 中所有树结构的基类,定义了一 系列通用的集合操作和树遍历操作接口 。

Expression 体系

表达式一般指的是不需要触发执行引擎而能够直接进行计算的单元,例如加减乘除四则运 算、逻辑操作、转换操作、过滤操作等。 如果说TreeNode是“框架”,那么Expression就是“灵 魂”。

4.2 SparkSqlParser之AstBuilder


其中,比较核心的是 AstBuilder,它继承了 ANTLR4 生成的默认 SqlBaseBaseVisitor,用于生成 SQL对应的抽象语法树 AST (Unresolved LogicalPlan); SparkSqlAst- Builder 继承 AstBuilder,并在其基础上定义了 一 些 DDL 语句的访问操作,主要在 SparkSqlParser 中调用 。
当面临开发新的语法支持时,首先需要改动的是 ANTLR4 文件(在 Sq1Base.g4 中添加文法), 重新生成词法分析器( SqlBaseLexer)、语法分析器( SqlBaseParser)和访问者类( SqlBaseVisitor 接口与 SqlBaseBaseVisitor类),然后在 AstBuilder等类中添加相应的访问逻辑,最后添加执行逻 辑。

Context

在 Catalyst中, SQL 语句经过解析,生成的抽象语法树节点都以 Context结尾来命名 。

5.1 Spark SQL 逻辑计划

Spark SQL 逻辑计划在实现层面被定义为 LogicalPlan 类 。 从 SQL 语句经过 SparkSqlParser 解析生成 Unresolved LogicalPlan,到最终优化成为 Optimized LogicalPlan,这个流程主要经过 3 个阶段,如图 5.1 所示 。 这 3 个阶段分别产生 Unresolved Logica!Plan, Analyzed LogicalPlan 和 Optimized LogicalPlan,其中 OptimizedLogicalPlan传递到下一个阶段用于物理执行计划的生戚。

具体来讲,这 3 个阶段所完成的工作分别如下 。
(1)由 SparkSq!Parser 中的 AstBuilder执行节点访问,将语法树的各利I Context节点转换成
对应的 LogicalPlan 节点,从而成为 一棵未解析的逻辑算子树( Unresolved LogicalPlan),此时的
逻辑算子树是最初形态,不包含数据信息与列信息等。
(2)由 Analyzer将一系列的规则作用在 Unresolved LogicalPlan 上,对树上的节点绑定各种
数据信息,生成解析后的逻辑算子树( Analyzed LogicalPlan)。
(3)由 SparkSQL中的优化器(Optimizer)将一系列优化规则作用到上一步生成的逻辑算
子树中,在确保结果正确的前提下改写其中的低效结构,生成优化后的逻辑算子树( Optimized
Logica!Plan) 。
本章首先在 5.2 节全面介绍 Logica!Plan 所涉及的方方面面的基础知识,包括 LogicalPlan 分
类和各种操作的概述;然后,在 5.3~5.5节分别讲解 3个阶段的执行过程;最后,在 5.6节对全 章内容进行总结 。

5.2.1 QueryPlan 概述


QueryPlan的主要操作分为6个模块,分别是输入输出、 字符串、 规范化、表达式 操作、基本属性和约束。

  • 输入输出: QueryPlan 的输入输出定义了 5 个方法,其中 output 是返 回值为 Seq[Attribute] 的虚函数,具体内容由不同子节点实现,而 outputSet 是将 output 的返回值进行封装,得 到 AttributeSet集合类型的结果 。 获取输入属性的方法 inputSet 的返回值也是 AttributeSet, 节点的输入属性对应所有子节点的输出; producedAttributes 表示该节点 所产生的属性; missinglnput表示该节点表达式中涉及的但是其子节点输出中并不包含的属性 。
  • 基本属性:表示 QueryPlan节点中的一些基本信息,其中 schema对应 output输出属性的 schema信息, allAttributes记录节点所涉及的所有属性( A忧ribute)列表, aliasMap 记录节 点与子节点表达式中所有的别名信息, references 表示节点表达式中所涉及的所有属性集 合, subqueries 和 innerChildren 都默认实现该 QueryPlan 节点中包含的所有子查询 。
  • 字符串:这部分方法主要用于输出打印 QueryPlan树型结构信息,其中 schema信息也会 以树状展示。 需要注意的一个方法是 statePre缸,用来表示节点对应计划状态的前缀字符
    串。 在 QueryPlan 的默认实现中,如果该计划不可用 (invalid),则前缀会用感叹号(“!”)
    标记。
  • 规范化: 类似 Expression 中的 方法定义,对 QueryPlan 节点类型也有规范化( Canonicalize) 的概念。 在 QueryPlan 的默认实现中, canonicalized直接贼值为当前的 QueryPlan类;此 外,在 sameResult 方法中会利用 canonicalized 来判断两个 QueryPlan 的输出数据是否相同 。
  • 表达式操作:在第 3 章中已经介绍过 Spark SQL 丰富的表达式体系,其典型的特点就是不 需要驱动,直接执行 。 而在 QueryPlan 各个节点中,包含了各种表达式对象,各种逻辑操 作一般也都是通过表达式来执行的。 在QueryPlan的方法定义中,表达式相关的操作占据 重要的地位,其中 expressions方法能够得到该节点中的所有表达式列表,其他方法很容易 根据命名了解对应功能,具体的实现细节可以参看代码 。
  • 约束(Constraints):本质上也属于数据过滤条件(F过ter)的一种,同样是表达式类型。 相 对于显式的过滤条件,约束信息可以“推导”出来,例如,对于“ a > S”这样的过滤条件, 显然 a 的属性不能为 null,这样就可以对应地构造 isNotNull (a)约束;又如“ a=S”和 “a=b”的谓词,能够推导得到“b=S”的约束条件。在实际情况下, SQL语句中可能会 涉及很复杂的约束条件处理,如约束合并、 等价性判断等。 在QueryPlan类中,提供了大 量方法用于辅助生成 constraints表达式集合以支持后续优化操作 。 例如, validConstraints 方法返回该 QueryPlan 所有可用的约束条件,比较常用的 constructlsNotNullConstraints 方 法,会针对特定的列构造 isNotNull 的约束条件 。

5.4.1 Catalog 体系分析

按照 SQL 标准的解释,在 SQL 环境下 Catalog 和 Schema 都属于抽象概念 。 在关系数据库 中, Catalog是一个宽泛的概念,通常可以理解为一个容器或数据库对象命名空间中的一个层次,主要用来解决命名冲突等问题 。
在 SparkSQL 系统中, Catalog主要用于各种函数资源信息和元数据信息(数据库、数据表、
数据视图、数据分区与函数等)的统一管理。 SparkSQL 的 Catalog体系涉及多个方面,不同层 次所对应的关系如图 5.11 所示 。

5.4.2 Rule 体系

在 Unresolved LogicalPlan 逻辑算子树的操作(如绑定、解析、优化 等 )中,主要方法都是 基于规则( Rule)的,通过 Scala语言模式匹配机制( Pattern-match)进行树结构的转换或节点 改写 。 Rule是一个抽象类,子类需要复写 apply(plan: TreeType)方法来制定特定的处理逻辑,基 本定义如下 。

6.1 Spark SQL 物理计划

在 SparkSQL 中,物理计划用 SparkPlan表示,从 OptimizedLogicalPlan传入到 SparkSQL物理 计划提交并执行,主要经过 3个阶段。 如图 6.1 所示,这 3个阶段分别产生 Iterator[PhysicalPlan]、 SparkPlan 和 Prepared SparkPlan,其中 Prepared SparkPlan 可以直接提交并执行(注:这里的 “PhysicalPlan”和“ SparkPlan”均表示物理计划) 。

spark sql物理执行计划操作

  • Project进行列剪裁
  • Filter 行过滤
  • Exchange 负责对数据进行重分区,
  • SampleExec 对输入 RDD 中的数据进行采样,
  • SortExec按照一 定条件对输入 RDD 中数据进行排序,
  • WholeStageCodegenExec 类型的 SparkPlan 将生成的代码 整合成单个 Java 函数 。

聚合过程 partial & final

Final模式一般和 Partial模式组合在一起使用 。 Partial模式可以看作是局部数据的聚合,在 具体实现中, Partial 模式的聚合函数在执行时会根据读入的原始数据更新对应的聚合缓冲区, 当处理完所有的输入数据后,返回的是聚合缓冲区中的中间数据 。 而 Final模式所起到的作用 是将聚合缓冲区的数据进行合并,然后返回最终的结果 。

Complete

Complete模式和上述的 Partial/Final组合方式不一样,不进行局部聚合计算,一般来讲, Complete模式应用在不支持Partial模式的聚合函数中。

聚合函数计算过程 – 包含distinct

相比 Partial、 Final和 Complete模式, PartialMerge模式的聚合函数主要是对聚合缓冲区进 行合并,但此时仍然不是最终的结果。 ParitialMerge主要应用在 distinct语句中,如图 7.10所示。 聚合语句针对同一张表进行 sum 和 count (distinct)查询,最终的执行过程包含了 4 步聚合操 作。 第 l步按照(A,C)分组,对 sum函数进行 Partial模式聚合计算;第 2步是 PartialMerge模 式,对上一步计算之后的聚合缓冲区进行合井,但此时仍然不是最终的结果;第 3步分组的列 发生变化,再一次进行 Partial模式的 count计算;第 4步完成 Final模式的最终计算 。

窗口函数

现象

最近打包了一个spring的jar包项目,但是运行时提示如下错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Caused by: org.xml.sax.SAXParseException; lineNumber: 9; columnNumber: 75; cvc-elt.1: Cannot find the declaration of element 'beans'.
at org.apache.xerces.util.ErrorHandlerWrapper.createSAXParseException(Unknown Source)
at org.apache.xerces.util.ErrorHandlerWrapper.error(Unknown Source)
at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
at org.apache.xerces.impl.xs.XMLSchemaValidator.handleStartElement(Unknown Source)
at org.apache.xerces.impl.xs.XMLSchemaValidator.startElement(Unknown Source)
at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanStartElement(Unknown Source)
at org.apache.xerces.impl.XMLNSDocumentScannerImpl$NSContentDispatcher.scanRootElementHook(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
at org.apache.xerces.parsers.DOMParser.parse(Unknown Source)
at org.apache.xerces.jaxp.DocumentBuilderImpl.parse(Unknown Source)
at org.springframework.beans.factory.xml.DefaultDocumentLoader.loadDocument(DefaultDocumentLoa

原因

很显然,这个错误提示的意思是解析xml时,无法识别<bean>这个标签。

spring识别xml中的语法是通过头部文件中表示的xsd生命进行识别的,一个样例如下:

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

那么这一次为什么报错呢?

网上的不优雅解决方案

关于这个问题,网上有两种常见的解决方法

第一种简单有效,但是工作量大,即:把所有spring配置文件中url形式的xsd路径转换成指向本地xsd文件的classpath形式的路径,例如:classpath:org/springframework/beans/factory/xml/spring-beans-2.5.xsd

再有一种方法就是在本机搭建web服务器,按URL创建相应文件夹,放入对应xsd文件,在本机hosts文件中加入”127.0.0.1 www.springframework.org".

实际上,这两种方法都属于“歪打正着”式的方法,直正弄明白这一问题还需要从spring的XSD文件加载机制谈起。

spring的XSD文件加载机制

首先:你必须知道一点:spring在加载xsd文件时总是先试图在本地查找xsd文件(spring的jar包中已经包含了所有版本的xsd文件),如果没有找到,才会转向去URL指定的路径下载。这是非常合理的做法,并不像看上去的那样,每次都是从站点下载的。事实上,假如你的所有配置是正确定的,你的工程完全可以在断网的情况下启动而不会报上面的错误。Spring加载xsd文件的类是PluggableSchemaResolver,你可以查看一下它的源码来验证上述说法。另外,你可以在log4j.xml文件中加入:

1
2
3
<logger name="org.springframework.beans.factory.xml">
<level value="all" />
</logger>

通过日志了解spring是何加载xsd文件的。

那么剩下的问题就是为什么spring在本地没有找到需要的文件,不得不转向网站下载。

spring 为什么找不到本地xsd?

在很多spring的jar包里,在META-INF目录下都有一个spring.schemas,这是一个property文件,其内容类似于下面:

1
2
3
4
http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd
http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
....

实际上,这个文件就是spring关于xsd文件在本地存放路径的映射,spring就是通过这个文件在本地(也就是spring的jar里)查找xsd文件的。那么,查找不到的原因排除URL输入有误之外,可能就是声明的xsd文件版本在本地不存在。一般来说,新版本的spring jar包会将过去所有版本(应该是自2.0以后)的xsd打包,并在spring.schemas文件中加入了对应项,出现问题的情况往往是声明使用了一个高版本的xsd文件,如3.0,但依赖的spring的jar包却是2.5之前的版本,由于2.5版本自然不可能包含3.0的xsd文件,此时就会导致spring去站点下载目标xsd文件,如遇断网或是目标站点不可用,上述问题就发生了。

但是,在实现开发中,出现上述错误的几率并不高,最常见的导致这一问题的原因其实与使用了一个名为“assembly”的maven打包插件有关。很多项目需要将工程连同其所依赖的所有jar包打包成一个jar包,maven的assembly插件就是用来完成这个任务的。但是由于工程往往依赖很多的jar包,而被依赖的jar又会依赖其他的jar包,这样,当工程中依赖到不同的版本的spring时,在使用assembly进行打包时,只能将某一个版本jar包下的spring.schemas文件放入最终打出的jar包里,这就有可能遗漏了一些版本的xsd的本地映射,进而出现了文章开始提到的错误。如果你的项目是打成单一jar的,你可以通过检查最终生成的jar里的spring.schemas文件来确认是不是这种情况。而关于这种情况,解决的方法一般是推荐使用另外一种打包插件shade,它确实是一款比assembly更加优秀的工具,在对spring.schemas文件处理上,shade能够将所有jar里的spring.schemas文件进行合并,在最终生成的单一jar包里,spring.schemas包含了所有出现过的版本的集合!

以上就是spring加载XSD文件的机制和出现问题的原因分析。实际上,我们应该让我们工程在启动时总是加载本地的xsd文件,而不是每次去站点下载,做到这一点就需要你结合上述提及的种种情况对你的工程进行一番检查。

场景描述

python 软件包的常用命令为

1
pip install xxx

由于默认的资源站为国外站,在国内,此安装源会经常安装失败

因此需要将资源站换成靠谱的国内资源站

国内常用的pypi镜像源

阿里:https://mirrors.aliyun.com/pypi/simple
中国科学技术大学:http://pypi.mirrors.ustc.edu.cn/simple/
豆瓣:https://pypi.douban.com/simple
清华:https://pypi.tuna.tsinghua.edu.cn/simple/

单次安装时指定数据源

单次安装时指定数据源

1
pip install <包名> -i https://pypi.tuna.tsinghua.edu.cn/simple/

指定全局安装源

在unix和macos,配置文件为:$HOME/.pip/pip.conf
在windows上,配置文件为:%HOME%\pip\pip.ini

其中pip.conf或者pip.ini的文件内容为

1
2
3
4
5
[global]
timeout = 6000
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
[install]
trusted-host = pypi.tuna.tsinghua.edu.cn

转载

本文转自 windows 生产服务器下部署 Flask

场景描述

很多 python 工程师,都会用 flask 来开发、部署 Web 应用。但也有很多工程师就直接使用 app.run() 在生产环境上启动服务。

那可能会发现 Flask 用 app.run() 这种方式启动,有异常特别容易退出,而且性能很弱。

生产环境下,python 常见的 web 部署搭配是 nginx+gunicorn

但是这种搭配只适合在 Linux 环境下。关于这种搭配的配置方法网上有很多。反而在 windows 环境下该使用什么搭配来部署,网上说的比不多。而我们的项目由于某种特殊的原因就必须搭建在 windows 环境下,所以使用的部署方案是:Flask + Tornado+nginx

解决方案示例

flask 服务文件 flask_app.py

1
2
3
4
5
6
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
return "Hello World!"

安装 Tornado

1
pip install tornado

flask_app.py 目录下编写 Tornado 的 server.py 文件

1
2
3
4
5
6
7
8
from tornado.httpserver import HTTPServer
from tornado.wsgi import WSGIContainer
from flask_app import app
from tornado.ioloop import IOLoop

s = HTTPServer(WSGIContainer(app))
s.listen(8080) # 监听 8080 端口
IOLoop.current().start()

启动服务

当前目录下执行 python server.py 浏览器中访问 http://ip:8080 即可

1
python server.py

配置 nginx 反向代理

配置 nignx 反向代理,这样在浏览器中直接访问 www.test.com 即可

windows 下 nginx 的安装和使用还是很简单的,网上教程很多,这里就不多说了

1
2
3
4
5
6
7
8
9
10
11
12
13
http {   
server {
listen 80;
server_name www.test.com;
charset utf-8;

location / {
root html;
index index.html index.htm;
proxy_pass http://ip:8080;
}

}