Fork me on GitHub

西北游玩指北 置顶

仅记录生活,照片见qq相册

西北,不仅是个地理概念,更是中国人的西部情节,有机会,就去西北游玩吧。

1. 旅行tips

1. 交通

成都-兰州西 ¥200

兰州西-中川机场 ¥17*2

9:38 的飞机,11:40 到敦煌机场,¥740

机场到莫高窟,参观3小时 ¥200

鸣沙山门票¥220 骆驼¥240 敦煌打车¥90 吃饭¥70

从敦煌20:43到嘉峪关23:57, ¥100*2 住宿¥90

嘉峪关坐出租车参观¥110,到10点

从嘉峪关南10:04到张掖西11:34 ¥65.5*2,张掖西到丹霞景区,打车¥6 大巴¥60 七彩丹霞景区¥148

张掖西18:31到西宁20:25 ¥91.5*2

西宁租车¥223,住宿¥107

第二天6点开车去青海湖环游,加油¥300

西宁的饭¥80*2

西宁回兰州¥58 回成都¥240.5

2. 小件

雨伞、充电宝、采购鸡腿和面包

3. 知识

参考出租车费:沙洲市场-莫高窟22;莫高窟-鸣沙山35;鸣沙山-沙洲市场15;沙洲夜市-机场30

2. 旅途

青海湖

去过两次,记录下

西宁站–>青海湖–>茶卡盐湖–>青海湖(看是否有时间环湖)–>塔尔寺–>西宁站

ditu

路上可能稍微有点堵车,到青海湖的黑马河乡地图时间3h左右,实际可能4h,路上可以问藏民哪个地方可以直接进去(一般10元),黑马河乡到茶卡盐湖地图时间1h30min,到茶卡盐湖坐小火车到天空之境拍照

青海湖还有其他几个地方拍照比较好:达秀木拉布宰、青海湖二郎剑景区对面的停车场,往青海湖南山行驶10分钟就可以找到理想的观景点

有兴趣的话,回西宁的路上再加上塔尔寺

高速到茶卡盐湖,平均150码左右,国道到黑河马乡,在菜根香小炒吃的土火锅,非常赞,第二天看完日出从国道开回西宁,平均100码左右,公路景观非常棒,跟游戏的画面似的,梦幻般的感觉。

费用明细:

  • 高速: 大水桥65 倒淌河21 日月山4 倒淌河33 另外的15
  • 加油:200
  • 晚饭:120
  • 茶卡盐湖的门票:300(各150,包括坐小火车到天空之境),停车费10块
  • 火车票:兰州到西宁往返 116*2
  • 旅馆:75
  • 旅途中草原上骑马:100(各50)用现金,那边(路途中间)没网…
  • 租车:40(剩余油费)+279(租车)+2000(违章押金,待退还)

青海湖日出

羊群

茶卡盐湖

茶卡盐湖

航天城

敦煌

莫高窟、鸣沙山

鸣沙山

嘉峪关

古长城

长城

张掖

七彩丹霞

丹霞

宁夏

甘肃到宁夏的高速开到了180码,经历了真正的沙尘天气,还是挺有感觉的。

宁夏

兰州

兰州美食:兰州古称金城,特色的兰州牛肉面、甜醅子、三炮台,其他的如大盘鸡、烧烤都差不多。

省博里面看到很多有意思的文物,可能地处边陲,年代尚早,记录着汉民族少年阶段的探索,笔法稚嫩,但就是比后面充满匠气的文物更有精神气。

甘肃省博

麦积烟雨

石窟开凿的痕迹还很明显,不像中原的登峰造极,更让我们看到佛法西来的历史脚印

麦积山

设计模式的落地

为什么需要掌握设计模式

类比于我们的成语、行话,助力我们的大脑快速高效的沟通和研读优秀的代码

1. 六大原则

介绍

1. 单一职责原则

Single Responsibility Principle, SRP,一个类只负责一个功能领域的相应职责。也就是我们常说的“高内聚,低耦合”

2. 开闭原则

Open-Closed Principle,OCP:对扩展开放,对修改关闭

也就是尽量在不修改原有代码的情况下进行扩展

3. 里式替换原则

Liskov Substitution Principle,LSP:所有引用父类的地方必须能透明的使用其子类的对象。

在程序中尽量使用基类类型来对对象进行定义,在运行时再确定子类类型,用子类对象来替换父类对象。

算是实现开闭原则的重要方式之一,通俗的说:子类可以扩展父类的功能,但不能改变父类原有的功能

4. 依赖倒置原则

Dependency Inversion Principle,DIP:抽象不应该依赖于细节,细节应该依赖于抽象,也就是面向接口编程,而不是针对实现编程

开闭原则是目标,里式替换是基础,依赖倒置是手段。

感觉和spring的DI有点联系,后续再思考。

5. 接口隔离原则

Interface Segregation Principle,ISP:使用多个专门的接口,而不适用单一的总接口,即客户端不应该依赖那些它不需要的接口

每个接口应该承担相对独立的角色,提供定制服务,当然接口也不能太小,灵活性会变差。控制好接口的粒度。

6. 迪米特法则

Law of Demeter,LoD, 也叫最少知识原则,LeastKnowledge Principle,LKP:一个软件实体应当尽可能少的与其他实体发生相互作用

也就是解耦合,降低系统的耦合度。

分类

大致按照设计模式的应用目标分类,分为创建型、结构型和行为型

  • 创建型模式,是对对象创建过程的各种问题和解决方案的总结,包括各种工厂模式(Factory, Abstract Factory)、单例模式(Singleton)、构建者模式(Builder)、原型模式(Prototype)
  • 结构型模式,是对软件设计结构的总结,专注于类、对象继承、组合方式的实践经验。常见的有桥接模式(Bridge)、适配器模式(Adapter)、装饰器模式(Decorator)、代理模式(Proxy)、组合模式(Composite)、门面模式(Facade)、享元模式(Flyweight)等。
  • 行为型模式,是从类或者对象之间交互、职责划分等角度总结的模式,常见有策略模式(Strategy)、解释器模式(Interpreter)、命令模式(Command)、观察者模式(Observer)、迭代器模式(Iterator)、模板方法模式(Template Method)、访问者模式(Visitor)

单例模式

之前总结有,https://blog.csdn.net/wjl31802/article/details/91360815

单例模式最常见,必须掌握。spring默认就是单例模式

适配器模式

封装有缺陷的接口设计

可以看作一种补偿模式,也就是设计初期未考虑到一些接口,比如登录没考虑到第三方登录,这时候可以用。

具体类图如下
Pasted image 20240810114413

实现的效果如下
Pasted image 20240831104325

适配器(Adapter)角色,既能够支持已有功能(用户名/密码登录)​,也能够适配扩展功能(第三方账号授权登录)​,适配的扩展功能还能够复用已有的方法(register方法和login方法)

具体gitee的实现详见

设计模式-适配器模式-通过gitee登录应用系统

统一多个类的接口设计

某个功能的实现依赖多个外部系统(或者说类),敏感词过滤为例,系统可能依赖多个敏感词库,提高过滤的召回率。但是他们的接口实现都不太一样,通过适配器把接口适配为统一的接口定义,保证复用性。

Pasted image 20240831120842

替换外部依赖A为外部依赖B

Pasted image 20240831182711

兼容老版本的接口

版本升级后,部分接口被弃用,标注deprecated,但是仍然有部分项目在用,如何丝滑的过渡?

jdk给我们的demo,jdk1.0的容器遍历的类Enumeration,在JDK1.2的时候改为Iterator类,那如何兼容呢?采用适配器模式,暂时保留 Enumeration 类,并将其实现替换为直接调用 Itertor

适配不同格式的数据

不同格式的数据之间的适配,比如,把从不同征信系统拉取的不同格式的征信数据,统一为相同的格式,以方 便存储和使用。再比如,Java 中的 Arrays.asList() 也可以看作一种数据适配器,将数组类 型的数据转化为集合容器类型。

不同日志框架的适配

2023年爆出的log4j的漏洞让全国人民都知道了这个日志框架。
java的日志框架比较混乱,或者说多元,因为最开始没有和数据库连接一样制定相关的接口规范。
梳理下大致的发展:

  1. 最开始只有System.out.print这种,没有日志级别啥的
  2. 2001年log4j诞生,定义了Logger Appender Level等概念,但是同步锁导致高并发下的性能有问题
  3. sun公司jdk1.4也搞了jul包java.util.logging 但是性能和功能都一般
  4. 为了适配这两个,搞了个接口,JCLL(Jakarta Commons Logging),在ClassLoader里面找log4j,没有就用JUL,但是性能太差
  5. log4j的作者写了个slf4j接口,为了追求性能,又搞了logback,完全兼容
  6. 那log4j由于性能问题,Apache宣布2015年不再维护,就又搞了log4j2,但是它跟log4j不兼容

那问题来了,如果不同的项目,又用到logback,又用到log4j,然后呢想用slf4j,怎么整?
就考虑到适配器模式,把不同的框架接口二次封装,适配为统一的slf4j接口定义。

桥接模式

桥接模式也叫作桥梁模式(Bridge Pattern),该模式旨在将抽象和实现解耦

Pasted image 20240810164618

还是以三方登录为例子,核心逻辑的实现需要在“右路Implementor的结构体系中

要实现什么功能?

  1. 实现三方登录
  2. 维持现有的注册登录

implementor的实现

1
2
3
4
5
6
public interface RegisterLoginFuncInterface {  
public String login(String account,String password);
public String register(UserInfo userInfo);
public boolean checkUserExist(String userName);
public String login3rd(HttpServletRequest request);
}

然后就是具体的实现,这里面就有问题了,比如RegisterLoginByDefault是否要实现login3rd?RegisterLoginByGitee是否要实现login? 通过抽象层解决

abstract的实现

需要提供给client这些方法:login抽象方法、register抽象方法、checkUserExists抽象方法和第三方账号登录的login3rd抽象方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public abstract class AbstractRegisterLoginComponent {  
// 桥梁
protected RegisterLoginFuncInterface funcInterface;
public AbstractRegisterLoginComponent(RegisterLoginFuncInterface funcInterface) {
validate(funcInterface);
this.funcInterface = funcInterface;
}

protected final void validate(RegisterLoginFuncInterface funcInterface) {
if (!(funcInterface instanceof RegisterLoginFuncInterface)) {
throw new UnsupportedOperationException("unknown register/login function type!");
}
}

public abstract String login(String username, String password);
public abstract String register(UserInfo userInfo);
public abstract boolean checkUserExists(String userName);
public abstract String login3rd(HttpServletRequest request);

}

用“抽象和实现”两种类结构的设计换来了代码的高扩展性,换来了核心实现对Client端的“最少知识”原则,换来了耦合度的降低,就好比我们用空间换来了执行速度。

继续左路突破,RegisterLoginComponent子类

搭建桥梁

通过在AbstractRegisterLoginComponent抽象类中关联RegisterLoginFuncInterface接口,并以“构造函数”的形式,初始化RegisterLoginFuncInterface接口属性,完成抽象与实现的桥梁搭建。

Pasted image 20240907184925

调用

Controller层→Service层→桥接模式的左路的抽象AbstractRegisterLoginComponent入口

service层调用具体的登录实现,不能直接new,这样的话,每个进行login登录的用户线程,都会new两个对象,一个是左路的抽象子类RegisterLoginComponent作为调用入口;一个是右路具体的子类,如RegisterLoginByDefault。对于用户量庞大且活跃度较高的应用,这种代码很可能会引起频繁的垃圾收集操作。

引入工厂类RegisterLoginComponentFactory进行RegisterLoginComponent对象的生成和缓存。

利用@PostConstruct注解,在RegisterLoginByDefault对象和RegisterLoginByGitee对象注入到SpringBoot的容器后,初始化funcMap

补充

桥接模式的核心其实就是桥梁,本质也是组合模式,只是扩展性更强。

