未验证 提交 068fa037 编写于 作者: 余林颖 提交者: GitHub

docs: Translate some of the README docs into Chinese (#1744)

* docs: translated docs to zh

* docs: translated doc of sharding pattern to Chinese

* docs: translated doc of factory pattern to Chinese

* docs: translated doc of factory-kit pattern to Chinese
Co-authored-by: NSubhrodip Mohanta <hello@subho.xyz>
上级 b5aaa947
---
layout: pattern
title: Circuit Breaker
folder: circuit-breaker
permalink: /patterns/circuit-breaker/
categories: Behavioral
tags:
- Performance
- Decoupling
- Cloud distributed
---
## 含义
以这样的方式(译者:指断路器方式)处理昂贵的远程服务调用,可以防止单个服务/组件的故障导致整个应用程序崩溃,同时我们可以尽快地进行服务重连。
## 解释
现实世界案例
> 设想一下,一个网络应用程序既有本地文件/图像,又有用于获取数据的远程服务。这些远程服务可能在某些时候是健康的、有反应的,也可能在某些时候由于各种原因而变得缓慢和无反应。因此,如果其中一个远程服务速度慢或不能成功响应,我们的应用程序将尝试使用多个线程/进程从远程服务中获取响应,很快所有的线程/进程都会挂起(也称为线程饥饿 thread starvation),从而导致我们整个 Web 应用程序崩溃。我们应该能够检测到这种情况,并向用户显示一个适当的信息,以便用户可以探索应用程序的其他部分,而不受远程服务故障的影响。同时,其他正常工作的服务应该保持运作,不受这次故障的影响。
简而言之
> 断路器允许优雅地处理失败的远程服务。当我们的应用程序的所有部分都高度解耦时,这种方式的效果会很好,一个组件的失败并不会导致其他部分停止工作。
维基百科的解释
> 断路器是现代软件开发中使用的一种设计模式。它用于检测故障,并封装了防止故障不断复发的逻辑,在维护期间,临时地处理外部系统故障或意外的系统问题。
## Programmatic Example
那么,这一切是如何实现的呢?考虑到上面的例子,我们将在一个简单的例子中模拟这个功能。一个监控服务(译者:下图的 Monitoring Service)模拟了网络应用,进行本地和远程调用。
该服务架构如下:
![alt text](../../circuit-breaker/etc/ServiceDiagram.png "Service Diagram")
终端用户(译者:上图的 End User)应用的代码如下:
```java
@Slf4j
public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/**
* Program entry point.
*
* @param args command line args
*/
public static void main(String[] args) {
var serverStartTime = System.nanoTime();
var delayedService = new DelayedRemoteService(serverStartTime, 5);
var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, 2,
2000 * 1000 * 1000);
var quickService = new QuickRemoteService();
var quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, 2,
2000 * 1000 * 1000);
//Create an object of monitoring service which makes both local and remote calls
var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,
quickServiceCircuitBreaker);
//Fetch response from local resource
LOGGER.info(monitoringService.localResourceResponse());
//Fetch response from delayed service 2 times, to meet the failure threshold
LOGGER.info(monitoringService.delayedServiceResponse());
LOGGER.info(monitoringService.delayedServiceResponse());
//Fetch current state of delayed service circuit breaker after crossing failure threshold limit
//which is OPEN now
LOGGER.info(delayedServiceCircuitBreaker.getState());
//Meanwhile, the delayed service is down, fetch response from the healthy quick service
LOGGER.info(monitoringService.quickServiceResponse());
LOGGER.info(quickServiceCircuitBreaker.getState());
//Wait for the delayed service to become responsive
try {
LOGGER.info("Waiting for delayed service to become responsive");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//Check the state of delayed circuit breaker, should be HALF_OPEN
LOGGER.info(delayedServiceCircuitBreaker.getState());
//Fetch response from delayed service, which should be healthy by now
LOGGER.info(monitoringService.delayedServiceResponse());
//As successful response is fetched, it should be CLOSED again.
LOGGER.info(delayedServiceCircuitBreaker.getState());
}
}
```
监控服务代码(译者:上图的 monitoring service):
```java
public class MonitoringService {
private final CircuitBreaker delayedService;
private final CircuitBreaker quickService;
public MonitoringService(CircuitBreaker delayedService, CircuitBreaker quickService) {
this.delayedService = delayedService;
this.quickService = quickService;
}
//Assumption: Local service won't fail, no need to wrap it in a circuit breaker logic
public String localResourceResponse() {
return "Local Service is working";
}
/**
* Fetch response from the delayed service (with some simulated startup time).
*
* @return response string
*/
public String delayedServiceResponse() {
try {
return this.delayedService.attemptRequest();
} catch (RemoteServiceException e) {
return e.getMessage();
}
}
/**
* Fetches response from a healthy service without any failure.
*
* @return response string
*/
public String quickServiceResponse() {
try {
return this.quickService.attemptRequest();
} catch (RemoteServiceException e) {
return e.getMessage();
}
}
}
```
可以看出,它直接进行了获取本地资源的调用,但它把对远程(昂贵的)服务的调用包装在一个断路器对象中,这样可以防止出现如下故障:
```java
public class DefaultCircuitBreaker implements CircuitBreaker {
private final long timeout;
private final long retryTimePeriod;
private final RemoteService service;
long lastFailureTime;
private String lastFailureResponse;
int failureCount;
private final int failureThreshold;
private State state;
private final long futureTime = 1000 * 1000 * 1000 * 1000;
/**
* Constructor to create an instance of Circuit Breaker.
*
* @param timeout Timeout for the API request. Not necessary for this simple example
* @param failureThreshold Number of failures we receive from the depended service before changing
* state to 'OPEN'
* @param retryTimePeriod Time period after which a new request is made to remote service for
* status check.
*/
DefaultCircuitBreaker(RemoteService serviceToCall, long timeout, int failureThreshold,
long retryTimePeriod) {
this.service = serviceToCall;
// We start in a closed state hoping that everything is fine
this.state = State.CLOSED;
this.failureThreshold = failureThreshold;
// Timeout for the API request.
// Used to break the calls made to remote resource if it exceeds the limit
this.timeout = timeout;
this.retryTimePeriod = retryTimePeriod;
//An absurd amount of time in future which basically indicates the last failure never happened
this.lastFailureTime = System.nanoTime() + futureTime;
this.failureCount = 0;
}
// Reset everything to defaults
@Override
public void recordSuccess() {
this.failureCount = 0;
this.lastFailureTime = System.nanoTime() + futureTime;
this.state = State.CLOSED;
}
@Override
public void recordFailure(String response) {
failureCount = failureCount + 1;
this.lastFailureTime = System.nanoTime();
// Cache the failure response for returning on open state
this.lastFailureResponse = response;
}
// Evaluate the current state based on failureThreshold, failureCount and lastFailureTime.
protected void evaluateState() {
if (failureCount >= failureThreshold) { //Then something is wrong with remote service
if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) {
//We have waited long enough and should try checking if service is up
state = State.HALF_OPEN;
} else {
//Service would still probably be down
state = State.OPEN;
}
} else {
//Everything is working fine
state = State.CLOSED;
}
}
@Override
public String getState() {
evaluateState();
return state.name();
}
/**
* Break the circuit beforehand if it is known service is down Or connect the circuit manually if
* service comes online before expected.
*
* @param state State at which circuit is in
*/
@Override
public void setState(State state) {
this.state = state;
switch (state) {
case OPEN:
this.failureCount = failureThreshold;
this.lastFailureTime = System.nanoTime();
break;
case HALF_OPEN:
this.failureCount = failureThreshold;
this.lastFailureTime = System.nanoTime() - retryTimePeriod;
break;
default:
this.failureCount = 0;
}
}
/**
* Executes service call.
*
* @return Value from the remote resource, stale response or a custom exception
*/
@Override
public String attemptRequest() throws RemoteServiceException {
evaluateState();
if (state == State.OPEN) {
// return cached response if the circuit is in OPEN state
return this.lastFailureResponse;
} else {
// Make the API request if the circuit is not OPEN
try {
//In a real application, this would be run in a thread and the timeout
//parameter of the circuit breaker would be utilized to know if service
//is working. Here, we simulate that based on server response itself
var response = service.call();
// Yay!! the API responded fine. Let's reset everything.
recordSuccess();
return response;
} catch (RemoteServiceException ex) {
recordFailure(ex.getMessage());
throw ex;
}
}
}
}
```
上述模式是如何防止失败的呢?让我们通过它所实现的这个有限状态机来了解。
![alt text](../../circuit-breaker/etc/StateDiagram.png "State Diagram")
- 我们用 `timeout`(超时)、 `failureThreshold` (失败阈值)、`retryTimePeriod`(重试时间周期) 参数初始化断路器对象 ,用于确定 API 的适应性。
- 最初,断路器处于 `closed` 关闭状态,没有发生对 API 的远程调用。
- 每次调用成功,我们就把状态重置为开始时的样子。
- 如果失败的次数超过了一定的阈值(`failureThreshold`),断路器就会进入 `open` 开启状态,它的作用就像一个开启的电路,阻止远程服务的调用,从而节省资源。
- 一旦我们超过重试时间周期(`retryTimePeriod`),断路器就会转到 `half-open` 半启用状态,并再次调用远程服务,检查服务是否正常,以便我们可以提供最新的响应内容。如果远程服务调用失败会使断路器回到 `open` 状态,并在重试超时后进行另一次尝试;如果远程服务调用成功则使断路器进入 `closed` 状态,这样一切又开始正常工作。
## 类图
![alt text](../../circuit-breaker/etc/circuit-breaker.urm.png "Circuit Breaker class diagram")
## 适用场景
在以下场景下,可以使用断路器模式:
- 构建一个高可用的应用程序,某些些服务的失败不会导致整个应用程序的崩溃。
- 构建一个持续运行(长期在线)的应用程序,以便其组件可以在不完全关闭的情况下进行升级。
## 相关模式
- [Retry Pattern](https://github.com/iluwatar/java-design-patterns/tree/master/retry)
## 现实案例
* [Spring Circuit Breaker module](https://spring.io/guides/gs/circuit-breaker)
* [Netflix Hystrix API](https://github.com/Netflix/Hystrix)
## 引用
* [Understanding Circuit Breaker Pattern](https://itnext.io/understand-circuitbreaker-design-pattern-with-simple-practical-example-92a752615b42)
* [Martin Fowler on Circuit Breaker](https://martinfowler.com/bliki/CircuitBreaker.html)
* [Fault tolerance in a high volume, distributed system](https://medium.com/netflix-techblog/fault-tolerance-in-a-high-volume-distributed-system-91ab4faae74a)
* [Circuit Breaker pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker)
\ No newline at end of file
---
layout: pattern
title: Collection Pipeline
folder: collection-pipeline
permalink: /patterns/collection-pipeline/
categories: Functional
tags:
- Reactive
---
## 释义
**集合管道(Collection Pipeline)**包含**函数组合(Function Composition)****集合管道(Collection Pipeline)**两组合概念,这是两种函数式编程模式,你可以在代码中结合这两种模式来进行集合迭代。
在函数式编程中,可以通过一系列较小的模块化函数或操作来编排复杂的操作。这一系列函数被称为函数组合。当一个数据集合流经一个函数组合时,它就成为一个集合管道。函数组合和集合管道是函数式编程中经常使用的两种设计模式。
## 类图
![alt text](../../collection-pipeline/etc/collection-pipeline.png "Collection Pipeline")
## 适用场景
在以下场景适用集合管道模式:
* 当你想执行一组连续的算子操作,其中一个算子收集的输出需要被输入到下一个算子中
* 当你在代码中需要使用大量的中间状态语句时
* 当你在代码中使用大量的循环语句时
## 引用
* [Function composition and the Collection Pipeline pattern](https://www.ibm.com/developerworks/library/j-java8idioms2/index.html)
* [Martin Fowler](https://martinfowler.com/articles/collection-pipeline/)
* [Java8 Streams](https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html)
\ No newline at end of file
---
layout: pattern
title: Composite Entity
folder: composite-entity
permalink: /patterns/composite-entity/
categories: Structural
tags:
- Enterprise Integration Pattern
---
## 含义
复合实体模式用于对一组相关联的持久化对象进行建模、描述和管理,用于取代对这组对象描述为单独粒度的实体。
## 解释
现实例子
> 对于一个控制台对象,需要管理许多接口功能。通过使用复合实体模式,将消息对象、信号对象等依赖性对象组合在一起,直接使用单个对象对其进行控制。
简单地说
> 复合实体模式允许使用一个统一对象来管理一组相互关联的对象
**编程示例**
我们需要一个通用的解决方案来解决上述的控制台问题。我们引入了以下的通用复合对象。
```java
public abstract class DependentObject<T> {
T data;
public void setData(T message) {
this.data = message;
}
public T getData() {
return data;
}
}
public abstract class CoarseGrainedObject<T> {
DependentObject<T>[] dependentObjects;
public void setData(T... data) {
IntStream.range(0, data.length).forEach(i -> dependentObjects[i].setData(data[i]));
}
public T[] getData() {
return (T[]) Arrays.stream(dependentObjects).map(DependentObject::getData).toArray();
}
}
```
专用的 `console` 复合实体继承自这个基类,如下所示。
```java
public class MessageDependentObject extends DependentObject<String> {
}
public class SignalDependentObject extends DependentObject<String> {
}
public class ConsoleCoarseGrainedObject extends CoarseGrainedObject<String> {
@Override
public String[] getData() {
super.getData();
return new String[]{
dependentObjects[0].getData(), dependentObjects[1].getData()
};
}
public void init() {
dependentObjects = new DependentObject[]{
new MessageDependentObject(), new SignalDependentObject()};
}
}
public class CompositeEntity {
private final ConsoleCoarseGrainedObject console = new ConsoleCoarseGrainedObject();
public void setData(String message, String signal) {
console.setData(message, signal);
}
public String[] getData() {
return console.getData();
}
}
```
现在我们使用 `console` 复合实体来进行消息对象、信号对象的分配。
```java
var console = new CompositeEntity();
console.init();
console.setData("No Danger", "Green Light");
Arrays.stream(console.getData()).forEach(LOGGER::info);
console.setData("Danger", "Red Light");
Arrays.stream(console.getData()).forEach(LOGGER::info);
```
## 类图
![alt text](../../composite-entity/etc/composite_entity.urm.png "Composite Entity Pattern")
## 适用场景
复合实体模式适用于以下场景:
* 你想要通过一个对象来管理多个依赖对象,已调整对象之间的细化程度。同时将依赖对象的生命周期托管到这个粗粒度的复合实体对象。
## 引用
* [Composite Entity Pattern in wikipedia](https://en.wikipedia.org/wiki/Composite_entity_pattern)
\ No newline at end of file
---
layout: pattern
title: Data Bus
folder: data-bus
permalink: /patterns/data-bus/
categories: Architectural
tags:
- Decoupling
---
## 含义
数据总线模式(译者:实际上,就是 Event-Bus 消息总线模式)允许在一个应用程序的组件之间收发消息/事件,而不需要这些组件相互感知,它们只需要知道所发送/接收的消息/事件的类型即可。
## 类图
![data bus pattern uml diagram](../../data-bus/etc/data-bus.urm.png "Data Bus pattern")
## 适用场景
可以在以下场景使用数据总线模式:
* 你希望由你的组件自己决定要接收哪些信息/事件
* 你希望实现多对多的通信
* 你希望你的组件不需要感知彼此
## 相关模式
数据总线类似于以下设计模式:
* 中介者模式(Mediator pattern),由数据总线成员自己决定是否要接受任何给定的消息。
* 观察者模式(Observer pattern),但进一步支持了多对多的通信。
* 发布/订阅模式(Publish/Subscribe pattern),但是数据总线将发布者和订阅者解耦。
\ No newline at end of file
---
layout: pattern
title: Data Mapper
folder: data-mapper
permalink: /patterns/data-mapper/
categories: Architectural
tags:
- Decoupling
---
## 含义
一个用于在持久化对象和数据库之间传输数据的映射器,同时保持它们之间和映射器本身的独立性。
## 类图
![alt text](../../data-mapper/etc/data-mapper.png "Data Mapper")
## 适用场景
数据映射器适用于以下场景:
* 当你想把数据对象从数据库访问层解耦时时
* 当你想编写多个数据查询/持久化实现时
## 引用
* [Data Mapper](http://richard.jp.leguen.ca/tutoring/soen343-f2010/tutorials/implementing-data-mapper/)
\ No newline at end of file
---
layout: pattern
title: Double Checked Locking
folder: double-checked-locking
permalink: /patterns/double-checked-locking/
categories: Idiom
tags:
- Performance
---
## 含义
通过先测试锁定标准("锁提示")而不实际获取锁的方式来减少获取锁的开销。只有当锁定标准检查表明需要锁定时,才进行实际的锁定逻辑。
## 类图
![alt text](../../double-checked-locking/etc/double_checked_locking_1.png "Double Checked Locking")
## 适用场景
在以下场景适合使用双重锁检查模式:
* 在创建对象时有存在并发的访问。如单例模式中,你想创建同一个类的单个实例,如果存在两个或更多的线程对实例进行判空,仅仅检查该该实例是否为空可能是不够的。
* 在一个方法上存在并发访问,该方法的行为是根据一些约束条件而改变,而这些约束条件在该方法中也会发生变化。
\ No newline at end of file
---
layout: pattern
title: Factory Kit
folder: factory-kit
permalink: /patterns/factory-kit/
categories: Creational
tags:
- Extensibility
---
## 含义
使用分离的构建器和工厂接口来定义一个不可变内容的工厂。
## 类图
![alt text](../../factory-kit/etc/factory-kit.png "Factory Kit")
## 适用场景
工厂套件模式适用于与以下场景:
* 一个类无法预知它需要创建的对象的类别
- 你只是想要一个新的自定义构建器(builder)的实例,而非全局的构建器
- 你明确地想要定义对象的类型,而且工厂可以创建这些对象
- 你想要分离构建器(builder)和创建器(creator)接口
## 引用
* [Design Pattern Reloaded by Remi Forax: ](https://www.youtube.com/watch?v=-k2X7guaArU)
\ No newline at end of file
---
layout: pattern
title: Factory
folder: factory
permalink: /patterns/factory/
categories: Creational
tags:
- Gang of Four
---
## 也被称为
* 简单工厂
* 静态工厂方法
## 含义
在工厂类中提供一个封装的静态工厂方法,用于隐藏对象初始化细节,使客户端代码可以专注于使用,而不用关心类的初始化过程。
## 解释
现实例子
>
> 假设我们有一个需要连接到 SQL Server 的 Web 应用,但现在我们需要切换到连接 Oracle。为了不修改现有代码的情况下做到这一点,我们需要实现简单工厂模式。在这种模式下,可以通过调用一个静态方法来创建与给定数据库的连接。
维基百科
> 工厂类是一个用于创建其他对象的对象 -- 从形式上看,工厂方法是一个用于返回不同原型或类型的函数或方法。
**编程示例**
我们有一个 `Car` 接口,以及实现类 `Ford`, `Ferrari`
```java
public interface Car {
String getDescription();
}
public class Ford implements Car {
static final String DESCRIPTION = "This is Ford.";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
public class Ferrari implements Car {
static final String DESCRIPTION = "This is Ferrari.";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
```
Enumeration above represents types of cars that we support (`Ford` and `Ferrari`).
以下的枚举用于表示支持的 `Car` 类型(`Ford``Ferrari`
```java
public enum CarType {
FORD(Ford::new),
FERRARI(Ferrari::new);
private final Supplier<Car> constructor;
CarType(Supplier<Car> constructor) {
this.constructor = constructor;
}
public Supplier<Car> getConstructor() {
return this.constructor;
}
}
```
接着我们实现了一个静态方法 `getCar` 用于封装工厂类 `CarsFactory` 创建 `Car` 具体对象实例的细节。
```java
public class CarsFactory {
public static Car getCar(CarType type) {
return type.getConstructor().get();
}
}
```
现在我们可以在客户端代码中通过工厂类创建不同类型的 `Car` 对象实例。
```java
var car1 = CarsFactory.getCar(CarType.FORD);
var car2 = CarsFactory.getCar(CarType.FERRARI);
LOGGER.info(car1.getDescription());
LOGGER.info(car2.getDescription());
```
程序输出:
```java
This is Ford.
This is Ferrari.
```
## 类图
![alt text](../../factory/etc/factory.urm.png "Factory pattern class diagram")
## 适用场景
在你只关心对象的创建,但不关心如何创建、管理它的时候,请使用简单工厂模式。
**优点**
* 可以把对象创建代码集中在一个地方,避免在代码库存散布 "new" 关键字。
* 可以让代码更加低耦合。它的一些主要优点包括更好的可测试性、更好的可读性、组件可替换性、可拓展性、更好的隔离性。
**缺点**
* 会使代码变得比原来的更加复杂一些。
## 现实案例
* [java.util.Calendar#getInstance()](https://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#getInstance--)
* [java.util.ResourceBundle#getBundle()](https://docs.oracle.com/javase/8/docs/api/java/util/ResourceBundle.html#getBundle-java.lang.String-)
* [java.text.NumberFormat#getInstance()](https://docs.oracle.com/javase/8/docs/api/java/text/NumberFormat.html#getInstance--)
* [java.nio.charset.Charset#forName()](https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html#forName-java.lang.String-)
* [java.net.URLStreamHandlerFactory#createURLStreamHandler(String)](https://docs.oracle.com/javase/8/docs/api/java/net/URLStreamHandlerFactory.html) (Returns different singleton objects, depending on a protocol)
* [java.util.EnumSet#of()](https://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html#of(E))
* [javax.xml.bind.JAXBContext#createMarshaller()](https://docs.oracle.com/javase/8/docs/api/javax/xml/bind/JAXBContext.html#createMarshaller--) and other similar methods.
## 相关模式
* [Factory Method](https://java-design-patterns.com/patterns/factory-method/)
* [Factory Kit](https://java-design-patterns.com/patterns/factory-kit/)
* [Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/)
---
layout: pattern
title: Sharding
folder: sharding
permalink: /patterns/sharding/
categories: Behavioral
tags:
- Performance
- Cloud distributed
---
## 含义
分片模式是指将数据存储划分为水平分区或分片。每个分片都有相同的模式,但持有自己独特的数据子集。
一个分片本身就是一个数据存储(它可以包含许多不同类型的实体的数据),运行在作为存储节点的服务器上。
## 类图
![alt text](../../sharding/etc/sharding.urm.png "Sharding pattern class diagram")
## 适用场景
这种设计模式提供了一下的好处:
- 你可以通过增加在额外的存储节点上,运行的更多分片来实现系统扩容。
- 系统可以使用现成的廉价硬件,而不是为每个存储节点使用专门(或者昂贵)的服务器硬件。
- 你可以通过平衡各分片之间的工作负载来减少竞争,以提高性能。
- 在云环境中,分片可以在物理上靠近访问该节点数据的用户。
## 引用
* [Sharding pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/sharding)
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册