Skip to content

Commit

Permalink
update readme
Browse files Browse the repository at this point in the history
update readme
  • Loading branch information
ShiningRush committed Jan 31, 2018
1 parent 5576774 commit 90c4c62
Showing 1 changed file with 77 additions and 74 deletions.
151 changes: 77 additions & 74 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,34 @@

[中文介绍请点击这里](https://github.com/ShiningRush/PdfComponentComparition/blob/master/README.zh-cn.md)

ServiceAnt 的定位是一个轻量级,开箱即用的服务总线,好吧,虽然它现在还没有实现真正意义上的服务总线,但正朝着那个方向发展。
目前它只能运行于进程内,以后会推出分布式的实现。现在你可以把它当作一个解耦模块的中介者(类似于 Mediator)  
ServiceAnt is a lightweight servicebus which is out-of-the-box. Well, though it can not be called servicebus but it will in soon.
It is only running in-process system, but i will make it run distributed system in future.
You can see it as a communication mediator in this version.

[了解ServiceAnt为什么而出现](#Detail)
[Learn why serviceant is created](#Detail)

## Get Started

## 安装
## Install

```
Install-Package ServiceAnt
```

## 使用
## Usage

ServiceAnt 有两种工作模式分别是:
ServiceAnt has two mode of communication:

* Pub/Sub(发布/订阅)模式
* Req/Resp(请求/响应)模式
* Pub/Sub
* Req/Resp

### Pub/Sub

该模式没有什么特别,完全遵从于观察者模式,当按这种方式使用时,你可以把 ServiceAnt 看作事件总线。
This mode has nothing special, it completely follow observer design pattern.

示例代码片段如下:
You can see serviceant as a eventbus when you use this mode.

Sample code:
```c#
static void Main(string[] args)
{
Expand Down Expand Up @@ -64,13 +67,13 @@ ServiceAnt 有两种工作模式分别是:

### Req/Resp

该模式在正常的请求响应模式下追加了管道处理机制,有点类似于 `Owin 的中间件` `WebApiMessageHandler` 它们的工作方式,不同的地方在于它是单向流动的,而后者是双向的.  
This mode appends pipeline handling in normal request-response mode, somewhat similar to `Owin's middleware` and `WebApi's MessageHandler`, which work differently, except that it is a one-way flow and the latter is bidirectional of. 

> ### 管道的处理顺序
> 管道处理顺序是按照的注册的顺序来执行的,暂时没有提供控制执行顺序的配置.
> 在大多数情况下,我们只推荐为 Req/Resp 注册单个注册函数.
> ### The order of Pipeline processing
> Pipeline processing order is in accordance with the order of registration to perform, there is no provision for the control of the implementation of the order.
> In most cases, we only recommend registering a single handler for Req / Resp.
示例代码片段如下:
Sample code:
```c#
static void Main(string[] args)
{
Expand Down Expand Up @@ -115,31 +118,31 @@ ServiceAnt 有两种工作模式分别是:
}
```

> ### 提示
> 在使用 Pub/Sub 模式时, 你的触发对象必须继承 IEventTrigger 接口, 而 Req/Resp 模式则必须继承 IRequestTrigger.
> ### Tips
> When using Pub / Sub mode, your trigger object must inherit the IEventTrigger interface, while Req / Resp mode must inherit IRequestTrigger.
## 注册处理函数
## Register handler

ServiceAnt 支持以下两种方式注册处理函数
ServiceAnt supports registering handlers in the following two ways:

* 注册委托
* Ioc注册
* Register with delegate
* Register with IOC

### 注册委托
### Register with delegate

这种注册方式已经在之前的代码片段中演示过了, 它支持两种不同输入参数的委托, 一种是显式的泛型, 还有种就是动态类型  
This registration method has been demonstrated in the previous code snippet, it supports two different input parameters of the delegate, one is an explicit generic, the other is a dynamic type  

值得一提的是,虽然我更推荐通过Ioc来注册处理函数(这样可以获得更好的可读性与可修改性以及可测试性),
但在我们团队使用过程中并没有发现通过委托注册存在什么弊端,而且在你需要复用某些局部变量时, 这种方式会更好一些
It is worth mentioning that, although I also recommend registering handler with IOC (which can achieve better readability and modifiability and testability),
However, We have not found any problems to register handler with delegate and when you need to reuse some of the local variables, this approach would be better

> ### 注意
> 动态类型参数的注册需要用到事件名称,这可能会导致无法正确注册某些泛型 Trigger 的处理函数, 因为泛型在转换名称的过程中不是单纯的类名转换
> ### Notice
> The registration of dynamic type parameters requires the event names, which may result in the failure to properly register certain generic Trigger handlers because generics are not simply class name conversions during name conversion.
### Ioc注册
### Register with IOC

在使用Ioc注册之前,首先我们需要把 ServiceAnt 集成到你的 Ioc环境中,请参考 [Ioc集成](#IocIntegration) 来将 ServiceAnt 集成到你的 Ioc当中。
Before using Ioc registration, we need to integrate ServiceAnt into your Ioc environment first. Please refer to [Ioc Integration](# IocIntegration) to integrate ServiceAnt into your Ioc.

注册事件处理函数:
Register event handler:
```c#
public class IocEventHandler : IEventHandler<TestEventTrigger>
{
Expand All @@ -152,7 +155,7 @@ ServiceAnt 支持以下两种方式注册处理函数
}
```

注册请求处理函数:
Register request handler:
```c#
public class IocRequestHandler : IRequestHandler<TestRequestTrigger>
{
Expand All @@ -163,27 +166,27 @@ ServiceAnt 支持以下两种方式注册处理函数
}
}
```
<h2 id="IocIntegration"> Ioc 集成 </h2>
<h2 id="IocIntegration"> Ioc Integration </h2>

ServiceAnt 可以开箱即用,但我相信很多使用者都会希望使用 Ioc 来注册自己的事件处理函数,这样做除了便于事件处理函数的单元测试外,同时也提高了可修改性。
ServiceAnt can be used out of the box, but I believe many users will want to register their own event handler with ioc, in addition to facilitate unit testing of event handlers, but also improve the modifiability.

目前 ServiceAnt 提供了以下几种Ioc框架的集成
ServiceAnt now provides the integration of several Ioc frameworks

* Autofac
* Castle Windsor
* DotNet Core

如果有你正在使用却没有实现的Ioc框架,欢迎在 Issue 里提出,我会找时间实现,或者你可以实现以后给我PR.
If you have an unsupported Ioc framework that you are using, please feel free to ask in the issues, I will find time to implement, or you can implement it and give me a PR.

### Autofac

首先你需要安装 ServiceAnt 的 Autofac 集成:
First you need to install ServiceAnt's Autofac integration:

```
Install-Package ServiceAnt.IocInstaller.Autofac
```

然后在你的初始化代码中调用
Then in your initialization code

```c#
// replace newContainer with your project container builder
Expand All @@ -193,24 +196,24 @@ Install-Package ServiceAnt.IocInstaller.Autofac
newContainerBuilder.RegisterModule(new ServiceAntModule(System.Reflection.Assembly.GetExecutingAssembly()));
```

好的,你现在可以在你的Ioc环境中使用 ServiceAnt .  
Sure, you can now use ServiceAnt in your Ioc environment.  

> ### 关于处理函数(Handler)的自动注册
> 需要注意一下 `ServiceAntModule` 的构造函数可以接受多个程序集, 这些程序应该包含你的处理函数(Handler)所在的程序集,  
> 安装器会帮你自动把处理函数(Handler)注册到Ioc的容器中, 同时也会自动添加到 `IServiceBus`.
> ### About the handler (Handler) automatic registration
> Note that the constructor of `ServiceAntModule` can accept multiple assemblies, and these should include the assembly where your handler is in,  
> The installer will automatically register your handler to Ioc's container and will also be automatically added to `IServiceBus`.
>
> 在一些复杂的模块化系统可能在初始化模块中找出所有处理函数的程序集会比较麻烦, 但遗憾的是 `Autofac` 不象 `Castle.Windsor` 一样支持拓展的钩子,
> 所以没办法在注册时依赖时自动注册到 `IServiceBus` 中, 目前我们正在研究是否有更合适的解决方案.
> In some complex modular systems, it is maybe difficult to find all assemblies which contains handlers in the startup module, but unfortunately `Autofac` does not support extended hooks like `Castle.Windsor`,
> So there is no way to automatically register to `IServiceBus` at registering handler to container, we are currently looking at whether there is a more suitable solution.
### Castle.Windsor

类似于 Autofac , 先安装 CastleInstaller:
Similar to Autofac, install Castle's Installer first:

```
Install-Package ServiceAnt.IocInstaller.Castle
```

然后在你的初始化代码中调用
Then call in your initialization code

```c#
// replace newContainer with your project container
Expand All @@ -220,13 +223,13 @@ Install-Package ServiceAnt.IocInstaller.Castle
newContainer.Install(new ServiceAntInstaller(System.Reflection.Assembly.GetExecutingAssembly()));
```

Castle 的安装器也支持在构造函数中放入处理函数(Handler)所在的程序集, 它会自动帮你注册到 Ioc 容器和 `IServiceBus` 中,  
但和 Autofac 有一点很大不同的是, Castle的容器支持一些注册时的钩子, 它可以让安装器在注册依赖时自动帮你把处理函数(Handler)注册到`IServiceBus`.  
这样一来, 如果你的安装器是在启动模块中安装的, 那么你可以不用关心你的处理函数处于哪个程序集了, 只要你在任意模块把它注册到你 Ioc 的容器中,  
ServiceAnt都会感知到, 并且自动添加它.
Castle's installer also supports accepting the assembly of handlers in the constructor, which will automatically register handler to Ioc containers and IServiceBus,
But one big difference with `Autofac` is that Castle's container supports some registration hooks that allow the installer to automatically register your handler with IServiceBus when registering dependencies.
As a result, if your installer is installed in the startup module, you do not have to worry about which assembly your handler is in, as long as you register it to your Ioc container in any module,
ServiceAnt will be aware of, and automatically add it.

请看下面的示例代码.
初始化模块:
Please see the following sample code:
startup module:
```c#
// replace newContainer with your project container
var newContainer = new WindsorContainer();
Expand All @@ -235,24 +238,24 @@ ServiceAnt都会感知到, 并且自动添加它.
newContainer.Install(new ServiceAntInstaller());
```

模块A(在初始化模块执行后):
ModuleA(after startup module executed):
```c#
// register your component to container, hook function will automatically add it to IServiceBus
           _container.Register(Component.For<IEventHandler>().ImplementedBy<SomeConcreteEventHandler>());
```

以上的代码可以即可将事件处理函数以Ioc的方式注册到 `IServiceBus`.
The above code can register the event handler to IServiceBus by Ioc.

### DotNetCore

ServiceAnt 也支持 `Standard2.0` 所以你可以在 .netcore2.0 以上的版本中使用 ServiceAnt,  
.net core中集成 ServiceAnt 需要先安装相关的集成包:
ServiceAnt also supports `Standard2.0` so you can use ServiceAnt in versions above .netcore2.0,  
Integrating ServiceAnt in .net core requires that you install the related integration package first:

```
Install-Package ServiceAnt.IocInstaller.DotNetCore
```

然后将它集成到你的应用程序中:
Then integrate it into your application:

```c#
public void ConfigureServices(IServiceCollection services)
Expand All @@ -266,26 +269,26 @@ Install-Package ServiceAnt.IocInstaller.DotNetCore
}
```

## 异常处理与日志
## Exception handling and log

### 异常处理
ServiceAnt 在触发处理函数的过程中,可能会产生某些异常,正常情况下我们希望用户能在自己的处理函数中干掉他们,  
但如果出现了用户未处理的异常, ServiceAnt 会采取以下的默认方式处理它们:
### Exception handling
ServiceAnt may raise some exception when triggering event or request. Normally, we expect users to be able to handle them in their own handler,  
However, if a user unhandled exception occurs, ServiceAnt takes the following default approach to them:

* `Pub/Sub`: 所有异常会被捕捉, 并且记录日志消息, 但不会上抛, 也就是说某一个处理函数发生异常并不会影响其他订阅者的触发.
* `Pub/Sub`: All exceptions will be caught, and log messages, but not rethrow, which means that a handler raising exception does not affect other handlers triggering.

* `Req/Resp`: 这种模式下的异常会记录日志消息, 并被上抛, 这会中断接下来的处理函数(如果有的话).
* `Req/Resp`: Exceptions in this mode will log messages and rethrow, and not execute the rest of handler.

如果你需要更改它们的默认行为, 在触发相应的函数时你可以传入 `TriggerOption` 来控制是否忽略未处理的异常, 如下:
If you need to change their default behavior, you can pass `TriggerOption` to control whether ignore unhandled exceptions when triggering event or request:

```c#
// it will make servicebus not ignore exception when handler function raise a unhanled exception
serviceBus.Publish(testEventData, new TriggerOption(false));
```

### 日志
### Log

你如果订阅了位于 `IServiceBus` 中的 `OnLogBusMessage` 事件, 那么 `ServiceBus` 的日志消息都会通过该事件发出.  
If you subscribe to the `OnLogBusMessage` event located in `IServiceBus`, the messages from `ServiceBus` will be sent out through this event.  

```c#
serviceBus.OnLogBusMessage += (logLevel, msg, ex) =>
Expand All @@ -295,33 +298,33 @@ ServiceAnt 在触发处理函数的过程中,可能会产生某些异常,正常
};
```

## 使用ServiceAnt 的一些最佳实践
## Some best practices for using ServiceAnt

### Trigger 的命名规范
### Trigger naming conventions

为了使参与开发的成员都能快速识别出 Trigger 的使用目的和选择的通信方式, 我们建议 Pub/Sub 的 Trigger 命名以 On 开头, 如
In order for team developer to quickly realize the purpose of use of the Trigger and the communication mode, we recommend that naming the trigger of the Pub / Sub start with On, as
```c#
public class OnEntityHasChanged : IEventTrigger
{
}
```

Req/Resp 的 Trigger 以 Get 开头.
and the trigger of Req/Resp start with Get.
```c#
public class GetDataItemWithCode : IRequestTrigger
{
}
```

### 不要过于频繁地调用 ServiceBus
### Do not call ServiceBus frequently

就算在同一进程内也不要过于频繁地调用 ServiceBus (比如在循环时), 在因为ServiceBus中为了解耦引用, 将对 Trigger 与返回值都进行了序列化, 如果调用过于频繁, 毫无疑问会带来一定的性能开支, 建议你把所需的内容一次性都获取到, 而不是等到遍历时再去获取.
Do not call ServiceBus frequently even ServiceAnt work in in-process enviroment, such as in the loop, because ServiceBus in order to decouple the reference, the trigger and return values are serialized, if the call is too frequent, no doubt to bring To a certain performance costs, it is recommended that you get all the required content at once, rather than wait until the traversal to obtain.

<h2 id="Detail">为什么会有ServiceAnt</h2>
<h2 id="Detail">Why ServiceAnt</h2>

### 动机
### Motivation

起因是这样的,我们团队在开发一个企业应用时采用了DDD,然后将我们的业务逻辑拆分为了复数个限界上下文,每个上下文低耦合高内聚的.
The reason is that our team used DDD when developing an enterprise application and then split our business logic into a number of bounding contexts with low in coupling and high in cohesion per context.

但无论再怎么低耦合,总会有一些高层次的交互,这些被称为“边界点”,通常在分布式部署中,我们会选择Webapi 或者 WebServie 等远程通信手段来进行交互  

Expand Down

0 comments on commit 90c4c62

Please sign in to comment.