监控告警可以采用这种,Notification是抽象,MsgSender是实现,具体有短信告警、电话告警等

组合模式

解释

将对象组合成树形结构以表示“部分—整体”的层次结构

Pasted image 20240907194633

  • Component抽象角色。所有树形结构的叶子节点和非叶子节点都需要继承该抽象角色
  • Composite树枝构件角色 非叶子节点
  • Leaf叶子构件角色

但是有些过时,现实场景,不能保证叶子节点永远不会没有子节点。因此再继续优化。
Pasted image 20240818173156

实现

设计要点:

  • addProductIteam方法和delProductIteam方法,都不强制子类进行实现,因此没有使用abstract修饰这两个方法,子类可以根据需求自主选择实现哪些方法。
  • 参数都用AbstractProductIteam本身,遵循李氏替换原则

对应controller service和repository

其中,generateProductTree方法是将数据库的商品类目信息转化成组合模式树形结构的核心代码

访问者模式

概念

旨在操作某对象结构中各个(各层级)元素的模式,在不改变元素整体结构的前提下,定义作用于这些元素的新操作。

商品类目的树形结构来说,我们可以利用访问者模式,对树形结构的任意节点进行操作(添加、删除)​。

Pasted image 20240907225018

除Client调用端以外,访问者模式包含5个角色,其中两个角色属于组合模式,我仅仅需要搞定其他3个角色即可

  • Visitor抽象访问者。接口或抽象类均可,定义访问者能够访问的数据类型
  • ConcreteVistor具体访问者 定义支持商品类目添加的具体访问者和支持商品类目删除的具体访问者
  • ObjectStructure 数据提供者 Client先通过ObjectStructure获取树形商品类目数据,再调用Visitor对第1步获取的树形商品类目数据进行访问操作
  • Component被访问者抽象角色
  • Composite被访问者具体角色

Visitor

1
2
3
public interface ItemVisitor<T> {  
T visitor(AbstractProductItem productItem);
}

ConcreteVistor

ObjectStructure数据提供者

其实就是RedisCommonProcessor,从缓存获取数据

Pasted image 20240907230308

controller service和repository

无论是商品类目添加还是商品类目删除,代码的实现逻辑并未涉及补偿机制以及极端场景

状态模式

概念

Pasted image 20240818190853

  • State抽象状态角色:该角色主要进行状态的定义和方法的定义。
  • ConcreteState具体状态类:不同的状态需要创建不同的状态类(订单有四个状态,就需要创建四个具体的状态类)​,并且实现抽象状态类定义的方法。
  • Context上下文角色(也称环境角色)​:封装状态的转化逻辑,是状态的转化过程的容器,暴露给客户端使用。可以类比Spring框架的ApplicationContext角色。

缺点:

  1. 使得service层形同虚设,逻辑全在context中
  2. 并发的问题,不同的订单不能使用同一个context,context有状态。但是无状态的话,违背UML类图的结构

解决:

采用spring状态机

概念

状态机是状态模式的一种应用,相当于上下文角色的一个升级版,在工作流状态转化、订单状态转化等各种系统中有大量使用,封装状态的变化规则。

  • state(状态)​:如订单的不同状态
  • transition(转移)​:一个状态接收一个输入,执行了某些动作到达了另外一个状态的过程就是一个transition(转移)​。例如,订单状态在ORDER_WAIT_PAY的情况下,接收到了一个支付动作,那么订单状态就会从ORDER_WAIT_PAY状态transition(转移)到ORDER_WAIT_SEND状态。
  • transition condition (转移条件)​:也叫作Event(事件)​,在某一状态下,只有达到了transition condition(转移条件)​,才会按照状态机的转移流程转移到下一状态,并执行相应的动作。例如,订单状态在ORDER_WAIT_PAY的情况下,接收到了一个支付动作,并且支付成功了,此处的支付成功就是transition condition(转移条件)​。
  • action (动作)​:在状态机的运转过程中会有很多种动作,此处的动作是一个统称,如进入动作—在进入状态时、退出动作—在退出状态时、输入动作—依赖于当前状态和输入条件进行、转移动作—在进行特定转移时进行

基于Spring状态机进行实战需要创建的类:

  • 订单对象类
  • Context上下文环境类 不需要
  • 代表订单状态的枚举类(enum),四个订单状态
  • 代表订单操作的枚举类(enum)
  • 转化过程配置到Spring状态机中,需要创建基于@Configuration注解的配置类

Pasted image 20240908072241

具体实现

创建订单状态枚举类

1
2
3
4
5
6
public enum OrderState {  
ORDER_WAIT_PAY, //待支付
ORDER_WAIT_SEND, // 待发货
ORDER_WAIT_RECEIVE, // 待收货
ORDER_FINISH; // 完成订单
}

订单操作枚举类

1
2
3
4
5
public enum OrderStateChangeAction {  
PAY_ORDER, // 支付操作
SEND_ORDER, // 发货操作
RECEIVE_ORDER; //收货操作
}

订单对象类

1
2
3
4
5
6
7
8
9
10
11
@Data  
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Order {
private String orderId;
private String productId;
private OrderState orderState;
private Float price;
}

状态机配置类OrderStateMachineConfig

  • 设置初始状态
  • 配置转换流程
  • 状态机自身的存储和读取 spring-statemachine-redis

观察者模式

核心就两个:观察者observer和被观察者subject,再加上各自的子类

缺点:很难优雅的把观察者添加到observerList中

解决:对observersList属性的初始化过程中,我们需要引入一个用fianl static修饰的Vector数据结构,并通过@PostConstruct注解初始化Vector中的元素

基于状态机的观察者模式

作用:

  1. 监听器 监听对象
  2. 监听到状态变化,所采取的行动逻辑

通过@OnTransition注解,标注source和target属性,代表了该方法专门用于监听订单状态从ORDER_WAIT_PAY到ORDER_WAIT_SEND的转化

PayToSend方法的参数Message<OrderStateChangeAction>message,此处使用org.springframework.messaging.Message类,对OrderStateChangeAction订单操作枚举类进行了封装,从字面意义来说,这个参数代表了一条订单操作的消息。我们可以根据这个消息,获取订单的信息,并进行后续处理

controller、service

雷点

所有的第三方平台的支付操作,都是两段式的:第一段向第三方支付平台提交订单支付请求;第二段第三方支付平台回调我们平台的接口。只有在第二段的时候,我们才能确认订单支付是否成功,才能确认是否需要将订单状态转为待发货状态。

其他注意:

  • 订单存储到Redis中,是以orderId作为key的。创建完订单后,可以打开Redis客户端,通过get具体的orderId命令查看当前存储的订单信息。当订单签收成功后,Redis中的订单信息就会被删除
  • 状态机存储到Redis中,是以具体的orderId+STATE为key的

命令模式

概念

将请求封装到一个命令(Command)对象中,实现了请求调用者和具体实现者之间的解耦

Pasted image 20240908115357

  • 抽象命令(Command)角色 一般定义为接口,用于定义执行命令的接口
  • 具体命令(ConcreteCommand)角色 与命令接收者进行关联,调用命令接收者的方法
  • 接收者(Receiver)角色 真正执行命令的对象。订单转化流程的相关逻辑,都在此处进行实现。接收者可以有多个,主要根据业务需求而定
  • 调用者(Invoker)角色 接收客户端正确的命令,并触发命令的执行
  • 客户端(Client)角色 创建Invoker和命令,并通过invoker触发命令

实现

接收者OrderCommandReceiver

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
@Component  
public class OrderCommandReceiver {
// 接收命令后执行
public void action(Order order) {
switch (order.getOrderState()) {
case ORDER_WAIT_PAY:
System.out.println("创建订单:order = " + order);
System.out.println("存入db");
return;
case ORDER_WAIT_SEND:
System.out.println("支付订单:order = " + order);
System.out.println("存入db");
System.out.println("通过 MQ 通知财务");
System.out.println("通过 MQ 通知物流");
return;
case ORDER_WAIT_RECEIVE:
System.out.println("订单发货:order = " + order);
System.out.println("存入db");
return;
case ORDER_FINISH:
System.out.println("接收订单:order = " + order);
System.out.println("存入db");
return;
default:
throw new UnsupportedOperationException("Order state error");
}
}
}

抽象命令OrderCommandInterface

1
2
3
public interface OrderCommandInterface {  
void execute(Order order);
}

具体命令OrderCommand

1
2
3
4
5
6
7
8
9
@Component  
public class OrderCommand implements OrderCommandInterface{
@Autowired
private OrderCommandReceiver receiver;
@Override
public void execute(Order order) {
this.receiver.action(order);
}
}

命令调用者Invoker

1
2
3
4
5
public class OrderCommandInvoker {  
public void invoke(OrderCommandInterface command, Order order) {
command.execute(order);
}
}

调用

OrderStateListener类的调用

详见之前的观察者模式的OrderStateListener

1
2
OrderCommandInvoker invoker = new OrderCommandInvoker();  
invoker.invoke(orderCommand,order);

