Spring Boot, Azure Database for MySQL, and Azure App Service – Part 1

I recently played with Java and Azure App Service. What I was trying to find out is how the development experience would look like for Java developers if they want to build their applications with Azure App Service and Azure Database for MySQL.

There are some documents on Microsoft doc site, such as this one. It might be good enough for an experienced Java developer, but for someone like me who has limit Java experience, it is not easy to follow, and the sample is also too simple to make any sense for a real development. So I decided to try it myself and documented my experience here for others to reference. There would be a series of posts, and this is the first one. 

Prepare the dev environment

So instead of installing IntelliJ or Eclipse, I choose to use VSCode as my Java IDE. On my computer I’ve already had the VSCode installed. According to this tutorial, I just need to install JDK and Maven. I am a bit lost with the Java terms like Java SE, JDK, JRE and their versions, but I don’t want to be bothered. I choose to install OpenJDK because Oracle JDK requires a license. So here are steps to install OpenJDK. 

  1. Download OpenJDK from here. Windows version of OpenJDK is a zip file. Unzip it to C:\Program Files\Java so the root fold of the JDK would be something like C:\Program Files\Java\jdk-11.0.1
  2. Add an environment variable JAVA_HOME, set its value to the root of the JDK, for example, C:\Program Files\Java\jdk-11.0.1
  3. Add C:\Program Files\Java\jdk-11.0.1\bin to the system path. 
  4. With the above steps, OpenJDK is installed completely. To test if it works, open a command window and run java -version. It should print out the OpenJDK version and runtime information. 

When OpenJDK is installed, you can follow the vscode tutorial to download and install maven, and the Java Extension Pack for vscode. 

Create a MySQL database

Instead of installing MySQL on my local computer, I choose to create an Azure Database for MySQL instance as the dev database environment. It is easy to provision an Azure Database for MySQL instance. Azure has quick start for it. I also run the following SQL query to configure the database in Azure Cloud Shell. 

CREATE DATABASE tododb; -- Create a database
CREATE USER 'springuser'@'%' IDENTIFIED BY 'Spring1234'; -- Create a database user
GRANT ALL PRIVILEGES ON tododb.* TO 'springuser'@'%'; -- Grant user permissions to the database
FLUSH PRIVILEGES;

With the above preparation, we have a Java development environment and a MySQL database ready for the development. In the next post, I will start to create a Spring Boot REST API app with VSCode. Stay tuned. 

Upgrade Ubuntu Server From 16.04 to 18.04.1

I have received several notifications from my Ubuntu server running in Azure for asking me to upgrade the server to Ubuntu 18.04.1. When Ubuntu 18.04 was first released, I didn’t upgrade the server. I was afraid there could be compatibility issues and I don’t want to break the server. With the release of 18.04.1, it seems the version is stable enough for an upgrade. So I decided to upgrade the server. 

Here is what I did.

First of all, I updated the server with apt update && apt upgrade, and then I backed up my server with Azure VM backup. In case upgrade failed, I can restore the VM back.

Then I ran do-release-upgrade to upgrade the server. The os kernel seemed to upgrade successfully, but the software package upgrade failed with the following output. 

authenticate 'bionic.tar.gz' against 'bionic.tar.gz.gpg' 
extracting 'bionic.tar.gz'

 libpython3.6-stdlib:amd64
 python3.6
 python3-apt
 python3
 python3-cffi-backend
 apt-xapian-index
 python3-xapian
 python3-gi
 mailutils
 python3-markupsafe
 python3-systemd
 python3-gdbm:amd64
 python3-lib2to3
 python-apt
 dh-python
 python3-distutils
 libpython3-stdlib:amd64
 python3-yaml
 python3-pycurl
 python3-dbus

Upgrade complete

The upgrade has completed but there were errors during the upgrade
process.

To continue please press [ENTER]

I did some search on the internet. It seems a common issue. To solve this issue, I ran the command sudo mv /usr/share/dbus-1/system-services/org.freedesktop.systemd1.service /usr/share/dbus-1/system-services/org.freedesktop.systemd1.service.bak as it is mentioned here

After the issue was fixed, I just ran sudo apt-get dist-upgrade to upgrade all packages, and I chose to keep all local copies of configurations. After that, the upgrade completed successfully with all software and services running normally.  

CQRS和Event Sourcing模式

这两天重读了微软Patterns & Practices团队几年前写的CQRS Journey。这本书我几年前就看到过,只是当时我工作的重点不在应用架构上,当时没读下去。这两天重读,对CQRS和Event Sourcing (ES)模式有了新的认识。在云计算和微服务大行其道的当下,这两个模式在应用架构方面仍然是非常有参考价值的。这篇文章算是读书小结吧。

什么是CQRS模式?

