Nicksxs's Blog

What hurts more, the pain of hard work or the pain of regret?

很久之前就听说过这个框架,不过之前有点跟消息队列混起来,这个也是种队列,但不是跟 rocketmq,nsq 那种一样的,而是在进程内部提供队列服务的,偏向于取代ArrayBlockingQueue,因为这个阻塞队列是使用了锁来控制阻塞,关于并发其实有一些通用的最佳实践,就是用锁,即使是 JDK 提供的锁,也是比较耗资源的,当然这是跟不加锁的对比,同样是锁,JDK 的实现还是性能比较优秀的。常见的阻塞队列中例如 ArrayBlockingQueueLinkedBlockingQueue 都有锁的身影的存在,区别在于 ArrayBlockingQueue 是一把锁,后者是两把锁,不过重点不在几把锁,这里其实是两个问题,一个是所谓的 lock free, 对于一个单生产者的 disruptor 来说,因为写入是只有一个线程的,是可以不用加锁,多生产者的时候使用的是 cas 来获取对应的写入坑位,另一个是解决“伪共享”问题,后面可以详细点分析,先介绍下使用
首先是数据源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class LongEvent {
private long value;

public void set(long value) {
this.value = value;
}

public long getValue() {
return value;
}

public void setValue(long value) {
this.value = value;
}
}

事件生产

1
2
3
4
5
6
7
public class LongEventFactory implements EventFactory<LongEvent>
{
public LongEvent newInstance()
{
return new LongEvent();
}
}

事件处理器

1
2
3
4
5
6
7
8
9
10
11
12
public class LongEventHandler implements EventHandler<LongEvent> {

// event 事件,
// sequence 当前的序列
// 是否当前批次最后一个数据
public void onEvent(LongEvent event, long sequence, boolean endOfBatch)
{
String str = String.format("long event : %s l:%s b:%s", event.getValue(), sequence, endOfBatch);
System.out.println(str);
}
}

主方法代码

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
package disruptor;

import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.util.DaemonThreadFactory;

import java.nio.ByteBuffer;

public class LongEventMain
{
public static void main(String[] args) throws Exception
{
// 这个需要是 2 的幂次,这样在定位的时候只需要位移操作,也能减少各种计算操作
int bufferSize = 1024;

Disruptor<LongEvent> disruptor =
new Disruptor<>(LongEvent::new, bufferSize, DaemonThreadFactory.INSTANCE);

// 类似于注册处理器
disruptor.handleEventsWith(new LongEventHandler());
// 或者直接用 lambda
disruptor.handleEventsWith((event, sequence, endOfBatch) ->
System.out.println("Event: " + event));
// 启动我们的 disruptor
disruptor.start();


RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();
ByteBuffer bb = ByteBuffer.allocate(8);
for (long l = 0; true; l++)
{
bb.putLong(0, l);
// 生产事件
ringBuffer.publishEvent((event, sequence, buffer) -> event.set(buffer.getLong(0)), bb);
Thread.sleep(1000);
}
}
}

运行下可以看到运行结果

这里其实就只是最简单的使用,生产者只有一个,然后也不是批量的。

scp 是个在服务器之间拷贝文件的一个常用命令,有时候有个场景是比如我们需要拷贝一些带有共同前缀的文件,但是有一个问题是比如我们有使用 zsh 的话,会出现一个报错,

1
zsh: no matches found: root@100.100.100.100://root/prefix*

这里就比较奇怪了,这个前缀的文件肯定是有的,这里其实是由于 zsh 会对 * 进行展开,这个可以在例如 ls 命令在使用中就可以发现 zsh 有这个特性
需要使用双引号或单引号将路径包起来或者在*之前加反斜杠\来阻止对*展开和转义

1
scp root@100.100.100.100://root/prefix* .

通过使用双引号"进行转义

1
scp root@100.100.100.100:"//root/prefix*" .

或者可以将 shell 从 zsh 切换成 bash

前阵子一个同事因为发现某个分支上的代码好像有缺失导致无法正常运行,然后就对比了下把缺失的代码从另一个分支上拷了过来,可能有所欠考虑,不过主要是说下操作过程和最后的处理方法,这位同学的操作是改一些代码commit 一下,这样的 commit 了大概五六次,并且已经 push 到了远端,然后就在想要怎么去处理,在本地可以 reset,已经到远端了,一个很不优雅的操作就是本地 reset 了用 force push,这个当然是不可取的,然后就是 revert 了,但是又已经 commit 了好几次了,网上看了下,好像处理方法还挺成熟的,git revert 命令本质上就是一个逆向的 git cherry-pick 操作。 它将你提交中的变更的以完全相反的方式的应用到一个新创建的提交中,本质上就是撤销或者倒转。可以理解为就是提交一个反向的操作,这里其实我们可以用range revert来进行 git revert, 用法就是

1
git revert OLDER_COMMIT^..NEWER_COMMIT

这样就可以解决上面的问题了,但是还有个问题是这样会根据前面的 commit 数量提交对应数量个 revert commit 会显得比较乱,如果要比较干净的 commit 历史,
可以看下 git revert 命令说明

然后就可以用 -n 参数,表示不自动提交

1
2
git revert -n OLDER_COMMIT^..NEWER_COMMIT
git commit -m "revert OLDER_COMMIT to NEWER_COMMIT"