service的调用,详见 [[设计模式重新整理#controller、service]]

策略模式

概念

指对象有某个行为,但是在不同的场景中,该行为有不同的实现逻辑,即不同的策略,实现方式不同

Pasted image 20240908121422
哈哈,看着跟状态模式一样,

  • Strategy抽象策略角色:该角色主要进行策略方法的定义
  • ConcreteStrategy具体策略类:不同的策略需要创建不同的策略类(在多种类支付的实战中,支付宝支付策略和微信支付策略就是具体的策略类)​,并且实现抽象策略类定义的方法。
  • Context上下文角色(也称环境角色)​:关联抽象策略类,并调用策略类的方法

实现

第三方支付,如支付宝

抽象策略类PayStrategyInterface

1
2
3
4
public interface PayStrategyInterface {  
// 返回值类型string 访问第三方支付,平台返回一个url地址,让用户进行支付
String pay(Order order);
}

具体策略AlipayStrategy

context

Service层调用的是门面模式的封装层,门面模式调用策略工厂及策略模式的相关类

策略模式和策略工厂的配合使用,就不需要将context修改成无状态类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public abstract class AbstractPayContext {  
public abstract String execute(Order order);
}

public class PayContext extends AbstractPayContext{
private PayStrategyInterface payStrategy;

public PayContext(PayStrategyInterface payStrategy) {
this.payStrategy = payStrategy;
}

@Override
public String execute(Order order) {
return this.payStrategy.pay(order);
}
}

门面模式

解释

旨在封装。可以封装一个需求模块,如多种类支付模块,也可以封装一个复杂的系统,从设计模式的角度看Spring Cloud Gateway,Gateway就是一个门面,Spring Cloud Gateway提供了所有请求的访问入口,为后续无数的微服务模块提供了门面。

  • Facade门面角色:该角色暴露给调用者进行调用。作为门面角色,它知道子系统的所有功能,门面角色会将客户端发来的请求转发到子系统中,转发之前,可以进行一定的类型转换和参数封装等辅助逻辑
  • subsystem子系统角色:子系统角色可以简单到一个类,也可以复杂到多个系统。当然,针对第三方支付需求来说,子系统角色就是策略模式的Context环境类——PayContext

实现

facade可以直接new创建Context类和具体的策略类,但是每次支付都创建,对内存产生很大的压力,导致频繁的minorGC

工厂模式

概念

  • Product:抽象产品角色
  • ConcreteProduct:具体产品角色。此处我们具体需要创建的对象是PayContext类
  • Creator:抽象工厂角色
  • ConcreteCreator:具体工厂角色

Pasted image 20240908130056

实现

享元模式

解释

旨在解决重复对象的内存浪费问题,为重复的对象创建缓冲池。享元模式最经典的就是池技术,如String常量池、数据库连接池等都是享元模式的经典应用。我们对享元模式的使用,就是通过Map作为本地缓存,减少PayContext对象的重复创建。

Pasted image 20240908202443

  • FlyWeight角色:抽象享元角色。定义对象的抽象方法和属性,对应我们的AbstractPayContext类
  • ConcreteFlyweight角色:具体享元角色。对应我们的PayContext类
  • FlyWeightFactory角色:享元工厂。提供对象的创建、缓存及获取,对应我们的PayContexFactory类
  • Client角色:3个箭头指向,意思是Client既能调用FlyWeightFactory享元工厂角色,又能直接调用具体的实现类,对应我们的PayFacaded类
  • UnsharedFlyweight角色:不可共享的角色,示例中没有

Pasted image 20240908202653

Spring IOC就是一个大型享元模式的应用,里边就有UnsharedFlyweight角色

Pasted image 20240908202721

  • FlyweightFacotry Spring的Bean Factory,就是一个享元工厂,BeanFactory就是采用的ConcurrentHashMap进行单例对象的缓存,减少对象的创建
  • Flyweight 使用@Controller、@Service、@Component等注解标注我们的类,Spring会根据我们标注的注解进行检索,找到标注注解的具体的类,将对象托管到Spring的容器中。诸如@Controller、@Service、@Component等注解,都是依托于java.lang.annotation包实现的。因此,可以将java.lang.annotation.*视为抽象享元角色
  • ConcreteFlyweight 通过@Autowired注解将对象注入到我们的类中,全局只有一个对象,完全共享,不会对内存产生压力
  • UnsharedFlyweight 对于一些对象,我们无法将其设置为单例对象,每次使用该对象,我们需要使用new关键字进行创建,或者我们可以使用@Scope注解,将对象标记为多例prototype对象,在每次使用对象时,都会重新创建一个全新的对象
  • Client调用者 可以通过Spring的上下文对象ApplicationContext,调用BeanFactory的getBean方法获取单例对象;可以直接通过new关键字创建具体对象,即使对象是单例的,我们依然可以通过new关键字创建对象;可以直接通过new关键字创建多例对象

责任链

解释

  • Handler抽象责任角色:抽象责任角色负责定义抽象方法,并且需要在该角色中定义nextHandler。开篇我们就提到过,责任链模式就类似于链表,创建链表节点时,我们会定义next节点属性。同理,此处我们定义nextHandler属性,将多个责任类定义到一个责任处理链条中
  • ConcreteHandler具体责任角色:具体的责任类。根据本章的实战需求,我们需要定义三个具体责任类:按用户购物种类筛选的责任类、按用户所在城市筛选的责任类和按用户性别筛选的责任类
  • Client责任链的装配者、使用者:Client在进行责任链的调用前,需要对责任链条进行装配,说直白些,就是将多个具体责任类,通过nextHandler属性进行连接,因此Client角色的作用分为两个:装配责任链条、调用责任链条的头节点
    Pasted image 20240825201408

实现

需求描述

业务投放场景

不同的用户可能会收到不同的投放内容,我们需要根据用户不同的属性进行投放业务的筛选,只展示符合当前用户的投放信息

返回信息之前,增加点判断条件对信息进行筛选

业务那边要求实时调整筛选条件

实时调整代表业务部门可以根据需求随意组合筛选条件。举个例子,我们的代码中定义了“按用户购物种类的筛选逻辑”​“按用户所在城市的筛选逻辑”和“按用户性别的筛选逻辑”​,如果业务部门想要将某条投放信息展示给所有女性用户,业务部门只需要使用“按用户性别的筛选逻辑”​;那如果业务部门想要将某条信息投放给所有的居住在北京的女性用户,业务部门就需要使用“按用户性别的筛选逻辑”和“按用户所在城市的筛选逻辑”​。

一些广告商想要作投放推广,投放全国女性用户和投放北京女性用户的价格肯定是不一样的啊。咱们目前只是入门级别的,把城市作为最小的投放单元。微信的广告投放更狠,都是按照方圆公里数进行投放的,投放多少公里以内的符合某些条件的用户。

Apollo是携程框架部门研发的开源配置管理中心,能够集中化管理应用的不同环境、不同集群的配置,配置修改后能够实时推送到应用端,而且Apollo还有UI操作界面,完全满足投放的业务需求。业务部门可以在Apollo的UI界面修改筛选条件,然后单击发布,新的刷选条件就会实时地推送到咱们的项目

Apollo配置中心+责任链模式。筛选条件目前只需要支持三个:​“按用户购物种类的筛选逻辑”​“按用户所在城市的筛选逻辑”和“按用户性别的筛选逻辑

实现

需求:

  1. 在Apollo中配置的duty.chain的值是“city,sex,product”​,我们如何将它们转化成CityHandler→SexHandler→ProductHandler这样的责任对象链条
  2. 如何确保,只有在第一次初始化责任链条或者是duty.chain的配置发生改变时,才去重新进行责任链条的组装

采用了枚举类+反射责任类创建+synchronized同步代码块+链表哑结点的简易算法进行实现,并且使用了两个全局变量控制组装时机,只有在第一次进行责任链条初始化和duty.chain配置更新时,才会触发责任链条的重新组装

Pasted image 20240825202221
责任类

组装和调用

装饰器模式

解释

Pasted image 20240826172210

  • Component抽象构件:Component是一个接口或者抽象类,定义我们核心的原始对象。本章实战,我们需要对OrderService的pay方法进行装饰,因此此处的Componet抽象构件,就是OrderService的抽象父类
  • ConcreteComponent具体构件:你要装饰的就是它,就是我们的OrderService类
  • Decorator抽象装饰角色:定义装饰器的属性和新的方法。此处我们的积分更新/红包发放方法的定义,就是在该类中进行的
  • ConcreteDecorator具体装饰角色:新的装饰功能的具体实现类。具体实现积分更新/红包发放逻辑,服务降级逻辑也在此处

实现

需求

平台积分更新和红包发放,在支付之后,并且需要做服务降级

  • 0代表正常服务,每次商品支付成功后,直接进行更新操作
  • 1代表延迟服务,遇上秒杀或双11活动,需要进行延迟更新,比如说凌晨0点开始秒杀,可以将积分更新或红包发放在30分钟后进行处理
  • 2代表暂停服务,平台可以随时将积分更新和红包发放业务暂停

实现

apollo设置延时时间+rabbitmqTTL机制和死信队列实现延迟服务+装饰器隔离支付和积分红包的服务

Pasted image 20240909173849

建造者模式

概念

  • Product产品类:要建造的对象,本章对应我们的电子发票类
  • Builder抽象建造者:负责产品的组建,定义产品组建的抽象方法
  • ConcreteBuilder具体建造者:实现抽象类定义的所有方法,并且返回一个组建好的对象。根据本章需求,返回电子发票对象
  • Director导演类:负责指导Builder进行对象创建,也可以称之为指挥者

Pasted image 20240826204457

实现

需求分析

开发票,分为个人和企业
个人电子发票只需要填写抬头,企业电子发票需要填写税号、抬头还有银行卡等信息

使用建造者模式可以。

扩展性呢?

指挥者角色,这个角色主要负责信息的拼装,一般不负责信息的校验和获取。比如,将银行卡信息校验放到指挥者角色中不太合适,产品信息的获取放到这里也不合适,为指挥者角色做一个代理类,代理类中会有这些辅助的校验和辅助信息。后续新增其他逻辑的时候,直接在代理类中作修改就行,非常方便,扩展性很不错

此外,支持clone

两个意义:
一是对于发票的不可变信息,我们可以直接克隆,减少new关键字的使用,因为发票信息中,有一部分信息是固定不变的;
二是考虑未知的备份操作,一张电子发票开具后,可能不同的部门有不同的使用方式,支持Clone的话,可以直接在生成好的电子发票对象上进行Clone操作,生成两个相同的电子发票对象,分别进行不同的使用

实现

product产品类

builder建造者

director导演

director搞了个AbstractDirector抽象类,为了程序的扩展性,银行卡信息的校验放在代理类,此外后续可能有其他的校验

上面也是代理模式

  • Subject抽象主题角色:被代理角色的抽象角色。拿本章需求来说,AbstractDirector类便是抽象主题角色
  • RealSubject具体主题角色:被代理角色。也就是我们的Director类
  • Proxy代理角色:在该角色中,我们要代理Director类的方法,并且增加前置的校验处理

原型模式

概念

Pasted image 20240826211305

  • Prototype原型抽象角色:原型模式的抽象角色,就是Java自带的java.lang.Cloneable接口,我们无须自主创建该角色
  • ConcretePrototype原型具体角色:实现Cloneable接口的对象。依照本章的实战需求,我们需要让PersonalTicket和CompanyTicket实现Cloneable接口。

实现

上个发票实现cloneable就是原型模式

这里涉及到 引用拷贝、深拷贝和浅拷贝的区别

引用拷贝:
创建了teacherWang对象,然后创建teacherSun对象等于teacherWang对象,teacherWang和teacherSun共用一个对象的内存地址,整个过程中,堆内存中只有一个Teacher对象

浅拷贝:
实现cloneable接口,只能拷贝最外层的对象。拿Teacher对象来说,通过浅拷贝,我们会在堆内存中创建新的Teacher对象,然而Teacher对象中的Student对象却不会被重新创建。

teacherSun和teacherWang指向不同的Teacher对象,但两个Teacher对象却指向同一个Student对象

深拷贝:
可以拷贝深层的student对象
如何实现?

  • 让所有层次的对象,都实现Cloneable接口,并实现Object的Clone方法
  • 通过Serializable序列化接口,实现深拷贝 大部分都是这种

中介者模式

概念

中介者模式可以通过中介者对象来封装一系列的对象交互,将对象间复杂的关系网状结构变成结构简单的以中介者为核心的星形结构,简化对象间的关系。同时,中介者模式能够将各个对象之间的关系解耦,每个对象不再与它关联的对象直接发生相互作用,而是通过中介者对象与关联的对象进行通信。

产品进销存系统中进行应用,包括我们的微信、QQ聊天,也都是采用了中介者模式的设计思想

Pasted image 20240827103053

  • Mediator抽象中介者:定义不同同事之间的信息交互方法
  • ConcreteMediator具体中介者:实现抽象中介者的方法,它需要知道所有的具体同事类,同时需要从具体的同事类那里接收信息,并且向其他具体的同事类发送信息。
  • Colleague抽象同事类:以本章为例,该抽象角色是购买者和帮忙支付者的父类。
  • ConcreteColleague具体同事类:每个具体同事类都只需要知道自己的行为即可,但是他们都需要认识中介者。以本章为例,购买者和帮忙支付者就是具体同事类,两个类都需要了解中介者类

跟代理模式的区别:
只负责中转,不处理具体事务

跟观察者模式的区别:
介入具体业务,对象的交互,eventbus属于观察者模式

缺点:
容易膨胀,变成大而复杂的上帝类

实现

需求分析

朋友代付

对于公司来说,咱们只关心商品的订单 订单谁支付、通过什么方式支付都不要紧,只要支付的时候,是当前的商品订单就行。
朋友代付功能,无非就是把商品订单信息转发到待支付的朋友那里,然后请朋友帮忙支付就行了

订单消息和支付结果中转的场景

  • 订单消息中转 把我要购买的商品订单转发给我的朋友
  • 支付结果中转 朋友付完钱,不得告诉你一声

实现

Mediator抽象中介者AbstractMediator

1
2
3
4
5
6
7
8
9
public abstract class AbstractMediator {  
/**
* 消息交互
* @param orderId 用户的订单ID
* @param targetCustomer 目标用户,比如说张三请李四帮忙支付,那么李四便是目标用户
* @param customer 抽象同事类 中介者需要根据该参数确定调用者的角色——购买者或帮忙支付者
* @param payResult 只有支付成功后此参数才不为null
*/ public abstract void messageTransfer(String orderId, String targetCustomer, AbstractCustomer customer, String payResult);
}

Colleague抽象同事类AbstractCustomer

  • 关联中介者,因为购买者和支付者都需要直接与中介者打交道,因此需要关联中介者类
  • 定义与中介者进行消息交互的方法
  • 有一些核心属性,如需要支付的订单号及当前客户信息等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public abstract class AbstractCustomer {  
//关联中介者
public AbstractMediator mediator;
//订单ID
public String orderId;
//当前用户信息
public String customerName;

public AbstractCustomer(AbstractMediator mediator, String orderId, String customerName) {
this.mediator = mediator;
this.orderId = orderId;
this.customerName = customerName;
}

public String getCustomerName() {
return this.customerName;
}

// 和中介者的信息交互方法
public abstract void messageTransfer(String orderId, String targetCustomer, String payResult);
}

ConcreteColleague具体同事类Buyer和Payer

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

public Buyer(AbstractMediator mediator, String orderId, String customerName) {
super(mediator, orderId, customerName);
}

@Override
public void messageTransfer(String orderId, String targetCustomer, String payResult) {
super.mediator.messageTransfer(orderId,targetCustomer,this,payResult);
}
}

public class Payer extends AbstractCustomer{

public Payer(AbstractMediator mediator, String orderId, String customerName) {
super(mediator, orderId, customerName);
}

@Override
public void messageTransfer(String orderId, String targetCustomer, String payResult) {
super.mediator.messageTransfer(orderId,targetCustomer,this,payResult);
}
}

ConcreteMediator具体中介者Mediator

  • 具体中介者需要知道所有的同事类,因此该类需要关联同事类
  • 具体中介者需要实现抽象中介者的方法,作为不同同事类之间的信息交互桥梁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Component  
public class Mediator extends AbstractMediator{
public static Map<String, Map<String, AbstractCustomer>> customerInstances = new ConcurrentHashMap<>();

@Override
public void messageTransfer(String orderId, String targetCustomer, AbstractCustomer customer, String payResult) {
if (customer instanceof Buyer) {
AbstractCustomer buyer = customerInstances.get(orderId).get("buyer");
System.out.println("Friend pays on behalf: "+buyer.getCustomerName() + " transfer orderId "+ orderId+" to customer "+targetCustomer+" to pay");
} else if (customer instanceof Payer) {
AbstractCustomer payer = customerInstances.get(orderId).get("payer");
System.out.println("payment completed on behalf "+payer.getCustomerName()+" completed orderId "+orderId+" pay, notify "+targetCustomer+", pay result: "+payResult);
customerInstances.remove(orderId);
}
}

public void addInstance(String orderId, HashMap<String, AbstractCustomer> map) {
customerInstances.put(orderId, map);
}
}

Pasted image 20240910140421

保证全局唯一中介者

通过Map<String, Map<String, AbstractCustomer>>数据结构存储购买者和实际支付者的信息。其中,外层Map的key为OrderId,内层Map的key为Buyer和Payer,value为AbstractCustomer对象

代支付完成后,从Map中删除此orderId的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service  
public class OrderService implements OrderServiceInterface{
@Autowired
private Mediator mediator;
public void friendPay(String sourceCustomer, String orderId, String targetCustomer, String payResult, String role) {
//创建中介者
Buyer buyer = new Buyer( mediator, orderId,sourceCustomer);
Payer payer = new Payer( mediator, orderId,sourceCustomer);
HashMap<String, AbstractCustomer> map = new HashMap<>();
map.put("buyer", buyer);
map.put("payer",payer);
mediator.addInstance(orderId,map);
if (role.equals("B")) {
buyer.messageTransfer(orderId,targetCustomer,payResult);
} else if (role.equals("P")) {
payer.messageTransfer(orderId,targetCustomer,payResult);
}
}
}

模板方法模式

概念

定义一个操作中的算法框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤

白话版:模板方法模式会定义好做一件事情的步骤,这个步骤是固定的,一部分步骤已经实现好了,你无须关心;另外一部分步骤没有做任何实现,需要子类进行实现

对于已经定义好的步骤,我们称之为基础方法,需要子类进行实现的方法称之为模板方法
Pasted image 20240827144906

  • AbstractClass抽象模板对象:抽象模板对象主要负责基本方法的实现以及抽象模板方法的定义
  • ConcreteClass具体模板对象:对抽象模板方法进行个性实现,不会改变整体的执行结构

Spring源码的核心refresh方法里面的postProcessBeanFactory方法和onRefresh方法就是两个模板方法,与我们距离最近的模板方法模式的使用案例,就是AQS源码中的使用。

实现

需求

审计日志

记录四种订单日志即可。每种订单日志有相似的部分,都有用户操作时间、操作类型、订单ID和用户ID

  • 订单创建日志:无其他特殊信息,包含刚才描述的相似内容即可
  • 订单支付日志:除相似内容外,还需要记录支付类型和实际支付金额
  • 订单发货日志:除相似内容外,还需要记录快递公司和快递单编号
  • 订单签收日志:无其他特殊信息,包含刚才所说的相似内容即可

这些日志放到哪里,数据处理部门给我们提供了一个Queue,我们直接将信息发送到Queue里就行。

实现

Pasted image 20240827150149

模型OrderAuditLog

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Data  
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OrderAuditLog {
//当前用户信息
private String account;
// 用户操作
private String action;
// 用户操作具体时间
private Date date;
private String orderId;
// 其他额外信息
private Object details;
}

抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public abstract class AbstractAuditLogProcessor {  
//创建我们的auditLog 基础方法 不允许子类重写
private final OrderAuditLog basicAuditLog(String account, String action, String orderId) {
OrderAuditLog auditLog = new OrderAuditLog();
auditLog.setAccount(account);
auditLog.setAction(action);
auditLog.setOrderId(orderId);
auditLog.setDate(new Date());
return auditLog;
}

//定义抽象模板方法,设置订单审计日志的额外信息,供子类进行实现
protected abstract OrderAuditLog buildDetails(OrderAuditLog auditLog);

//定义订单审计日志的创建步骤 不允许子类重写
public final OrderAuditLog createAuditLog(String account, String action, String orderId) {
//设置审计日志的基本信息
OrderAuditLog auditLog = basicAuditLog(account, action, orderId);
// 设置额外信息
return buildDetails(auditLog);
}
}

具体实现如CreateOrderLog PayOrderLog 等过程

1
2
3
4
5
6
7
@Component  
public class CreateOrderLog extends AbstractAuditLogProcessor{
@Override
protected OrderAuditLog buildDetails(OrderAuditLog auditLog) {
return auditLog;
}
}

调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Service  
public class OrderService implements OrderServiceInterface{
@Autowired
private CreateOrderLog createOrderLog;

public Order createOrder(String productId) {
//订单生成的逻辑
String orderId = "OID" + productId;
Order order = Order.builder()
.orderId(orderId)
.productId(productId)
.orderState(OrderState.ORDER_WAIT_PAY)
.build();
redisCommonProcessor.set(orderId,order,900);
OrderCommandInvoker invoker = new OrderCommandInvoker();
invoker.invoke(orderCommand,order);
// 暂时 testAccount 后续可以从用户中心获取account
createOrderLog.createAuditLog("testAccount", "create", orderId);
return order;
}
}

2024新年规划

背景

过年回家,感觉真的是自己长大了,我们父母都是60左右了,已经步入老年,小孩也开始上小学,有了真正的压力,如果自己再不顶上去,恐怕很惨很惨。

很痛心自己没有早点用上自己的人脉,转行之后又是虚度3年光阴,希望能抓紧时间,成为业务专家,不再沦为底层。

340724fa0727f0bf50410582a4afd2e

规划

其实今年开年对自己来说还是挺好的,自己所处的行业是软件行业,也会接触到底层的很多东西,需要快速反馈,快速学习,保持这两天的状态,不能分散精力,先把通话模块完全搞定!

工作

2-3月份,搞定通话模块大部分内容

4-5月份,搞定算法刷题

6-7月份,重拾面试技巧

8月份,跟公司提离职,或者去上海分公司入职

最关键的半年,一定要顶住,加油,成败在此一举!

房子

3月份之前把房子挂出去

孩子

2-4月份,搞定洪恩识字的所有常用字,每天晚上抽查

上半年,跟上斑马的课程进度,英语能学会常用的单词

债务

把房子卖了之后就差不多了,重新开始新生活,每个月也要挤出来1w

周计划

每周要有至少两篇整理的文章

中国人口2023

人口乌云

大家都对中国人口下降隐隐约约有个预期,但没想到下降的这么惨烈

前段时间,工程院院士、北大医学部主任公开表示,2023年的出生人口恐怕只有700-800万,700多万是什么概念,可能需要对比大家才有感觉,我们的巴铁,预测今年是647万,印度预测是2306万。我们也就是印度的1/3,和巴铁相当,而我们的断崖式下降才刚开始…

如何破局?

可以放宽绿卡,引入巴铁人口,好歹是盟邦,也是黄种人,比广州的黑叔叔强多了,能短时间内抵御人口灰犀牛的冲击,至于长期,也不是我们所能想到的了,毕竟我们的同化能力是无与伦比的,只是代价比较大而已,五胡乱华、五代十国殷鉴不远,新疆可能更绿了,其他都还好,先苟住再雄起嘛,肉食者不想发钱补贴民众,就只能接受次优解了。

参考

linux命令拾遗

jenkins流水线构建时,有tar命令

1
sh 'tar --transform="flags=r;s|${NAME}${DIST_PATH}|src|" -czf /tmp/$UPLOAD_FILE ${NAME}${DIST_PATH}'

--transformtar命令的一个选项,用于指定文件名的转换规则。

  • flags=r:这部分指定了转换规则的标志。在这种情况下,r表示替换(replace)
  • s|${NAME}${DIST_PATH}|src|:这部分是实际的转换规则,使用了sed命令的替换语法。它指定了源文件名和目标文件名的替换关系。${NAME}${DIST_PATH}是源文件名,src是目标文件名。具体来说,它将tar归档中的文件名${NAME}${DIST_PATH}替换为src

jenkins基础

基础概念

持续集成CI

概念

continuous integration,持续集成的重点是构建编译和测试,通过自动化的构建(主要是构建编译、自动化测试)来验证,从而尽早发现集成错误。

持续集成的工作流程

  • 初始化CI流程 基本的CI流程的配置,如脚本、定时任务
  • 拉取最新代码 从gitlab仓库拉取最新代码到构建服务器磁盘
  • 构建 通过配置的脚本触发执行构建,如java构建一般基于maven或gradle
  • 执行测试 一般包括单元测试和集成测试,Java的单元测试默认Junit
  • 结果处理 一般都要通知给对应人员,如邮件、钉钉、短信

持续交付CD

continuous delivery,将产品尽快的发布上线的过程。持续交付是在持续集成的基础上的扩展,也就是说除了自动化编译、自动化测试,为尽快上线还要自动化发布

持续部署

continuous deployment,在持续交付的基础上,将编译、测试、打包部署到生产环境的过程实现自动化。当然,一般非生产环境保证自动化,生产环境还是手动。

DevOps

概念

开发和运维的组合,打破开发和运维的壁垒,通过工具链,将开发、测试、运维的工作串联起来,将全部流程自动化,减少人力重复投入,降低人为风险。

工具链

  • 编码 代码开发和审查、源码管理工具、代码合并 gitlab
  • 构建 持续集成工具 Jenkins maven
  • 测试 持续测试工具 selenium Jmeter
  • 发布 变更管理、发布审批、发布自动化
  • 容器平台 docker k8s
  • 配置 基础设置配置和管理 Ansible
  • 监控 应用性能监控、终端用户体验 logstash nagios kibana zabbix

jenkins

文档 https://www.jenkins.io/zh/doc/

背景

前身hudson,Oracle收购sun之后hudson归Oracle所有,贡献者基于hudson改为jenkins。目前hudson已停止开发,Jenkins成为使用最多的CICD工具。持续集成是Jenkins的核心功能。

应用场景

  • 集成svn/git客户端实现源码checkout
  • 集成maven/ant/gradle/npm等构建工具实现源码编译打包和单元测试
  • 集成ansible实现自动化部署发布
  • 集成jmeter/k8s…
  • 可自定义插件或脚本通过Jenkins传参运行
  • 非常灵活,日常运维工作都可自动化

版本

  • Jenkins 2.x 主流,支持pipeline

  • Jenkins X 基于k8s的持续集成

搭建

主从集群,将构建任务分发到多个从节点执行,支撑多个项目的大量构建任务

部署方式:

  • 服务器直接运行war
  • 服务器yum下载Jenkins安装包
  • docker容器运行

环境说明

  • centos7

  • jdk1.8

  • maven3.x

  • git 1.8.3

  • jenkins 最新版本

  • master主机 centos30

  • slave主机 centos31

master安装

jdk
1
2
3
4
5
6
7
8
9
10
# 上传jdk包
# 配置Java环境变量
vi /etc/profile
export JAVA_HOME=/opt/jdk1.8.0_301
export JRE_HOME=/opt/jdk1.8.0_301/jre
export PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
# 使用source命令,使其生效
source /etc/profile
# 验证安装成功
java -version
git安装
1
2
3
yum install -y git
# 验证是否安装成功
git --version
sshpass安装
1
2
# 远程连接工具,备份恢复使用
yum install sshpass -y
wget安装
1
yum install wget -y
axel安装

多线程下载工具

1
2
wget http://download-ib01.fedoraproject.org/pub/epel/7/x86_64/Packages/a/axel-2.4-9.el7.x86_64.rpm
rpm -ivh axel-2.4-9.el7.x86_64.rpm
TODO 创建Jenkins用户
1
2
useradd -d /home/jenkins -m -s /bin/bash jenkins
# 密码
maven安装
1
2
3
4
5
6
7
8
9
# 上传压缩包,解压到/opt
# 配置环境变量
vi /etc/profile
export MAVEN_HOME=/opt/apache-maven-3.8.5
export PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin:$MAVEN_HOME/bin
# source
source /etc/profile
# 验证
mvn -v

配置镜像加速

1
2
3
4
5
6
7
8
9
10
maven下的conf/settings.xml找到<mirrors>和</mirrors>标签,添加
vi /opt/apache-maven-3.8.5/conf/settings.xml

<!-- 配置镜像加速 -->
<mirror>
<id>alimaven</id>
<name>alimaven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>

注意:如果配置本地仓库的地址,一定要赋予该目录其他用户可读写的权限

rsync安装

跨机器文件同步

1
yum install rsync -y
关闭防火墙
1
2
3
systemctl stop firewalld
# 永久关闭
systemctl disable firewalld
Jenkins安装
1
2
3
4
5
6
mkdir /opt/jenkins
# 为jenkins用户授权
chown -R jenkins:jenkins /opt/jenkins
su jenkins
# 下载Jenkins最新版
axel -n 20 http://mirrors.jenkins-ci.org/war/latest/jenkins.war

创建启动脚本 vi jenkins.sh

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
#!/bin/bash
args=$1
#注意修改jenkinswar包的目录
jenkins_war_path="/opt/jenkins"
#jenkins开放端口
jenkins_http_port="8888"
#java安装路径
java_home="/opt/jdk1.8.0_301"
function isRuning(){
local jenkinsPID=`ps -ef|grep jenkins.war|grep -v grep|awk '{print $2}'`
if [ -z ${jenkinsPID} ];then
echo "0"
else
echo ${jenkinsPID}
fi
}

#停止jenkins
function stop(){
local runFlag=$(isRuning)
if [ ${runFlag} -eq "0" ];then
echo "Jenkins is already stoped."
else
`kill -9 ${runFlag}`
echo "Stop jenkins success."
fi
}

#启动jenkins
function start(){
local runFlag=$(isRuning)
echo "${runFlag}"
if [ ${runFlag} -eq "0" ];then
`${java_home}/bin/java -jar ${jenkins_war_path}/jenkins.war --httpPort=${jenkins_http_port} &` > /dev/null
if [ $? -eq 0 ];then

echo "Start jenkins success."
exit
else
echo "Start jenkins fail."
fi
else
echo "Jenkins is running now."
fi
}

#重启jenkins
function restart(){
local runFlag=$(isRuning)
if [ ${runFlag} -eq "0" ];then
echo "Jenkins is already stoped."
exit
else
stop
start
echo "Restart jenkins success."
fi
}

#根据输入的参数执行不同的动作
#参数不能为空
if [ -z ${args} ];then
echo "Arg can not be null."
exit
#参数个数必须为1个
elif [ $# -ne 1 ];then
echo "Only one arg is required:start|stop|restart"
#参数为start时启动jenkins
elif [ ${args} = "start" ];then
start
#参数为stop时停止jenkins
elif [ ${args} = "stop" ];then
stop
#参数为restart时重启jenkins
elif [ ${args} = "restart" ];then
restart
else
echo "One of following args is required: start|stop|restart"
exit 0
fi

启动jenkins

1
sh jenkins.sh start

配置加速

vi /home/jenkins/.jenkins/hudson.model.UpdateCenter.xml
将xml的url替换为http://mirror.xmission.com/jenkins/updates/update-center.json

1
2
3
4
5
6
7
<?xml version='1.1' encoding='UTF-8'?>
<sites>
<site>
<id>default</id>
<url>http://mirror.xmission.com/jenkins/updates/update-center.json</url>
</site>
</sites>

或在web界面manage jenkins->manage plugins->Advanced->Update Site修改

访问Jenkins http://ip:8888

获取管理员密码 cat /home/jenkins/.jenkins/secrets/initialAdminPassword

修改管理员密码

页面左上角jenkins->people->admin->configure修改

插件安装

需要安装ssh、Pipeline、Role-based Authorization Strategy、Git、Gitee、Git Parameter、thinBackup这些插件,manage jenkins->manage plugins,点击available搜索下载

插件名 作用
Pipeline 流水线部署项目
Role-based Authorization Strategy 基于角色的用户管理权限策略,最常用的jenkins权限策略管理插件
Git 支持使用GitHub、gitlab等源码仓库,创建普通job会用到
Gitee Gitee基于gitlab plugin开发的插件,配置Jenkins触发器,接受gitee平台发送的webhook触发jenkins进行自动化持续集成或持续部署,并可将构建状态反馈gitee平台
Git Parameter 可把git的tag branch作为构建参数传进来,方便使用branch构建
Extended Choice Parameter 参数化构建
Maven Integration 为maven2/3项目提供高级集成功能
SonarQube Scanner 代码扫描
Email Extension 扩展发送告警邮件的控制粒度,可定义邮件触发器、邮件内容、收件人
Workspace Cleanup 每次build之前删除workspace目录下指定的文件
Monitoring 监控Jenkins节点的CPU、系统负载、平均响应时间和内存使用
Build Monitor View 将Jenkins项目以看板的形式呈现
ThinBackup 单元测试覆盖率
jacoco 单元测试覆盖率
Generic Webhook Trigger webhook

jenkins的配置

Jenkins->Manage jenkins->Configure System 进入系统配置页面

令牌配置

证书令牌

分组管理

new view即可

备份恢复

存储结构

在Jenkins中,所有数据默认都以文件形式存储在$JENKINS_HOME目录。占用空间最大的是jobs目录和workspace目录。

  • jobs目录 项目在Jenkins上的配置、构建日志、构建结果等所在的目录,对应/home/jenkins/.jenkins/jobs目录
  • workspace目录 项目在Jenkins上配置的源码仓库地址下载的源码所在的目录,如Java的maven构建操作就在此目录,对应/data/jenkins_data/workspace目录

结构

  • jobs目录下的一级目录名,均为在Jenkins上新建的job名称
  • 二级目录下的build里面出现的数字1,2,3…表示构建历史所在的目录,也就是一次构建有一个构建版本号,从1开始随着构建次数增加递增
  • workspace下面都是空的,因为还没拉取代码

pipeline

概念

流水线即代码,流水线的代码定义了整个的构建过程,包括构建、测试和交付应用程序的阶段,对流水线的定义写在Jenkinsfile文件中,可被提交到源码仓库。创建Jenkinsfile并提交到源码仓库的好处:

  • 自动为所有分支创建流水线构建过程并拉取请求
  • 在流水线上代码复查/迭代
  • 对流水线进行审计跟踪
  • 该流水线的真正源码可被团队成员共同查看和编辑

语法支持

Jenkinsfile能用两种语法编写,都能用pipeline内置的插件和steps

  • Declarative Pipeline声明式 推荐使用,语法更严格,有固定的组织结构
  • Scripted Pipeline 脚本化 groovy语言,只对结构和语法限制,用户可自己灵活实现和扩展

基本概念

  • stage 阶段,一个pipeline分为多个stage,每个stage代表一组操作,stage是一个逻辑分组的概念,可跨多个node
  • node 构建服务器节点,一个node就是要给Jenkins节点,或master或agent,是执行step的具体运行期环境
  • step 步骤,最基本的操作单元,小到创建一个目录,大到构建一个docker镜像,由各类Jenkins plugin提供

pipeline语法

参考https://www.jenkins.io/zh/doc/book/pipeline/syntax/#declarative-directives

  • 声明式pipeline必须包含在固定格式的pipeline{}内

    1
    2
    3
    pipeline {
    // insert declarative pipeline here
    }
  • 每个声明语句必须独立一行,行尾无需分号

  • 块blocks 由大括号括起来的语句如 pipeline{} parameters{} script{}

  • 章节sections 通常包括一个或多个指令或步骤 如agent post stages steps

  • 指令directives 如 environment option parameter trigger stage tools when

  • 步骤step 执行脚本式pipeline,如script{}

  • 块只能由阶段stages{}、指令、步骤(steps{})或赋值语句组成

  • 属性引用语句被视为无参方法调用,如input()

示例

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
// jenkinsfile(declarative pipeline)
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'make'
echo "Hello world"
script {
def browsers = ['chrome','firefox']
for (int i = 0; i < browsers.size(); ++i) {
echo "Testing the ${browsers[i]} browser"
}
}
}
}
stage('Test') {
steps {
sh 'make check'
junit 'reports/**/*.xml'
}
}
stage('Deploy') {
steps {
sh 'make publish'
}
}
}
}

