问题描述

在使用LibreOffice的Calc组件将电子表格导出为PDF文件时,如果启用了“Whole sheet export”(整页导出)选项,导出的PDF文件中的超链接将不会保留原始的URL,而是显示为本地文件路径。

这个问题在

  1. LibreOffice 官方论坛上的提问
  2. Stack Overflow 上的提问

上都有讨论。

解决思路

阅读全文 »

将Microsoft Office文件转换为其他格式的场景

在一些情况下,可能需要将Microsoft Office文件转换为其他格式:

  • 兼容性问题:与不同的办公软件或操作系统进行交互,可能需要将MS Office文件转换为更通用的格式。例如,如果要与没有安装Microsoft Office的人分享文档,将其转换为PDF格式可能更合适。
  • 归档和存档:将Office文件转换为更稳定、可持久保存的格式可以确保文件的长期保存和归档。某些文件格式(如PDF/A)专门用于长期存档目的,以确保文件内容的完整性和可访问性。
  • 数据提取:你可能只对文档中的特定数据或内容感兴趣。通过将Office文件转换为其他格式(如纯文本或CSV),可以更容易地提取所需的数据,并在其他应用程序中进行分析或处理。
  • 网页发布:如果要将MS Office文件发布到网页上,可能需要将其转换为HTML或其他网页友好的格式,以确保文件在网页上正确显示。

LibreOffice - 微软Office的开源替代

LibreOffice是一个免费、开源的办公套件,在某种程度上可以被视为微软Office的开源替代品。

  • LibreOffice Writer:对应于Microsoft Word。
  • LibreOffice Calc:对应于Microsoft Excel。
  • LibreOffice Impress:对应于Microsoft PowerPoint。
阅读全文 »

第一个问题:在多个线程中同时运行隔离级别为serializable的事务而导致的无法重试获取锁的问题

Spring Integration JDBC分布式锁的实现会需要使用一个serializable级别的事务来获取锁

如果多个线程同时尝试获取锁,这些事务之间可能会出现顺序问题。

具体而言,可能会遇到以下错误:

1
org.postgresql.util.PSQLException: ERROR: could not serialize access due to read/write dependencies among transactions

发生这样的问题其实也不可怕,因为JDBC锁会进行重试。

阅读全文 »

在一个Kubernetes(K8s)集群中,部署了Prometheus和Grafana用于监控集群本身和应用的状态。

在其中一个Java应用对应的Pod级别观察到了内存上升的现象。具体而言,当该应用刚启动时,内存占用并不高。如果不发送请求给应用,内存将保持在启动时的水平上。

如果大量发送请求给应用并在短时间内持续发送,内存会迅速增加。这在一定程度上是正常的。

一旦内存增加之后,即使停止发送请求和压力,内存使用也不会下降,一直保持在高峰水平。

上面的状况是由Grafana中观察到的。

观察到的现象看起来像是内存泄漏,但实际上并不一定是内存泄漏。

阅读全文 »

最近在项目中需要使用Spring Integration提供的基于JDBC实现的分布式锁。

在实践的过程中,我们遇到了一些有趣的问题,现在在此记录和总结一下。

一共遇到了两个问题,第一个和time to live有关,第二个还是和time to live有关。

第一个问题:由于time to live默认值不够长而导致被动失去锁的问题

sequenceDiagram

actor event_initiator
participant instance_1
participant instance_2

event_initiator->>instance_1: do something

note over instance_1: instance 1 获得了 lock

instance_1->>instance_1: start doing its thing

event_initiator->>instance_2: do another thing

note over instance_2: instance 2 等待 lock
note over instance_2: 等 ......
note over instance_2: 等 ......

note over instance_1: lock的超时时间TTL到,instance 1还没干完活,但是它失去了 lock
失去不同于主动release
失去lock后,instance 1还会继续干活
而这些活里面可能会有SQL写操作 note over instance_2: instance 2 获得了 lock instance_2->>instance_2: start doing its thing note over instance_1,instance_2: 此时二者同时干活,有撞车的风险,因为二者干活的先后顺序没有保证
instance 1尚未把它干完活后才能确定的状态写入DB,而instance 2已经开始干活了 note over event_initiator,instance_2: 为了降低风险,可以:
① 想办法尽量让instance 1能在超时前干完活
② 以防万一可以考虑在合适的时间节点延长锁的过期时间

根据上图所示,我们有两个实例。

阅读全文 »

一份有意思的代码

最近看到了一份使用随机生成的数据作为测试输入的有趣代码,把其大致思路用伪代码描述如下

需要被测的实现代码

1
2
3
4
5
function calculateSomething(inputData) {
// 使用inputData来计算结果
// 假装这里有一些很复杂的逻辑
return result;
}

这是被测的函数,在此不管它算的是什么,总之它接受input,返回result。