spring框架中如果想使用一些一部操作,除了依赖第三方中间件的消息队列,还可以用spring自己的event,简单介绍下使用方法
首先我们可以建一个event,继承ApplicationEvent

1
2
3
4
5
6
7
8
9
10
11
12
13

public class CustomSpringEvent extends ApplicationEvent {

private String message;

public CustomSpringEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}

这个 ApplicationEvent 其实也比较简单,内部就一个 Object 类型的 source,可以自行扩展,我们在自定义的这个 Event 里加了个 Message ,只是简单介绍下使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public abstract class ApplicationEvent extends EventObject {
private static final long serialVersionUID = 7099057708183571937L;
private final long timestamp;

public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}

public ApplicationEvent(Object source, Clock clock) {
super(source);
this.timestamp = clock.millis();
}

public final long getTimestamp() {
return this.timestamp;
}
}

然后就是事件生产者和监听消费者

1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class CustomSpringEventPublisher {

@Resource
private ApplicationEventPublisher applicationEventPublisher;

public void publishCustomEvent(final String message) {
System.out.println("Publishing custom event. ");
CustomSpringEvent customSpringEvent = new CustomSpringEvent(this, message);
applicationEventPublisher.publishEvent(customSpringEvent);
}
}

这里的 ApplicationEventPublisher 是 Spring 的方法接口

1
2
3
4
5
6
7
8
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
this.publishEvent((Object)event);
}

void publishEvent(Object var1);
}

具体的是例如 org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType) 中的实现,后面可以展开讲讲

事件监听者:

1
2
3
4
5
6
7
@Component
public class CustomSpringEventListener implements ApplicationListener<CustomSpringEvent> {
@Override
public void onApplicationEvent(CustomSpringEvent event) {
System.out.println("Received spring custom event - " + event.getMessage());
}
}

这里的也是 spring 的一个方法接口

1
2
3
4
5
6
7
8
9
10
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E var1);

static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {
return (event) -> {
consumer.accept(event.getPayload());
};
}
}

然后简单包个请求

1
2
3
4
5
6

@RequestMapping(value = "/event", method = RequestMethod.GET)
@ResponseBody
public void event() {
customSpringEventPublisher.publishCustomEvent("hello sprint event");
}


就能看到接收到消息了。

又是一年年终总结,本着极度讨厌实时需求的理念,我还是 T+N 发布这个年终总结

工作篇

工作没什么大变化,有了些微的提升,可能因为是来了之后做了些项目对比公司与来还算是比较重要的,但是技术难度上没有特别突出的点,可能最开始用 openresty+lua 做了个 ab 测的工具,还是让我比较满意的,后面一般都是业务型的需求,今年可能在业务相关的技术逻辑上有了一些深度的了解,而原来一直想做的业务架构升级和通用型技术中间件这样的优化还是停留在想象中,前面说的 ab 测应该算是个半成品,还是没能多走出这一步,得需要多做一些实在的事情,比如轻量级的业务框架,能够对原先不熟悉的业务逻辑,代码逻辑有比较深入的理解,而不是一直都是让特定的同学负责特定的逻辑,很多时候还是在偷懒,习惯以一些简单安全的方案去做事情,在技术上还是要有所追求,还有就是能够在新语言,主要是 rust,swift 这类的能有些小玩具可以做,rust 的话是因为今年看了一本相关的书,后面三分之一其实消化得不好,这本书整体来说是很不错的,只是 rust 本身在所有权这块,还有引用包装等方面是设计得比较难懂,也可能是我基础差,所以还是想在复习下,可以做一个简单的命令行工具这种,然后 swift 是想说可以做点 mac 的小软件,原生的毕竟性能好点,又小。基于 web 做的客户端大部分都是又丑又大,极少数能好看点,但也是很重,起码 7~80M 的大小,原生的估计能除以 10。
整体的职业规划貌似陷入了比较大的困惑期,在目前公司发展前景不是很大,但是出去貌似也没有比较适合我的机会,总的来说还是杭州比较卷,个人觉得有自己的时间是非常重要的,而且这个不光是用来自我提升的,还是让自己有足够的时间做缓冲,有足够的时间锻炼减肥,时间少的情况下,不光会在仅有的时间里暴饮暴食,还没空锻炼,身体是革命的本钱,现在其实能特别明显地感觉到身体状态下滑,容易疲劳,焦虑。所以是否也许有可能以后要往外企这类的方向去发展。
工作上其实还是有个不大不小的缺点,就是容易激动,容易焦虑,前一点可能有稍稍地改观,因为工作中的很多现状其实是我个人难以改变的,即使觉得不合理,但是结构在那里,还不如自己放宽心,尽量做好事情就行。第二点的话还是做得比较差,一直以来抗压能力都比较差,跟成长环境,家庭环境都有比较大的关系,而且说实在的特别是父母,基本也没有在这方面给我正向的帮助,比较擅长给我施压,从小就是通过压力让我好好读书,当个乖学生,考个好学校,并没有能真正地理解我的压力,教我或者帮助我解压,只会在那说着不着边际的空话,甚至经常反过来对我施压。还是希望能慢慢解开,这点可能对我身体也有影响,也许需要看一些心理疏导相关的书籍。工作篇暂时到这,后续还有其他篇,未完待续哈哈😀

0%