说明:

  • pipeline是声明式流水线的一种特定语法,定义了包含执行整个流水线的所有内容和指令的block
  • agent 指示Jenkins为整个流水线分配一个执行器(在Jenkins环境中的任何可用代理/节点上)和工作区。一般用作指定在哪个节点上构建,如果不指定就写any表示任意节点
  • 定义Build Test Deploy三个阶段,每个阶段执行不同的步骤
  • stage 描述stage of this pipeline的语法块,定义在pipeline的不同阶段
  • steps 声明式流水线的特定语法,描述这个stage中要运行的步骤
  • sh 是一个执行给定的shell命令的流水线
  • echo 简单的字符串到控制台输出
  • junit 另一个聚合测试报告的流水线
  • stage括号的值表示阶段名称,内容不固定,自定义即可

gitee的demo

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
pipeline {
//指定运行的节点,slave运行
agent {
node {
label 'follower'
}
}
//定义各个阶段
stages {
// 获取git代码,credentialsId是配置的git账号
stage('git') {
steps {
git credentialsId: 'gitee-username', url: 'xxx'
}
}
// test阶段
stage('test') {
steps {
//可从environment获取
sh '${MAVEN_HOME}/bin/mvn test'
}
}
// 编译打包阶段,打jar包
stage('build') {
steps {
sh '${MAVEN_HOME}/bin/mvn clean package -DskipTests'
}
}
// docker镜像
stage('docker') {
steps {
sh 'docker build -t jenkins-demo:${BUILD_NUMBER} .'
}
}
// 部署阶段,停掉旧的docker进程,启动新镜像
stage('deploy') {
steps {
//如果test-boot没有在启动中,如果不跟或语句,会执行失败
sh 'docker rm -f jenkins-demo || sleep 0'
sh 'docker run --name jenkins-demo -d -p 8080:8080 jenkins-demo:${BUILD_NUMBER}'
}
}
}
}