测试代码的helpers

阅读全文 »

此书讲什么

马克思《资本论》讲的三件事:

  • 原始积累靠暴力
  • 贫富差距会扩大
  • 最终自毁

经济与政治

经济学是有阶级属性的。

阅读全文 »

金本位时期金融机构的层级结构

19th century nation state financial institution hierarchy

Untitled

央行的负债≈银行的资产≈货币

银行的负债≈私营部门的资产≈存款

亏钱先亏哪儿?

阅读全文 »

2007年,我在读大二。

当时经常会去学校食堂对面的报刊亭买杂志,一本,是《大众软件》,另一本,是《程序员》。

印象中当时《程序员》上的多数文章充满了我没听过的各种缩写与稀奇古怪的名词,文风是老成持重,我看不懂,但很是佩服。

而这当中偶尔会夹杂着几篇文风犀利,睥睨天下的文章,加上作者头像很是非主流,我虽也看不懂,但印象深刻。
这些犀利文章的作者,便经常冠有TW的头衔。

2012年,我在一家小软件公司上了两年的班。
当时我们每半年发布一个版本,每到要发布前夕,程序员便都停止写代码,去做回归测试。

阅读全文 »

引子

工业化、现代化、城市化、民主化、自由化、市场化、全球化,不一定要姓“西、资、基”。

第一章

五常的否决权是对大国毁灭性力量的承认。核武器,氢弹,洲际导弹,核潜艇,多弹头分导。

退缩不会让狼群放弃供给,逃跑不会让狼群放弃追捕,倒下不会让狼群放弃杀戮。

大国没资格投降。

阅读全文 »

泡水膨胀球

drawing

这是一种小玩具,干燥时拿在手里,小巧紧致。

泡在水里,一段时间之后,浑圆饱满,一只手都未必能捧的住。

放在干燥通风的地方晾晒,一段时间过后,又可以恢复原本的大小。

软件需求

阅读全文 »

前言

分布式键值存储(Distributed Key-Value Store)并不是一个新鲜的玩意。
常见的Redis,Memcache等等都有很多人在用。

不过如果要是说其中的细节,如replicate,读写,一致性,retry等等的话,又是经常会遇到把自己绕的混淆不清的情况。。
恰巧我最近在看的一门网课《Programming Reactive Systems》中有一道作业题就是要自己实现一个Distributed Key-Value Store,那就正好借此机会详细写下其中的关键点。

组成系统的参与者

既然是分布式键值存储,那么肯定会有主从节点,每个结点又会有自己的持久化,而主从之间也需要协调,于是就得出了如下关键参与者:

  • Primary: 主节点。接受来自client的更新(增/删/改)操作,并把更新后的数据扩散到其它节点。当然,也可以接受来自于client的读操作。
  • Secondary: 从节点。接受来自主节点的更新操作。接受来自client的只读操作。
  • Arbiter: 仲裁者。任何节点,无论主从,都要把自己注册到Arbiter上去。当有从节点加入或者离开集群的时候,Arbiter负责告知主节点。
  • Persistence: 每个节点都拥有自己的独享的Persistence。用于把节点上的数据持久化。
  • Clients: 客户端,可能与主或者从节点通信,进行各种读写操作。
阅读全文 »

Scala里有一个很有趣的语言特性叫做Self Type,可以用来限定一个trait可以被mixin到哪里去。

看个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
trait User {
def username: String
}

trait Tweeter {
self: User =>
def tweet(tweetText: String) = println(s"$username: $tweetText")
}

class VerifiedTweeter(val username_ : String) {
def username = s"real $username_"
}

object SelfTypeBlog {
def main(args: Array[String]): Unit = {
val realBeyoncé = new VerifiedTweeter("Beyoncé") with User with Tweeter
realBeyoncé.tweet("Just spilled my glass of lemonade")
}
}

User就仅仅相当于一个Interface,定义一个username。

Tweeter内的第一行是重点 self: User => 就限定了Tweeter只能被mixin到实现了User的类里面去。
由于可以确定Tweeter只能被mixin到实现了User的类里面去,这样Tweeter的tweet方法内就可以放心大胆地用 username 了。

VerifiedTweeter是一个很普通的class,别人new它的时候给什么字符串,它的username就是啥。

阅读全文 »

先看这么一段Scala代码:

1
2
3
4
5
6
7
8
9
10
11
object PFBlog {

def usePF(pf: PartialFunction[Option[Int], Int]) = {
pf(Some(11))
}

usePF {
case Some(x) => x + 1
case None => 0
}
}

声明一个usePF方法,接受一个PartialFunction作为参数,它的实现就是传一个 Some(11) 给pf。
pf的具体类型是 PartialFunction[Option[Int], Int] 所以传递给它一个Some(11)可以期待它会返回一个Int。