CQRS是Command and Query Responsibility Segregation的缩写,中文直译过来,就是命令与查询责任分离的意思。这里涉及到两个定义,何为命令?何为查询?

  • 命令会改变对象的状态,但不返回任何数据。
  • 查询则相反,会返回数据,但并不改变对象的状态。

如果将查询和命令简化理解成对数据的读写操作,CQRS模式的含义就是,应用架构中负责模型读写的模块应当分离。这里的分离,不单是程序代码或逻辑上的分离,也包括数据模型,甚至是数据存储的分离。CQRS Journey的这张描述了CQRS的一种典型应用。

虽然不是必须的,但CQRS模式通常与Domain Driven Design (DDD)同时使用。CQRS不是一种全局性的架构模式,它只适用于特定的bounded context。对于领域模型十分复杂的场景,CQRS模式可以增强架构的扩展性和灵活性,同时降低模块的复杂度。由于读和写的模型分离,可以分别针对读写操作优化,同时避免的数据锁定,对于性能提升也有帮助。这是CQRS模式带来的好处。

于此同时,CQRS模式也有其局限性。首先,它并不是一种易于实现的模式。因为读写责任的分离,它不如CRUD来的直观。而且读写数据同步和确保数据一致性会是一个问题。比较常见的作法,是通过事件(Event)来将写操作的结果同步到读操作的数据库中。为了确保数据的一致性,常常借用Event Sourcing模式来实现事件的存储和分发。同时由于通过事件实现的数据同步,其实是异步完成的,在分布式系统中,需要考虑数据的最终一致性(Eventual Consistency)。在CQRS模式中,通常写数据具有完全一致性(Full Consistency),而读数据则具有最终一致性。

这些局限决定了,CQRS模式并非适用于所有场景,或所有的bounded context。它通常只适用于复杂多变,涉及多方操作的场景。而对于业务简单,操作方单一,以及非核心的bounded context,使用CQRS模式可能会增加开销,但并不能带来明显的好处。

什么是Event Sourcing模式?

Event Sourcing (ES)模式是一个关于如何存储domain model状态的模式。这个模式不直接存储模型的状态,而是存储模型状态变化的历史。应用想要获取模型的当前状态时,需要重演整个历史来得到当前状态。一个常用的解释ES模式的场景,是银行账户。

账户余额的直观存储方式,是存储余额本身。当用户存入100元时,余额假定是100,其后用户取出10元,余额变为90,其后用户又存入50元,余额变为140。当用户查询余额时,系统直接获取当前余额。

同样的场景,使用ES模式时,系统不存储余额本身,而是存储用户的行为,即Event。当用户存入100元时,存储“存入100”这个Event,当用户取出10元时,存储“取出10”。当用户查询余额时,系统获得所有的Event,然后进行计算,得到余额。这实际上是金融机构存储交易信息的方式。Bitcoin也是使用同样的方式存储账户的send和receive操作,并且将所有的Event使用blockchain链接起来。

ES模式带来的好处显而易见,比如它能简化写操作,所有Event一旦发生,就变为immutable,写操作就变为简单的添加纪录,避免了复杂的锁定和冲突。同时ES模式保留了所有状态的历史,容易做audit,或纠错。而ES模式的一个问题是,随着历史数据的增加,查询操作的性能可能会降低。

虽然不是必须的,但通常CQRS模式和ES模式会被同时使用。因为通常CQRS模式使用Event来同步读写两端的数据,使用ES模式存储Event有助于这种数据同步,在数据出现不一致的情况时(根据CAP理论,这在分布式系统中是不可避免的),读操作端可以通过replay event的历史,来确保数据的最终一致性。

被删答案2

又一个很久之前发的答案,被举报政治敏感而遭删除。这个答案应该是在2012年左右写的,比英国脱欧早很久。可惜知乎发送的答案备份,居然没有原答案发布的时间,确切的时间不得而知了。

在可见的未来,不可能。
欧洲各国政治制度基本相同,但是现在欧盟也快要维持不下去了。中日韩历史上恩怨情仇就理不清,现在的政治制度有差异极大,还要照顾朝鲜,俄罗斯和美国的感情,根本不存在结盟的基础。对于天朝而言,与日本结盟,岂不是少了一条转移国内愤青爱国热情的对象?

Deploying a Service Fabric cluster to run Windows containers

From container perspective, Service Fabric is a container orchestrator which supports both Windows and Linux containers. In legacy application lift and shift scenarios, we usually containerize the legacy application with minimal code change. And Service Fabric is a good platform to run these containers.

To deploy a Service Fabric cluster on Azure which is suitable for running containers, we can use ARM template. I created a template with the following special settings:

1 – An additional data disk is attached to the VMs in the cluster to host the downloaded container images. We need this disk is because by default all container images would be downloaded to C drive of the VMs. The C drive may run out of space if there are several large images downloaded.