其他常见语法:

  • when 允许流水线根据给定的条件决定是否应该执行阶段,必须包含至少一个条件

  • environment

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    pipeline {
    environment {
    // 顶层流水线块中使用的 environment 指令将适用于流水线中的所有步骤
    }
    stages {
    stage('Example') {
    environment {
    //在一个 stage 中定义的 environment 指令只会将给定的环境变量应用于 stage 中的步骤
    AN_ACCESS_KEY = credentials('my-prefined-secret-text')
    }
    steps {
    sh 'printenv'
    }
    }
    }
    }
  • options 从流水线内部配置特定于流水线的选项,如超时,中止流水线

    1
    2
    3
    4
    5
    6
    7
    pipeline { 

    options {
    timeout(time: 30, unit: 'SECONDS') //unit根据需要可选值有:SECONDS、MINUTES、HOURS
    retry(2)
    }
    }

《秦吏》有感

《秦吏》虽然是一个穿越小说,然而深厚的历史积淀,以及现代人的代入感,让人更能融入2千年前那赳赳老秦的画面。

昔日我们读书时,”秦王奋六世之余烈“这话并没有如何的感同身受,但是扪心自问,如果你的家族,从你开始,不说六代人,一般两三代就已经可以到寒门,跨越阶层了吧。如果六代人一直奋勇进取,不能扫六合才怪。这是中华的气运呀。