然后调用usePF,传递给usePF的是一个pattern match表达式。给Some加一,给None返回0。

这时问题就来了,这个pattern match表达式是怎么能够符合usePF需要的参数类型的呢?
这么单纯的一个pattern match表达式怎么变成PartialFunction[Option[Int], Int]的呢?

为了探寻答案,先把这几行Scala代码编译成class文件,然后把byte code反编译成Java来一探究竟吧。

阅读全文 »

我们来一起看一下两个程序员之间的故事。

以下示例代码是用Scala写的,不过本文所讲的话题并不仅限于Scala,任何有Future/Promise支持的语言都是适用的。
下面这个wiki页面罗列了各个有Future/Promise支持的语言,已经涵盖了大多数的常用语言。
Future与promise实现列表

我是异步函数的编写者

我写了两个异步函数,来提供给其他程序员同事使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
type CallBack = Try[String] => Unit

def pretendCallAPI(callBack: CallBack, okMsg: String, failedMsg: String) = {
val task = new TimerTask {
override def run() = {
val percentage = Random.between(1, 100)

if (percentage >= 50)
callBack(Success(okMsg))
else if (percentage <= 30)
callBack(Failure(new Exception(failedMsg)))
else
callBack(Failure(new Exception("network problem")))
}
}

new Timer().schedule(task, Random.between(200, 500))
}

val searchTB = pretendCallAPI(_, "product price found", "product not listed")
val buyFromTB = pretendCallAPI(_, "product bought", "can not buy, no money left")

这两个异步函数: searchTB用来从淘宝搜索物品,另一个buyFromTB用来购买搜到的物品。

阅读全文 »

原文地址: https://blog.cleancoder.com/uncle-bob/2018/04/02/InTheLarge.html

原作者:Robert C. Martin (Uncle Bob)

自敏捷之开端始,我们就思考大规模敏捷的问题。我们是否能够把轻量级,迭代,增量,快速反馈等软件开发的原理应用于规模巨大的项目呢?

最初我们想到的答案是Scrum of Scrums之类的东西。这个想法是在更高的层次上递归地应用敏捷开发的原理。如果一个项目需要超过5-12个开发人员,那么可以组织两个这样的团队,以及一个更高级别的团队来“监督?”他们。

请注意上面的问号。当我们开始考虑大型项目时,我们不可避免地要谈及层级化的组织。但是敏捷似乎是厌恶组织层级的。毕竟,敏捷就是关于平等主义的。敏捷是拒绝命令和控制的。敏捷是拒绝计划和时间表的,还有…

无稽之谈!并不是这样的!

阅读全文 »

原文地址:https://blog.cleancoder.com/uncle-bob/2018/04/13/FPvsOO.html

原作者:Robert C. Martin (Uncle Bob)

在过去的几年中,我通过与人结对来学习函数式编程,他们中的很多人表达了反对OO的偏见。他们经常会说:“啊,这太像对象了。”

他们会这样说是因为他们认为FP和OO在某种程度上是互斥的。许多人似乎认为程序FP的程度等同于其非OO的程度。我认为这种观点是学习新事物的自然结果。

当我们采用一种新技术时,我们通常倾向于避开以前使用的旧技术。这很自然,因为我们认为新技术“更好”,因此旧技术就一定是“更糟”的。

在此博客中,我将说明OO和FP是正交的,但它们并不互斥。一个好的函数式程序可以(并且应该)是面向对象的。而且一个好的面向对象程序可以(并且应该)是函数式的。在此之前,我们必须非常谨慎地给FP和OO这两个词语下个定义。

阅读全文 »

身陷南洋囹圄,心系故园风物,无以聊赖之中,撰文权作慰藉。

从Neal Ford的一段话讲开去

前段时间看了Neal Ford的一个演讲,原视频

其中一段讲到了传统工程与软件工程的区别。

於我心甚有戚戚焉。

译如下:

阅读全文 »

最近快速浏览完了太祖的五卷选集。粗浅的尝试着总结一下太祖做事情和分析问题解决问题时都会运用一些什么样的套路。

性质决定形式

革命不是请客吃饭,不是做文章,不是绘画绣花,不能那样雅致,那样从容不迫,文质彬彬,那样温良恭俭让。革命是暴动,是一个阶级推翻一个阶级的暴烈的行动。


大家明白,不论做什么事,不懂得那件事的情形,它的性质,它和它以外的事情的关联,就不知道那件事的规律,就不知道如何去做,就不能做好那件事。


阅读全文 »

第一章一个好点子出炉了

基本的经济原则,这个原则可以提高人们的生活水平:消费不足,敢于冒险!

在经济学术语中,资本指的是一种设备,这种设备的建设和使用本身没有什么意义,其意义在于利用设备建设和制造其他需要的东西。艾伯想要的不是那张网,而是鱼。这张网或许可以给他带来更多的鱼。因此,这张网就是一种资本,是有价值的。