"dataDisks": [
    {
        "lun": 0,
        "createOption": "Empty",
        "caching": "None",
        "managedDisk": {
            "storageAccountType": "Standard_LRS"
        },
        "diskSizeGB": 100
    }
]

2 – A custom script extension is used to run a custom script to format the data disk and change the configuration of dockerd service.

{
    "properties": {
        "publisher": "Microsoft.Compute",
        "type": "CustomScriptExtension",
        "typeHandlerVersion": "1.9",
        "autoUpgradeMinorVersion": true,
        "settings": {
            "fileUris": [

"https://gist.githubusercontent.com/chunliu/8b3c495f7ff0289c19d7d359d9e14f0d/raw/2fdcd207f795756dd94ad7aef4cdb3a97e03d9f8/config-docker.ps1"
            ],
            "commandToExecute": "powershell -ExecutionPolicy Unrestricted -File config-docker.ps1"
        }
    },
    "name": "VMCustomScriptVmExt_vmNodeType0Name"
}

The customer script is as follows:

被删答案

故上兵伐谋,其次伐交,其次伐兵,其下攻城。

这是很久之前发在知乎上一个问题下的答案,最近被删除了,原因是“政治敏感”。当时发这个答案,只是因为好玩,因为正好看到《经济学人》和《时代周刊》在讨论俄罗斯介入叙利亚的新闻。我以为“政治敏感”通常只针对国内事件的讨论,没想到已经扩及到其它国家相关的事件了。还好知乎将答案备份用邮件发给了我,我把它贴在这里,算个备份吧。

在几个答案因为各种原因被删除之后,我已经很久不在知乎上答题了。花时间写出的答案,不知什么时候就会被删除,没有意义。知乎并不能帮你“发现一个更大的世界”,最多只能帮你“发现一个更大的,被过滤的世界”而已。

Create authorization header for Cosmos DB with Go

I started a side project to create a client package for Cosmos DB SQL API with Go so I can try Go in a real project. My plan is to implement something similar to .NET Core SDK with Go. As this is a project for learning and practice, I will do it little by little, and there is no timeline regarding when it can be done.

I build the project based on SQL API via REST. To access resources in Cosmos DB with SQL API via REST, an authorization header is required for the requests. The value of the authorization header has the following format, as it is mentioned in this document.

type={typeoftoken}&ver={tokenversion}&sig={hashsignature}

In the above string, the values of type and version are simple: type is either master or resource, while the current version is 1.0. The value of signature is a bit complex. It is a hash of several other values by using the access key of Cosmos DB as the hash key. The document has all details in it and even better it has a sample written in C#.

So following the document and the sample, I implemented a Go equivalence as follows. It is a good example to try the base64 encoding and HMAC hash in Go.

The date format in the signature is required to be in HTTP-date format defined by RFC7231. However, the time package in the Go standard library doesn’t seem to support this format out of the box, but it provides a very easy way to create custom format. The utcNow() function in the above code is what I implemented to format the time to RFC7231 format.

手机短信

人在国外的一大困扰,是如何使用中国国内的手机号码,来收取国内的短信。随着移动互联网和智能手机的普及,国内的几乎所有服务都可以通过网络来完成,尤其是金融服务和移动支付。但是使用这些服务的前提是,用户需要一个中国国内的手机号码,并且能够随时收到各种服务发出的短信验证码。手机号码还好说,许多人即使出了国,也还是会每个月交月租费,保留自己原来的手机号码。但是如何用这个号码在国外收短信,却是一个问题。

如果在网络上搜索这个问题,会看到各种各样的解决方法。有每个月多花点钱,开通短信漫游服务的;有使用联通的沃信,或移动的Jego服务的;还有用Raspberry Pi来硬件hack,将短信转发到邮箱的。可以说海外的华人在这个问题上充分发挥了聪明才智。

我之前一直使用移动的Jego服务,因为我在国内的时候,一直用的是移动的号码。而且联通的沃信很早就不开放新用户注册了,即使我想让国内的朋友帮忙开个联通的号码,也没机会注册沃信。后来随着手机号码的实名制,更不好意麻烦国内的朋友开新号码了。之前Jego的服务一直不错,虽然手机应用界面比较丑,也没什么更新,但短信服务一直还比较稳定。每个月0.99美元月租,也不算贵。所以我一直用着。

可是这个月初,Jego服务突然中断了。账号无法登录,给他们的客服发邮件,也如石沉大海,没有回应。上网查了查,有人说Jego服务没人管了,人员都被调去做无忧行了。也不知真假。直到前两天,Jego的客服忽然回复了我邮件,确认说Jego服务已经停了,他们推出了Jego升级版,Jego trip,也就是无忧行。新版比原来的Jego功能更多,支持“免费接收来电和短信”,他们建议我换无忧行。