跑步随想

前言

原文本想发表到CSDN,但是牢骚话发不了,说什么违反社区规定,索性才开了自己的博客,专门记录自己的人生感悟和技术整理。

随想

当时日期为2023-05-31

最近看到或者亲身感受到好几个消息,还是有些担忧的。

  1. 卫健委开始呼吁一孩补贴了
  2. 《中国慈善家》根据基层医院的数据估算出生人口低于800万 而2022年为956万,2016年为1786万…
  3. 2022年人口首次下降
  4. 自己公司延长一个小时下班

其实陷入了一个死亡螺旋。

  • 要年轻人生孩子->鼓励结婚
    • ->鼓励谈恋爱->有更多的空闲时间
      
    • ->需要买房->要有合适的房价
    • ->生育年龄要尽早->初婚年龄要合适

而现实:

  • 构建营商环境->企业的残酷竞争和漠视法律

    • ->996已经是普遍情况->年轻人没时间谈恋爱
      
    • ->下行周期内卷更严重->学历内卷->被迫大龄单身->初婚年龄已不适合要孩子
  • 持续多年的土地财政->房价高企,经济下行周期更没人买账

    • 不敢降价,否则银行全是不良资产
      
    • 不敢放开,更严重,更无法收拾
    • 只能继续高企->年轻人继续买不起房,下行周期断供、烂尾->结不起婚

希望小伙伴们都能早日穿过幽暗的岁月,向前看,发出属于自己的光,温暖自己和周围。

后续

自己会关注这些烂事,当然,由于自己尚在生存关,仅仅只记录,防止国内互联网没了记忆,欢迎拍砖。

英语规划

前言

英语,作为一个拦路虎,很烦人,但是又不得不面对,因为英语作为国际通用语言,比较前沿的东西都是英语写的,科研成果、github、chatGPT等等,而中文环境充斥着僵尸号、各种二三手的消息、自我阉割的审查机制,导致学习成本用人生长度来计算的话,比英语高多了。

而作为成年人,尤其是职场人,你很难跟学生一样,抽出专门的时间去学习,因此,学习的目的是什么?如何有效学习?需要我们深度思考。

学习英语的目的

最通俗的来说,就是升职加薪。作为程序员,各类语言、工具基本上都是英语的,写的代码、甚至注释,最好也是英语,减少歧义,让自己的代码和轮子被复用,增强自己的影响力。

此外,最新的思想、潮流,基本上都是大洋彼岸吹来的,必须跟上潮流才能端好饭碗。如果有机会参与Google、Amazon、Facebook等的开源项目甚至加入这些公司,那就是世界级的程序员,不用再考虑生存价值了。

如何有效学习

现阶段不需要会说,只需要会读写即可。毕竟大部分是博客、论文交流。

以2年为期,让自己彻底学会读写。

读写的训练

一个是读,也就是单词和短语。因为程序世界的会短语即可,不需要太过复杂。

工具有:

  • 谷歌翻译 最简单
  • Burning Vocabulary 插件,遇到不认识的,放到行囊中,上下班的地铁上再多认识几遍即可

要以我为主,在两年后彻底摆脱谷歌翻译为目标。

材料 TODO

有空列清单,碰到就放这里,方便查看。

PS

看到一个安卓大佬的GitHub,https://github.com/JakeWharton

我竟然沉默无言了,确实”talk is cheap, show me your code.”,希望激励下自己。

image-20230618161203722

2023年6月29日

今天刷YouTube,发现自己关注的一个博主被裁员,然后很快就又找到一个新公司,晒他的leetcode,原来这么努力啊。

image-20230629224912979

感觉真的是自己时来运转,发现了自己之前连努力都达不到…难怪这么水,💪

人生规划笔记

人生规划

三步走:先生存,后发展,再实现自我

十年计划

中国的阶层

  • 贵族 开国功勋及家属群体
  • 世家 连续三代担任朝廷三品以上重要官员、朝廷内阁、翰林院等核心官员
  • 豪门 拥有巨额财富社会地位,为保证财富地位,会不得不和政治圈层联姻,以保证阶层不掉下去
  • 寒门 没落的贵族、世家、豪门,有钱,但进不了政治圈,如特朗普家族
  • 庶民 贵族、世家、豪门以外的远房亲戚,已经没有资源的没落贵族
  • 布衣 有房有地有产业的中产阶层
  • 流氓 流是没房没地,氓是没有固定工作的流浪汉

给你30年,你能冲到哪个阶段?是不是很现实?

三层生命

生理意义上的生命、行业范畴上的天命、人类范畴上的使命

第一层死亡是你的心脏停止跳动,脑子停止活动,那么从生物的角度来说你死了。因此这一个层面,是可以通过参加运动和锻炼的方式,提升免疫力和自身活力来延缓衰老的,但是你最终还是逃脱不了死亡。

第二层死亡通常在你的葬礼上,你的家人、朋友、认识你的人都来为你最后送行,那么你在社交圈上的地位和角色就死了,你下葬的那一刻,就代表你在社交圈中的消亡。你帮助过多少人,你影响过多少人改变命运?人毕竟是社会的人,你的社会地位,也是由你自身的专业能力,以及在行业中的认可度,自己的社群圈层深度和广度决定的。

第三层死亡是最后一个记得你的人也死掉后,在地球上,关于你的所有信息都不再被任何一个人知道,传承,这就是真正意义上的死亡。有一个玄孙理论,会让你想明白被人记住的意义:从未亲眼见过你一面的你孙子的孙子,因为你为家族的努力奋斗而得到庇护,因为你在社会上的贡献而感到自豪,因为家族中有你这样一个长辈而感到敬佩骄傲的时候,你这一辈子基本上就没有白活。

思考的时间跨度

短期计划靠自己,中期计划靠他人,长期计划靠家族

三年为一个公司小规划,你的年度规划得以专业知识和交付效率为主。

五年为一个公司大规划,你的年度计划修炼得结合公司部门发展。

十年为一个经济周期,你的人生修炼得结合经济发展周期,必要的时候得学习行业知识。

二十年为一个行业周期,你的人生修炼得看得懂行业变迁,并得到家人的全力支持。

三十年为一个产业生态链周期,你的人生修炼得看得懂公司的兴衰,然后培养自己孩子的未来。

五十年为一个城市的发展周期,你的人生修炼已经涉及整个家族的兴旺和开枝散叶。

百年为一个国运周期,你的人生修炼涉及怎么用自己的绵薄之力,为国效力,报效国家,并且让家族成员也可以前赴后继参与进去。

但凡我们能够看到出版人物传记级别的名人,都是在人类文明史上,用自己的精力和热情刻下自己为社会贡献的一丝痕迹。

一旦涉及10~30年的长期规划,这已经不是一个人努力,而是几代家族的共同努力了,一个家族是否能够兴旺,是在这一个层面上下功夫的,努力做一个好丈夫,努力做一个好妻子,努力让自己的家族持续兴旺。

不要以为世界很大,其实你只要处理好身边9个人左右的关系,就基本上能够搞定你的小世界了

你的商业合作伙伴、你的直接领导、你的工作同事、你的朋友圈、你的家人和另一半,以及你的内心世界

能做什么

三步走:先生存,后发展,再实现自我

“探索阶段、学习阶段、适应阶段、收获阶段、完成阶段”五个步骤在拆解成长路线图。

生存关冲发展关的20%进度:探索阶段;生存关冲发展关的40%进度:学习阶段;生存关冲发展关的60%进度:适应阶段;生存关冲发展关的80%进度:收获阶段;生存关冲发展关的100%进度:完成阶段。

即便你再怎么愚笨,花上两三年时间只专注努力打通一个阶段,都可以让自己逐步变强。当你以这样的方式来思考十年维度的时候,就会发现,其实十年规划、五年规划、一年规划合在一起,都是在讲同一件事情——改变人生命运

用Excel表格给自己DIY一套电子版本的《十年日记》表格文件,只要开12个sheet作为12个月,然后每一个sheet中画上10列作为10年,31行作为每一天,然后以不同颜色作底色,来标记自己的“发展周期”。

十年日记法设计人生蓝图

image-20230618075547805

设计当年的年历就更加简单,12列代表每个月,31行代表每一天,然后每三个月微调成一个背景色,于是一年四个季度的感觉就出来了。

我要做的所有目的无非是让自己开启上帝视角的全局感。我们想要得到的是:通过这些年的记录,我会察觉了什么,我发现了什么。

静下心来算出每一年的工资收入和非工资收入的总和,就是你一年的年收入了。连续三年记录,就可以计算出来,你这只“股票”的成长走势喜不喜人,当然我可以非常肯定地告诉大家,50%以上的人是横盘状态,年收入几年都不动。

对自己收入的不满意,一旦拉长到时间维度,痛苦系数就会被稀释掉,于是自己的感知能力就会被慢慢钝化,最后不得不趋于平凡。

如何知道自己忙得有没有价值?简单!

反向录入自己最近三年,将近1000多天前的自己都在干什么事情,而且把某一天的某一个行为,都看作是改变自己人生剧本的一个小小事件,如果你记录不了近三年,那么今年一年,或者今年上半年也可以。

如何监督自己的目标?简单!

举一个很简单的例子,当你在某一年的1月2日对天发誓,接下来的时间内,你要做什么事情的时候,请你先郑重其事地将自己要做的事情记录下来。于是你可以去反查去年在做同样事情时候的起心动念,以及准备行动之后,自己第二天,一个礼拜,半个月,一个月之后的表现。

假设我1月3日买了健身器材,或者某样学习资料,或者参加某个学习社团,那么我就会在十年日记上写下这样的话:“今天参加了×××活动,准备让自己有所改变。”

记得画几根箭头,分别指向3天后,10天后,30天后,50天后,100天后,半年后,看看自己是不是能够有所改变?或者在半年后的这一天事先标记一下:半年前的自己曾经发誓做什么事情,现在的我有感受到这个变化了吗?看看自己在持续行动,还是完全忘记自己在干什么了。

可以用Google Calendar或者QQ邮箱,写定时消息提醒,备注上自己的评论,循环5~10年提醒自己。

这预示着你现在的行动可以改变你未来的趋势,并且有持续的良性循环。这也是一个逐渐看清别人给你设定迷雾,自己戳穿自己谎言的识别过程,在这个过程中,你会重新估算自己的风险把控能力和自律性,以及自己对时间、资源、能力的把控边界值,是一个月,还是半年、一年。