在艾伯孤注一掷去编织渔网之前,小岛上还没有什么储蓄之所。他决定冒险挨饿制造的这张网成了小岛上的第一件资本设备。这件设备接着会带来储蓄(为了让这个故事能够继续进行,我们假设这些鱼不会腐烂变质),而这种剩余产品就是健康经济的命脉。

工具改变了一切,使经济的出现成为可能。长矛帮助我们捕获猎物,铲子帮助我们种植庄稼,渔网帮助我们捕鱼。这些工具提高了我们的劳动效率。我们生产的东西越多,可以消费的东西就越多,我们的生活也就变得越美好。

努力使有限的资源(每种资源都是有限的)产生最大的效益以尽可能满足人类的需求,这就是经济这一概念最简单的定义。工具、资本以及创新是实现这一目标的关键。

阅读全文 »

随着持续集成,持续交付等理念的传播,很多软件开发团队都搭建了自己的staging、UAT等类生产环境。这些环境的软硬件及网络配置会尽量贴近真实的生产环境,起到沙盘演练的作用。

类生产环境毕竟前面还有一个类字,沙盘毕竟不是真实的战场,尽量贴近毕竟还不是完全吻合。

类生产环境与真实生产环境的一个重要差异就是访问量。稍具规模的互联网应用每天几百万访问量是很正常的,而类生产环境的访问量一般都会相形见绌。

有各种工具可以弥合这个差异,比如Apache JMeter,Gatling。测试人员可以和开发人员一起设计测试用例,以自动化或者半自动化的方式对类生产环境进行压力测试

不过即便是精心设计出来的用例也还是用例,不是真实请求。真实请求具有多样性,会随着昼夜交替而变化,会随着时事热点而波动,这是很难用工具模拟出来的。

这就引出了这篇文章的主角-影子流量(shadow traffic)。

阅读全文 »

第一部分科技颠覆

20世纪,来自纽约、伦敦、柏林和莫斯科的全球精英讲述了三大故事,号称能够解释人类过去、预测全球未来。这三大故事是:法西斯主义故事、共产主义故事,以及自由主义故事。第二次世界大战打倒了法西斯主义故事,于是从20世纪40年代末到80年代末,世界成为共产主义故事和自由主义故事的战场。等到共产主义受挫,自由主义故事就成为人类了解过去的主要指南、未来无法取代的使用手册

1938年,人类有三种全球性的故事可以选择;1968年只剩下两个;1998年,似乎只有一个故事胜出;2018年,这个数字降到了0。

过去推动俄国、中国和古巴革命的,是一群对经济至关重要但缺乏政治权力的人;而2016年,支持英国脱欧和特朗普的,却是一群虽然还享有政治权力却担心失去经济价值的人。

自由主义分别学习了帝国主义、法西斯主义和共产主义某些最优秀的概念。

自由主义学习了共产主义,于是扩大了同理的范围,开始在重视自由之外也同时重视平等。

即使在第二次世界大战之后,西方自由主义者还是很少将他们所谓的共通价值应用到非西方人民的身上。所以,荷兰人在自己的国土被纳粹残酷占领5年,于1945年重新站起来之后做的第一件事,就是召集军队横跨半个地球,希望重新占领前殖民地印度尼西亚。尽管在1940年,荷兰人只战斗了4天就举手投降放弃独立地位,但为了压制印度尼西亚的独立,他们却鏖战了4年之久。这也就难怪全球许多民族解放运动所寄望的都是苏联和中国,而不是自诩为自由主义领导者的西方国家。

阅读全文 »

一首绝句作为定场诗,接下来开始正式的内容。

最近,我在网上进行了一些稀奇古怪的操作,于是乎我莫名其妙的得到了一个“众邦银行”的账户。

这个名字并不是很熟悉,查了一下,看起来还算是有头有脸的样子。

武汉众邦银行股份有限公司(简称武汉众邦银行,英语:Wuhan Zhongbang Bank Co., Ltd.缩写Z-BANK)是中华人民共和国境内第11家开业的民营银行,也是湖北省首家获批开业的民营银行。

以上是wiki的信息。

阅读全文 »

Stackable traits是一种怎样的特性呢?

来举一个🌰

1
2
3
4
abstract class IntQueue {
def get(): Int
def put(x: Int)
}

定义一个IntQueue,抽象类,定义了get和put,没有实现。

1
2
3
4
5
6
7
8
9
class BasicIntQueue extends IntQueue {
private val buf = new ArrayBuffer[Int]

def get() = buf.remove(0)

def put(x: Int) = {
buf += x
}
}

再定义一个BasicIntQueue,把上述IntQueue实现了。
它的实现没有什么花样,就是先进先出。

阅读全文 »