注册了无忧行用了两天,这个应用确实可以免费接收来电和短信,但需要开通国际漫游功能。不过还好,移动的号码只是开通国际漫游的话,并不产生任何费用。这岂不是比以前的Jego还好,连每个月0.99美元也省了?应用是针对Android和iOS新写的,也比以前的好用。现在我只希望,这个无忧行的服务能稳定运营下去,不会说关就关了。

一个加密的世界

我之前配置了一台基于Linux的邮件服务器,我曾经写过配置的步骤。那篇文章还是我的blog上访问量颇高的一篇。

这个邮件服务器我一直在用。大部分时间是用来收邮件,偶尔也发邮件。我原来配置的时候,邮件收发都是启用了TLS的,只是一直用的是自签名的证书,除了在配置客户端的时候,要专门信任一下证书之外,也没什么大的影响。可是最近随着各大邮件服务商,比如outlook和gmail,纷纷升级了TLS策略之后,似乎都不再信任用自签名证书发送的邮件了,必须使用由受信任CA签发的证书。各大浏览器也早已对http的页面提出安全警告了。今后要想玩自建网站和邮箱,一张由CA签发的证书是标配。还好有Let’s Encrypt出来提供免费的证书服务,否则网站和邮箱就没法随便玩了。

如今互联网成了漏洞攻击和数据泄露的高危之地,各大服务商层层防范,端到端加密,HTTPS/SSL更是成为了网站标配,整个世界都快要被加密了。尽管如此,数据泄露的事故还是经常发生。这两天闹得最厉害的,莫过于Facebook的数据泄露事件了,直接导致Facebook股价暴跌,市值一度被阿里巴巴超越。最终扎克伯格不得不亲上火线灭火。毕竟,用户的信任是所有社交网络业务的立足之本,一旦失去用户的信任,想要赢回将是很难的。扎克伯格的回应还算漂亮,这也再次说明,互联网时代最好的危机公关,就是公开透明,让用户知道来龙去脉,以及接下来的因应之道。如果试图掩盖,结果只会是欲盖弥彰,进而被唾弃了。

其实由CA签的证书,只是保证了一层信任关系。有了SSL/TLS,你和服务器之间的通讯,比较难被窃听,比较不会受到所谓的中间人攻击。但是中间人攻击,只是千变万化的网络攻击方式中的一种。即便没有被攻击,但要说有SSL的网站就值得信任,那也不尽然。更有甚者,根证书的签发单位本身也许就有问题,就像之前CNNIC闹出来过的一样。作为普通用户,我们所能做的有限,只能是尽量提高自己的水平,尽量避免让自己成为受害者了。

马航

最近这半年多,我经常要在新加坡和吉隆坡之间往返。新吉两地之间,每天的往返航班很多,我比较经常坐的是新航,胜安航空,还有马航。尤其是最近这两个月,几乎坐的都是马航。

比较起来,新航执飞的是A320这样的大飞机,票价贵,座位多,但人也多,常常满座。人多上下飞机就麻烦。胜安航空和马航用的则是737这样的单通道小飞机,相对而言即使满座,也没新航的人多。由于新加坡到吉隆坡的航程不到一个小时,机上服务基本谈不上,新航和胜安会发杯饮料,马航则是一杯果汁加一小包花生。这样,马航就显得性价比很高了。因为他家的票价最便宜,常常只有新航票价的三分之一,胜安的六成。即使这样还常常坐不满,三人的座位只坐两人,不像其它两家那么挤。但相比其它两家,马航的一大问题是,他的航班有时会晚点,尤其是从吉隆坡飞往新加坡的航班,晚点的几率挺高的。

马航的票价这么便宜,还坐不满,而另外两家贵很多,却还常常满座,表面看来,是航空公司的声誉对于经营真的很重要。马航自从经历MH370这宗现代航空史上的最大谜案,以及之后MH17被击落的悲剧之后,声誉跌入谷底,似乎一蹶不振了。人们买机票时,出于心理因素,会避免选择马航。虽然之后被国有资金控股,退市重组,又请了老外来当CEO,大规模裁员,但感觉一直没有缓过来,经营没什么起色。

空难事故的影响是一方面,但我想,企业文化和国企的效率不彰,恐怕是经营不善的更大根源。在MH370事件之后上任CEO的Mueller,上任一年就辞职了。他曾形容马航是一艘又许多漏洞的船,离职前也暗示马航的企业文化有问题。比如飞机的经常晚点,也许就反应的马航的管理和纪律问题。如果不能从根本上解决这些企业的本质问题,马航要想从谷底翻身,恐怕还需要不少时间呢。