记录下来自己在这半年中,学习了哪些知识体系,见了哪些高人,帮助了哪些朋友和陌生人,参加了什么样的活动,推进了什么样的人生主线任务。再用信息记录软件和其他各类软件做交叉验证,我们会对每一天自己有没有对自己负责的感知度越来越清晰。当你站在现在,给自己的未来设定“加持砝码”的时候,就会有一种未来自己给现在自己的一种期待,而且10天后的自己和50天后的自己,以及一年后的自己对现在自己的各种殷切期望,这种自己看得起自己的感觉,会在一次又一次的正向反馈中体现出来的。

某月某日的早晨8点出门,你会收到系统设定的循环提醒短信。

【社交】【短信】去年的这一天,你见了某个人,聊了关于什么的人生话题,这会对你的未来有什么样深远的影响吗?今天的自己打算怎么过?

【培训】【短信】一个月前参加了某某培训,今天的你有没有内化吸收,一个月后是否开始学以致用,来改变自己的命运呢?

【决策】【短信】半年前的自己决定开始做这件事情,当时的决定是绝不后悔,现在的自己状况如何,是不是依旧不后悔?

【技术】【短信】某某技术其实很难,重点是多写几遍,实现步骤是删除掉重新再来,反复做五遍以上,直到可以又快又好地完成,技术都是一点一点练出来的,别放弃,

加油!有些事情值得不值得?站在未来思考一下。

作者的规划demo

抑郁后两三年感觉人生已经完蛋了。

思考了以下问题:

  • 哪些是徒劳无用特别费时间的事情?
  • 哪些才是隐藏的主线任务?
  • 明天的自己是否会变得更加强大?
  • 我应该怎么做才能让明天的我变得更加强大一点点?
  • 如果今天状态不好,我能为后天的我准备哪些修炼材料?
  • 10天后的我是不是变强了一点?
  • 如果今天只能做一件改变我命运的事情,那么这个待办事项是什么?
  • 我现在做的这件事情,对我未来的一个月有用吗?
  • 一年前的我做了哪些蠢事?

“基于当下自己”的现状,开始积极自救的自我耕耘工作。参考时间坐标系从绝对坐标系的“年月日”逻辑,改用参考坐标系的百分比进度条逻辑,来对抗我暗无天日的灰暗人生。在独自修行的时候,是一个特别艰难的“至暗时刻”,憋着一口气咬牙熬过去,是特别不容易的,这时候鼓励特别重要。试着给不同时间段的自己,发送一封时间胶囊定时邮件,可以时隔一个月,时隔半年,时隔一年,自己给自己写邮件,然后自我督促,自我鼓励。

首先切换到“今天,明天,后天待办事项清单”,不断获得自己人生的三天自我控股权;其次不断用压力测试自己的能力上限和能力下限;再次保持节奏,让自我指挥权变得越来越强,逐步去做能够改变自己命运的事情;最后盘活自己的人生。

其实就是互联网产品的产品生命周期模型,一个阶段、一个阶段地对标,我每一个阶段发展到了哪里,如何相互验证,并且知道现有阶段应该做什么,不应该做什么。用心打磨好属于自己的核心产品,自然而然就会有越来越多的收益,让我能够养得起一个团队,同时一个团队也可以助力我加速产品的推广和传播。

第一阶段:好点子。

第二阶段:最小可行性模型。

第三阶段:好产品。

第四阶段:好产品+附属产品搭建产品组合。

第五阶段:有全职团队为我助力。

第六阶段:建立公司的规章制度。

年计划

为什么很多人的年计划都是错的

其实年计划分为以下几大类年计划:

  • 工作赚钱类计划 占比60%左右
  • 日常生活类计划 占比25%左右
  • 学习投资类计划 占比10%左右
  • 娱乐休闲类计划 占比5%左右

比如,以学习名义来逃避成长”的任务就会越多,这类“非收益型任务”会逐步抢占工作赚钱类任务的占比,时间一长就会出现你的收入越来越少

年计划的底层逻辑,是让自己变得更加强大的修炼过程。举一个工作中的例子,当你决定要从基层主管努力向项目经理方向发展的时候,这是一个一年到两年规划。这种从“不知道到知道”,从“不是某个能力到掌握某个能力”,从“资源少到资源多”的成长型修炼计划,才算是优秀的年计划。

战略落地规划

一个人的个人战略必须至少回答这三个重要问题

  • Where,我需要在哪里竞争?我是在现有的职场竞争,还是在未来的职场竞争,还是在家里赚钱? 家庭角色、工作角色、自我角色。需要在新的一年,用家庭角色赚钱,还是用工作角色赚钱,还是用自我角色赚钱,决定了这一年的赚钱方式。
  • How,我需要如何竞争?如何才能实现这样一个愿景? 当你决定在职场上发力的时候,就应该调配70%的主要精力在职场上,这包括你需要花费更多的时间扑在业务上,专业知识上,系统培训上,规章制度优化和团队建设上,你会较少参加社交,以及各种可有可无的娱乐活动,而不是全面开花。因为经过各种数据和信息推演,这样做的投资回报率是最高的,能够符合你近阶段收益,所以牺牲一两个月的社交和休息又能如何?
  • When,我什么时候竞争,未来三年到五年中你的身份是什么,分别怎么安排?关键里程碑的时间段是多少? 现在的身份有哪些,分别在社会、家庭、自我中的角色有哪些?我会在这个三年到五年时间内,将关键时间段专注在哪一个角色上,如何实施,如何微调和改进?这个实施会微调和改进持续多久,什么时间段会结束?哪些角色得做出让步,哪些角色得做出牺牲,需要你自己做好准备。

小栗子:给一个职能岗位普通的学员指点了一两小时,告诉他接下去怎么做才能破局,快速晋升到管理层,他所需要做的事情,就是系统整理自己所在工作岗位的各种操作说明书和培训手册,从此以后,他即便是约会见女朋友,都随身带着笔记本电脑来整理档案,最后整理出来的培训手册,光是目录就整整9页,为此吃了不少苦头,查阅了很多归档和资料,连约会等女朋友的时候都用电脑见缝插针地写文档,正是因为他想明白了要在管理岗位和技术岗位上发力,所以把以前看连载修仙类小说的时间都用来做行业技术规范的笔记整理了。80%的人受不了这个苦,最终选择了在当下安逸,选择追美剧,看日剧,而不去为未来风险做准备,因为要蹚过这个孤独修行的时间,需要500~1000小时的持续积累。

当你有了这样的觉悟,你会衡量现在的自己和未来的自己之间的不足和差距值,自己需要花费多少时间才能慢慢补全这方面的差距。

正因为有了7年的专业知识历练,才能有接下来的解决方案的经验值积累。

正因为有了经验值积累,才能有做项目为公司创收,为公司培养人才,制定规章制度的能力。

正因为有培养人才搭建团队能力,才会有一起合作共赢的机会。

正因为跟着你一起合作会共赢,才会有机会创业,去成就自己的梦想。

这是一个层层递进的过程,快不得,也偷懒不得。任意阶段的偷懒,都会让下一个阶段进阶无比困难。

当你理解了这个“战略”的意思之后,就明白了很多年度计划就是过家家瞎玩,根本就行不通。

想法如何变成待办事项

写下自己的内心念头

与外界主动隔绝掉你的听觉渠道和说话渠道的时候,你的内在噪声就会自动出现,然后满脑子各种杂音,各种胡思乱想的念头都会出现,这就是内在小孩在向自己发出请求的声音,不要担心,试着写下来自己的起心动念,自己的焦虑、想法、价值观,以及各种对自己的期望。

一开始肯定非常不习惯,你会非常抗拒这股内心噪声,但是当你静下心来,自由书写两三个小时,持续一个礼拜到一个月,直到写到心里面没有想法的时候,基本上你和内心小孩的会话完成了。

当你试着将这些碎碎“杂念”放在一处,搁置两三个礼拜之后再拿出来看,你就会发现,有些念头已经自动消亡了,而有些看上去重要的念头已经不怎么重要了,那么剩下重要的碎碎“杂念”,就有可能是需要你认真思考的。

加上动作,增强行为背后的意义

看着自己书写下来,天马行空的信息碎片,重新用相对规范工整的范式誊写到一个本子上,所有的短语结构用动宾关系结构微调,附上下一步动作来强化意义。

自由 -> 去巴黎街头喝咖啡,让我觉得很自由。

给几百条待办清单排序分类

哪一类事情属于理念原则类,这类任务解决应不应该做的问题;哪一类事情属于梦想愿景类,这类任务解决想不想做的问题;哪一类事情属于战略规划类,这类任务解决要不要去做,不做就不行的问题;哪一类事情属于年度计划类,这类任务解决今年能不能做,今年能不能搞定的问题;哪一类事情属于月度计划类,这类任务解决这个月做什么,下个月做什么,分别怎么做的问题;哪一类事情属于周计划类,这类任务解决这个礼拜要做掉哪些事情的问题;哪一类事情属于日计划类,这类任务解决今天得做什么,先做什么后做什么,什么重要什么不重要的问题。

image-20230618084902505

基于自身能力和资源,给待办事项进行排序分类

要做的事情有很多,但是可以调用的资源却很少;要耗费的时间很多,但是能用的时间很少;要享乐的时间太多,但是用于学习投资的时间太少;要完成简单难度的任务太多,但是用于提升能力的事情太少。

一旦思路切换到这种“设计自我人生”的状态了,就不会每天纠结于自己是不是胖了,是不是瘦了,今天吃什么,今天开不开心。

当大家遭遇到相同问题,当别人在抱怨的时候,你因为看过自己的未来剧本,所以会更加坚定自己在接受未来的考验而更加亢奋。这种感觉,就像是海量选拔的淘汰赛一样,一关一关会越来越难,不少人会知难而退,但是你会越来越兴奋,仿佛看到了修炼尽头胜利的曙光。

当你想明白自己缺什么,自己要什么,自己愿意付出什么的时候,你的自驱力将会得到突飞猛进的提升。

每年投入多少小时,决定你的人生

你一年能够用来学习的时间其实是有限的,而每年修炼多少时间,决定了你的毕业速度、成长速度、变现速度,以及你改变命运的速度。如果你持续四五年无法通过学习提升自己的话,很有可能会慢慢落后,被自己所在行业的后来居上者所淘汰

如果你是一个工作稳定的上班族的话,每月可以抽出多少空闲时间?

每天2小时,每月25天左右的时间,于是你每月可以抽出50个小时留给自己系统的学习,那么一年下来也就是:12个月的50小时充电学习时间,总计600小时。即使你每天投入3小时的高质量学习,一年也就是900小时。毕竟你还得处理工作和生活两方面的事情。

经过了这一年累计600~900小时的修炼之后,你会有什么样的变化?你的经验值,你的专业能力是否可以帮助你得到更好的工作岗位?这就是修炼的意义所在。

如果你以前的年收入只是10万元,但是经过这一年的修炼之后,你的年收入变成了15万元的话,那么这600小时的坚持修炼就值5万元平均每小时的修炼至少值83.3元。一旦用投资回报率的思维方式来思考这方面的内容之后,你就知道你为什么要努力了,以及每小时时间成本背后的代价了。

这样的投资回报率思维,反向计算分析你自己的年度计划的时候,有没有也是这样认真思考这么一个问题的呢?什么样的知识体系,值得让你学习600小时以上?并且能在未来给你带来“神助攻”一样的加速进度?

月计划

统筹调度。有没有效果,基本上一个月就见分晓了。

在这个月中,你的收入是多少钱?你的固定开支是多少钱?你要还多少钱?你还剩余多少钱?你准备怎么支配这些剩余的钱?在这个月中,你会工作多少天?有多少天加班?在这个月中,你会拜访多少个客户?你会帮助多少个人?有多少人帮助你?在这个月中,你了解了多少专业知识和解决方案?在这个月中,你和你的家人有效互动多少次?在这个月中,你收到了多少个快递?你收到了多少个别人送你的礼物?在这个月中,你出差了几次?去了几个城市?在这个月中,你请了多少人吃饭?多少人请你吃饭?在这个月中,你有多少时间自己独处?有多少时间在学习?

