Move WordPress sites to a new domain

Today I finally decided to move this site from its old domain to this new one,, which I got last year. For a personal hosted WordPress site, changing domain name is not a straight forward task, especially when you don’t want to break the existing external links that point to the site. Here is how I did it, with a lot of search on internet of course.

Copy the database and WordPress folder

To minimize the impact to the existing site, I duplicated the MySQL database of this site and its WordPress folder. Copy WordPress folder is easy, simply use cp command to copy the whole folder. Copy database is a bit tricky because I forgot the password of the MySQL root user. I used the following way to reset the password of the root user.

  1. Run mysql with --skip-grant-tables.
    $ sudo service mysql stop
    $ sudo mkdir -p /var/run/mysqld
    $ sudo chown mysql:mysql /var/run/mysqld
    $ sudo mysqld --skip-grant-tables --skip-networking &
    $ jobs
  2. Update the password of root user with mysql client.
    $ mysql -u root
    mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'MyNewPass';
    mysql> QUIT;
  3. Restart mysql service.
    $ sudo pkill mysqld
    $ jobs
    $ sudo service mysql start

With the above steps, the database can be copied with the following command.

mysqldump -u root --password=<pwd> <original db> | mysql -u root -p <new db>

Update the URL of the new site

With the copied WordPress and database, the URL needs to be updated in the following several places.

  • wp_config.php: update DB_NAME and DOMAIN_CURRENT_SITE;
  • wp_options table: update site_url and home;
  • wp_site and wp_blogs table: update domain;

Create a URL rewrite rule in the old site

The last thing is to create a url rewrite rule in the old site so that all requests to the old site can be redirected to the new site. This will help to keep the external links without broken.

Edit the .htaccess file of the old site with the following code.

RewriteEngine On
RewriteCond %{HTTP_HOST} ^olddomain\.com$ [OR]
RewriteCond %{HTTP_HOST} ^blog\.olddomain\.com$
RewriteRule (.*)$ https://newdomain/$1 [R=301,L]

With that, the site is moved to the new domain successfully.

Build Hexo site with Azure Pipelines

Azure Pipelines has the very good integration with GitHub. It provides 10 free parallel jobs and unlimited minutes for open source projects. It is a very good choice for open source projects to have the CI/CD capability.

As I have configured the site to use hexo’s git deployment, an Azure build pipeline is enough to carry out the deployment. And in the build pipeline, I need the following three tasks.

  • Install hexo-cli.
  • Install all npm packages used by the site with npm install.
  • Configure git credential and run hexo deploy.

The first 2 steps are quite straight forward. The tricky part is how to use script to configure git credentials. git credential store only accept inputs from stdin. I did some search and tests, and ended up with a bash pipe to feed in the credentials. The following is a sample for credential store.

git config --global "<git email address>"
git config --global "<git user name>"   
git config --global credential.helper 'store --file ~/.my-credentials'
printf "protocol=https\\nusername=<username>\npassword=%s\n\n" "$GHP" | git credential-store --file ~/.my-credentials store
./node_modules/.bin/hexo clean && ./node_modules/.bin/hexo deploy

As credential store will save the credential to a file on disk, it is not a good practice for a shared build agent. I ended up to replace the credential store with credential cache, and exit the cache when deployment is done.

The following is the yaml file of the build definition. $GHP is an environment variable storing the personal token of GitHub. The actual value is saved in Azure DevOps encrypted.

- repo: self
  name: Hosted Ubuntu 1604
  demands: npm

- script: |
   echo "install hexo-cli"   
   npm install hexo-cli

  displayName: 'Install hexo-cli'

- task: Npm@1
  displayName: 'npm install'
    workingDir: /

    verbose: false

- script: |
   git config --global "<git email address>"   
   git config --global "<git user name>"   
   git config --global credential.helper cache   
   printf "protocol=https\\nusername=<username>\npassword=%s\n\n" "$GHP" | git credential-cache store   
   ./node_modules/.bin/hexo clean && ./node_modules/.bin/hexo deploy   
   git credential-cache exit
  displayName: 'hexo deploy'
    GHP: $(password)

Now when I update the site, I just need to commit the changes and push it to GitHub. Azure Pipeline will take care of the build and deployment automatically.

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

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'


Upgrade complete

The upgrade has completed but there were errors during the upgrade

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是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模式的场景,是银行账户。




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




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.

[code lang=”HTML”] "dataDisks": [ { "lun": 0, "createOption": "Empty", "caching": "None", "managedDisk": { "storageAccountType": "Standard_LRS" }, "diskSizeGB": 100 } ] [/code]

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

[code lang=”HTML”] { "properties": { "publisher": "Microsoft.Compute", "type": "CustomScriptExtension", "typeHandlerVersion": "1.9", "autoUpgradeMinorVersion": true, "settings": { "fileUris": [ "" ], "commandToExecute": "powershell -ExecutionPolicy Unrestricted -File config-docker.ps1" } }, "name": "VMCustomScriptVmExt_vmNodeType0Name" } [/code]

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.


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服务没人管了,人员都被调去做无忧行了。也不知真假。直到前两天,Jego的客服忽然回复了我邮件,确认说Jego服务已经停了,他们推出了Jego升级版,Jego trip,也就是无忧行。新版比原来的Jego功能更多,支持“免费接收来电和短信”,他们建议我换无忧行。