西维蜀黍

【Design Pattern】Concurrency Pattern

  ...


【Design Pattern】Microservices - Health Check Pattern

Context

Sometimes a service instance can be incapable of handling requests yet still be running. For example, it might have ran out of database connections. When this occurs, the monitoring system should generate a alert. Also, the load balancer or service registry should not route requests to the failed service instance.

  ...


【Design Pattern】Microservices - Circuit Breaker Pattern

Background

In Electrical or Electornics domain, a circuit breaker is an automatically operated electrical switch designed to protect an electrical circuit. Its basic function is to interrupt current flow after a fault is detected. It can then be reset to resume normal operation after the fault is solved.

Similarly to protect our microservices from an excess of requests, it’s better to interrupt communication between the front-end and the back-end as soon as a recurring fault has been detected in the back-end.

In his excellent book Release It, Michael Nygard popularized the Circuit Breaker pattern to prevent this kind of catastrophic cascade.

  ...


【Golang】内存管理

  ...


【Golang】slice 分析和实现

Analysis

Slice internals

A slice is a descriptor of an array segment. It consists of a pointer to the array, the length of the segment, and its capacity (the maximum length of the segment).

Our variable s, created earlier by make([]byte, 5), is structured like this:

The length is the number of elements referred to by the slice. The capacity is the number of elements in the underlying array (beginning at the element referred to by the slice pointer).

  ...


【Golang】源码 - unsafe包

Overall

Pointer represents a pointer to an arbitrary type. There are four special operations available for type Pointer that are not available for other types:

  • A pointer value of any type can be converted to a Pointer.
  • A Pointer can be converted to a pointer value of any type.
  • A uintptr can be converted to a Pointer.
  • A Pointer can be converted to a uintptr.

Pointer therefore allows a program to defeat the type system and read and write arbitrary memory. It should be used with extreme care.

The following patterns involving Pointer are valid.

  ...


【Golang】Code Review

Summary

  • Use goimports to standardise imports

    • a superset of gofmt which additionally adds (and removes) import lines as necessary.
  • godoc

  • gofmt

  • go lint

Flow

Indent Error Flow

Try to keep the normal code path at a minimal indentation, and indent the error handling, dealing with it first. This improves the readability of the code by permitting visually scanning the normal path quickly. For instance, don’t write:

if err != nil {
	// error handling
} else {
	// normal code
}
  ...


【Golang】empty Slice和nil Slice

Best Practise

When declaring an empty slice, prefer

// nil slice
var t []string

over

// empty slice, or name non-nil but zero-length slice
t := []string{}

The former declares a nil slice value, while the latter is non-nil but zero-length. They are functionally equivalent—their len and cap are both zero—but the nil slice is the preferred style.

Note that there are limited circumstances where a non-nil but zero-length slice is preferred, such as when encoding JSON objects (a nil slice encodes to null, while []string{} encodes to the JSON array []).

When designing interfaces, avoid making a distinction between a nil slice and a non-nil, zero-length slice, as this can lead to subtle programming errors.

  ...


【Operating System】进程 - 进程通讯 - 信号量(Semaphore)

信号量 Semaphore

信号量(Semaphores)机制被用作进程/线程间同步,且包括两个原语(primitive), down 和 up 。

信号量机制最初由 Dijkstra 在 1965 年提出,在他原始的论文中,他使用 P 和 V ,分别表示荷兰语中的 Proberen (try)和 Verhogen (raise, make higher)对应 down 和 up 。为了简化,我们统一采用 down 和 up 进行描述。

信号量相当于一个计数器,使用一个非负的整型变量来表示当前可用资源的数量。

假设一个进程每次只请求一个资源,当一个进程请求获取某个资源时,先要读取资源对应的信号量的值。

  • 当信号量为 0 时, 表示当前没有资源可再被分配(因为其他进程已经占有完了所有可用资源了,因此当前进程会进入阻塞状态(blocked)。直到信号量大于 1 时,会被唤醒,且再次请求获取资源);
  • 当信号量大于 0 时,表示当前有资源可再被分配。因此当前进程可以成功获取到资源。这个进程通过调用 down 操作,以实现获取资源,并将信号量减一(以标识当前可用资源少了一个)。
    • 当该进程不再使用这个资源时,这个进程需要调用 up 操作,以实现释放该资源,并将信号量加一(以标识当前可用资源多了一个)。
    • 此后,若仍有一个或多个进程仍因等待可用资源的分配,而处于休眠状态时,内核会选择其中的一个进程以使其调用 up 操作。

从获取信号量的值、判断信号量的值(是否大于 0 )到改变信号量的值(或者因为没有可分配的资源因而不改变信号量的值,最终进程休眠)的整个过程是一个原子操作(atomic action)。换句话说,当一个信号量操作(semaphore operation)开始后,其他进程都不能获取信号量直到这个信号量操作完成。

原子性是解决同步问题(synchronization problems)和避免竞态条件的基础。

信号量机制的实现

信号量中的两个原语(primitive), down 和 up ,可以被实现为 down 和 up 两个系统调用(system call)。

对于单处理器系统,而操作系统可以通过在获取信号量或更新信号量(或将进程调度为休眠状态)的整个过程中禁用中断(disable interrupt)。

对于多处理器系统,信号量机制需要通过锁定变量(Lock Variables) + TSL 或 XCHG 指令实现。

  ...


【Concurrent Control】CAS(Compare-and-swap)

CAS(Compare and swap)

CAS(比较与交换,Compare and swap) 算法是一种有名的非阻塞算法(non-blocking algorithm),同时也是一种无锁算法(lock-free algorithm),即在没有锁的情况下实现同步(并发控制),基于**乐观并发控制(optimistic concurrent control)**的思想。

2021.5 update:

  • 这里避免使用“乐观并发控制(optimistic lock)”这个词,原因是这可能会带来confusion
  • 因为“乐观并发控制”本身 imply使用到了锁(lock),而锁本来就是悲观的(pessimistic),这与乐观(optimistic)相互矛盾
  • 另外,乐观并发控制其实描述的是一种进行并发控制(concurrent control)的思想,在这种思想中并不存在锁
  • 因而,就可能导致 confusion
  • 如果称为乐观并发控制(optimistic concurrent control),就会更准确。因而在本文中,不会使用“乐观锁”这个词,而是使用乐观并发控制。
  • 也可能称为无锁并发控制(lock-free concurrent control)

java.util.concurrent包中的原子类(比如 AtomicInteger)就是通过CAS来实现乐观并发控制的。

当多个线程尝试使用 CAS 同时更新同一个变量时,只有其中一个线程能成功地更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。

  ...