何谓任务思维?我这个月主要做什么?何谓底线思维?我必须完成什么!我们会看到很多人也会犯这样的毛病,什么不重要的准备工作都做了,唯独最重要的主线任务一点儿都没碰,最后还是白干,不光浪费了资源,也浪费了时间,最后还完不成任务。

用角色拆分法,来分解当月任务

工作角色,就得在新的一年里每天多工作两小时的时间。

家庭角色中,就得在新的一年里每天早出晚归少掉两小时的陪伴时间。

工作充电的角色,也得配合在新的一年,学习精进的专业知识来提升自己。

作为一个××××(角色),我想做×××(意图),这样我就可以得到××××(预期结果)。

作为A公司业务部门的中层干部,我想抽空周末加班完成某业务系统的标准化流程的优化,这样我就可以在后续几个月中,每月都可以提升20%的效率

一旦把这个公式厘清,就可以再追加上时间、成本、质量、进度、资源等相关要素,公式就会变得更加精准。作为一个××××(角色),我想做×××(意图),并且希望在××××(时间期限)内,以××××(多少资源投入)的成本下,完成这个任务,这样我就可以得到××××(预期结果)。

工作角色

作为一个A公司业务部门的领导角色,我需要在一年内培养更多的优质下属作为准备干部,来为完成部门目标扛下指标,于是我需要每周多出来××小时的时间,将我自己的成长经历和经验以案例式教学,来培养我的下属,让他们在短时间内变得更强。

作为一个A公司业务部门的经理角色,我需要和其他部门的经理达成资源互换的跨部门沟通的思维方式,以及利益共赢的战略同盟关系,所以我需要花更多的时间,来协助他们完成一些棘手的问题,由此我的项目组可以得到更多的“神助攻”

作为一个A公司业务部门领导的下属,我需要花费更多的时间,去领会和理解领导的意图,并且帮助领导处理掉一些没有必要的风险和雷区,这样领导就可以有更多的时间和精力去扛更多的指标,所以我需要学会更多战略能力和风险掌控力,而不是事必躬亲的“瞎折腾”。

家庭角色

作为配偶的角色,我每周的五个工作日少掉了10个小时的时间在陪伴家人上,那么作为相应的补偿机制,我就得在周末的家庭角色上,至少多承担些家务,以及和孩子陪伴的时间,在这个时间段,就不可以时时刻刻关注工作的事宜。

作为长辈的角色,我需要和孩子达成一个什么样的契约,虽然作为长辈少掉了几小时的陪伴孩子的时间,但是将会以阶段性的奖励来和孩子达成一起成长的相互见证。

作为晚辈的角色,我需要定期打电话给家人,嘘寒问暖,并且定期回家和长辈们保持沟通。

自我充电角色

拆分的类型

作为职场的角色,需要学习什么样的知识体系,是通过看书、学习、实践、沙盘推演,还是试错的方式来完成,具体怎么应用到自己工作上?

作为家庭角色需要学习什么样的相处方式,来增进家庭之间的和谐,或者弥补因为工作忙碌而产生对家人及孩子陪伴上的缺失,如何相处才是高质量的相处?

作为自己,需要学习怎么平衡这几个关系之间的角色,每天的时间、每周的时间怎么分配,这也是一个练习工作。

莫斯科法则排序

Mo-S-Co-W,是四个优先级别的首字母的缩写,发音谐音为“莫斯科”。

  • Must:必须做的;
  • Should:应该做的;
  • Could:可以做的;
  • Would not:不要做的。

日计划

1-3-5工作法

第一类事情是创造价值的工作,创作过程中不可以被他人干扰。

第二类事情是你需要和他人协同互动才能推进的工作,另一方不在无法推进。

第三类事情是附加值相对低的碎片任务,即使随时被打断都可以继续做的,并且有别于前两者必须你自己去做的事情,可以理解为番茄工作法。

  • 一个3小时的创造类任务 重要任务,亲自参与,而且不可以被打断,能改变你的命运,增加你工作经验的主线任务
  • 三个1小时的沟通类任务 次要任务,需要花费你1小时的时长,和他人互动配合完成,或者是需要你不断优化才能完成的效率、流程类任务
  • 五个0.5小时的琐碎任务 维持性、告知性、碎片任务。随时都可以被打断,然后随时都可以启动,并且是没有什么营养的例行性任务

优先面对面沟通,或者直接打电话说明白要做什么事情,然后双方沟通协商一致之后,再发一封邮件定为正式函,附上相关的表格和公文附件

如果一封邮件的涉及面达到三个以上的部门沟通的时候,最好的建议是先进行一对一关键人沟通,约定召开三方都可以参加的临时会议来梳理大家的想法、建议,下一步行动,注意风险是什么,讨论结果是什么,然后再发会议讨论的结果文书。

第一个阶段是观看。当你还不知道这一门知识体系的时候,你所需要做的最主要的事情就是像什么都不懂的小孩子,用眼睛看,对这个知识体系有一个初步的理解,在视觉认知上了解。这个阶段是中频思考,需要在一个相对安静的地方完成。

第二个阶段是记录。当你快速看了几遍之后,下一步你需要做的事情,就是开始变慢,这是一个思考和内化的过程。开始一笔一画记录知识点和内容的时候,是你理解和背诵知识点的过程,如果你理解不了,那么你也很难想明白这方面的问题。这个阶段是高频思考,不可以被干扰。

一般建议第一天晚上在家认真学习视频课程,第二天上班路上哪怕听音频复习

第三个阶段是思考。当你合上记录本、关掉电脑或放下手机的时候,才是你的大脑真正开始思考的阶段,因为你看到了对标知识,你也知道了背后的知识,下一步你需要去做的事情,就是思考和内化,以及开始用专家的思维方式来思考问题。这个阶段不可以被打扰,而且需要持续思考,才能够慢慢接近真相。这个阶段是高频深度思考,不可以被干扰。

第四个阶段是制订方案。这是一个积累经验的过程,你只有知道解决问题的多种可能性,才能根据具体情况设计出至少三四种不同方式的解决方案。这个阶段是中频深度思考,不可以被干扰。

第五个阶段是迭代优化,践行到底。当你有了自己的一套解决方案之后,你下一步需要去做的事情,就是不断校验自己的解决方案的可行性,哪一套适用于哪一类人,哪一套适用于这一类人群,只有这样你才可以把自己修炼成“瑞士军刀款”的专业人才。这个阶段是低频深度思考,随时随地都可以。

复盘

针对以专业知识学习为主的学习方式,自己这个月的主要目标是掌握多少个知识点,记录多少页专业书籍的读书笔记。我曾经和学习专业的学员说,先输出100页专业书的读书笔记再说,先别想太多。

针对以打市场为主的学习工作方式,自己这个月的主要目标是认识拜访多少位客户,一共吃多少顿饭,请客户喝多少次下午茶,出差拜访多少个城市。我曾经和销售学员说,你先半年不回家陪家人吃晚饭,这个量先做起来再说。

针对以管理为主的学习工作方式,自己这个月的主要目标是和上级进行多少次互动,优化公司多少流程表格,和下属进行多少次有效互动,帮助多少位下属提升工作战斗力,跨部门沟通多少次,举行多少次会议。

针对以操作为主的学习工作方式,自己这个月的主要目标是如何提升品质,降低损耗,增加客户的服务满意度,提升话术,优化转化率。

知识管理

成人学习和学校教育的区别

六个特点:

  • 目标明确:学会权衡收益与代价,付出与收获。
  • 独立自主:希望独立学习,自主思考,而不是被灌输。
  • 经验学习:带入以往的经验,具有特定的思维和习惯。
  • 功利性:意识到学习的必要性时才会准备投入学习,不喜欢被强迫。
  • 任务驱动:跟工作任务相关的学习,喜欢实践性主题。
  • 问题驱动:喜欢现实的问题解决,愿意学习解决思路。

废掉一个成年人到底有多简单:只要不停给他新知识,让他忙于学,却从不实践就可以了。

image-20230618105216529

知识价值的体现

决策判断 -> 超出常人的认知 ->进入直觉失效区域 -> 专业领域的知识 -> 总结和掌握规律 -> 不确定的因素转化为最优解决方案 -> 流程优化和资源配置改良 -> 企业获取竞争优势 -> 体现专业价值和社会价值 -> 换取财富资源

你是自己所在领域的专家吗?你的电脑里,你的书架上,是否有关于这个领域问题的10本以上书籍?你是否反复观看并分析对比再应用这些专家总结出来的方法呢?现在知道你收入高低的原因了吧?!

如何学进去专业知识

TED演讲《会“变形”的恐龙》老爷子给观众提出一系列非常经典的问题

  • 为什么会有这么多相似类型的恐龙命名?
  • 为什么大型恐龙没有童年时期的骨架化石?
  • 小型恐龙是不是生下来就是小型恐龙?
  • 为什么这么多的恐龙品种看上去很相似却又不一样?

其实恐龙的骨架是随着成长逐步变形的,以至于让人家觉得像是不同的品种一样,但是按照生长序列,其实是可以按照时间线还原出来的。那么为什么不按照年龄序列,来给所有的恐龙重新认祖归宗呢?

他差遣自己的科研小组找了几组实验用对象,给不同命名的恐龙化石的骨头做了横向切割,测试不同大小的恐龙的骨密度来了解这些恐龙的年龄,结果验证了他的假设,这些所谓看上去身材大小不一,命名不一的恐龙品种,其实就是同根同源的不同年龄段的同一个物种的恐龙而已。于是老爷子把已经灭绝的恐龙品种,又给灭绝了一大半。整个恐龙的种群族谱一下子清晰了很多。

站在这样的“上帝视角”去思考问题的时候,就不急于追逐最新的知识变化,而是先思考知识框架和分支脉络,以及不同分支之间的区别和关联,当看懂骨架之后,再来思考不同分支专业知识的应用场景。

当我开始按照物种生长周期和演化规律的方式,来看各个知识点之间的关联的时候,就会发现,其实知识点有它自己的布局和逻辑,而这个就是我们所说的知识地图和知识的生长序列。一旦看懂生长序列,你就可以知道具体学科的分支在哪里,相关专业知识之前的先修知识是什么,以及跨学科应用的场景在哪里。

如何反向渗透一个专业知识体系

逆向工程

有经验的工程师看一眼某一样东西,就可以在脑子中用想象力把目标对象拆解成支离破碎的结构程度,这样的反向拆解能力可以理解为“逆向工程思维”。

第一步:找到需要拆解的目标对象

如果我要成为软件开发的架构师,我应该掌握多少知识体系?如果我要成为上市公司的人力资源高管,我应该学会哪些专业知识?如果我想要让我女儿通过中考,中考英语会有多少条英语语法?如果我想让我儿子高考英语拔高10分,高考英语词汇量有多少?如果我要进入管理层,要达到雅思7.5分,至少要掌握多少词汇和短语?

能够达到这种程度,得学习哪些专业书籍,你能想办法找到这些书籍吗?找到这些专业书,然后认真学习这些专业书中的书籍目录、知识点和相关案例。

第二步:深度拆解并排序

拆解到自己能够理解的程度。无聊至极的过程,但是它会让你站在上帝视角重新对某一样完整的事物有分解的透视感。进入这个阶段的时候,就会开始从专业知识有什么,开始慢慢转化到专业知识我会多少的排序阶段。

第三步:归纳总结,为我所用

实战阶段,我可以用多少种方法解决这个难题?我可以用多少步骤解决这个问题?我可以用哪几种工具搞定这个问题?我可以用多少成本搞定这个问题?我应该怎么做,能搞定10个以上类似的问题?我应该怎么做,让这类问题的发生率降低10倍以上?

  • Copyrights © 2023-2024 Larry Wang
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信