Zamperini


  • 首页

  • 关于

  • 标签23

  • 分类11

  • 归档56

Hystrix-实践

发表于 2017-08-13 | 分类于 hystrix

Hystrix使用

Hystrix的使用方式有两种:原生和注解
首先引入依赖Change Log:

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>${hystrix.version}</version>
</dependency>
<!--引入 Hystrix注解切面-->
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
<version>${hystrix.version}</version>
</dependency>

Hystrix提供了非常便捷的封装 – 两个可继承的抽象类HystrixCommand/HystrixObservableCommand。只需继承它们,实现run()/construct()方法,在方法中实现对需要隔离的外部依赖和逻辑,即可享受Hystrix带来的诸多好处。
HystrixCommand 有四种方法供不同场景需求:execute()/queue()/observe()/toObservable() ,HystrixObservableCommand提供observe()/toObservable()

  • 四种方法的功能:
    • execute():以同步堵塞方式执行run()
    • queue():以异步非堵塞方式执行run()
    • observe():事件注册前执行run()/construct()。HystrixCommand 会创建新线程非阻塞执行run(),HystrixObservableCommand 则会在调用程序里执行construct()。
    • toObservable():事件注册后执行run()/construct()。调用toObservable() 会直接返回一个就直接返回一个Observable<R>对象,而不去执行。当执行subscribe时,才会去执行逻辑 run()方法中的逻辑或者HystrixObservableCommand中 constrcut()返回的Observable中的逻辑。与上面一样,HystrixCommand创建一个线程来执行逻辑,而HystrixObservableCommand会直接使用调用线程执行。

首先,来简单看一看 Hystrix官方Wiki给出的例子(movie-ocs中的 xxx)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
public class CommandHelloWorld extends HystrixCommand<String> {
private final String name;
public CommandHelloWorld(String name) {
Setter setter = Setter.withGroupKey(
HystrixCommandGroupKey.Factory.asKey("helloWorldGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("helloWorldQuery")
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("helloWorldThreadPollKey"))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
.withExecutionIsolationStrategy(THREAD))//SEMAPHORE
);
super(setter);
this.name = name;
}
@Override
protected String run() {
//实现业务逻辑
System.out.println("Time:" + System.currentTimeMillis() + ",Thread:" +Thread.currentThread().getName());
return "Hello " + name + "!";
}
public static class UnitTest {
//同步模式访问
@Test
public void testSynchronous() {
assertEquals("Hello World!", new CommandHelloWorld("World").execute());
assertEquals("Hello Bob!", new CommandHelloWorld("Bob").execute());
}
//阻塞模式的异步
@Test
public void testAsynchronous1() throws Exception {
assertEquals("Hello World!", new CommandHelloWorld("World").queue().get());
assertEquals("Hello Bob!", new CommandHelloWorld("Bob").queue().get());
}
//异步
@Test
public void testAsynchronous2() throws Exception {
Future<String> fWorld = new CommandHelloWorld("World").queue();
Future<String> fBob = new CommandHelloWorld("Bob").queue();
assertEquals("Hello World!", fWorld.get());
assertEquals("Hello Bob!", fBob.get());
}
//监听模式
@Test
public void testObservable() throws Exception {
Observable<String> fWorld = new CommandHelloWorld("World").observe();
Observable<String> fBob = new CommandHelloWorld("Bob").observe();
System.out.println("This Step");
// 阻塞
assertEquals("Hello World!", fWorld.toBlocking().single());
assertEquals("Hello Bob!", fBob.toBlocking().single());
// 非阻塞模式
// - 冗长的匿名内部类
fWorld.subscribe(new Observer<String>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(String v) {
System.out.println("onNext: " + v);
}
});
/*
Time: 1501830396399, Thread: hystrix-ExampleGroup-7
This Step
*/
//非阻塞模式,省略了异常和onCompleted标记
fBob.subscribe(new Action1<String>() {
@Override
public void call(String v) {
System.out.println("onNext: " + v);
}
});
}
//仅当被订阅时,才会执行run里面的逻辑
@Test
public void toObserables() {
Observable<String> kitty = new CommandHelloWorld("Kitty").toObservable();
System.out.println("This Step");
kitty.subscribe(new Action1<String>() {
@Override
public void call(String s) {
System.out.printf(s);
}
});
}
//输出结果:
//This Step
//Time: 1501830396400, Thread: hystrix-ExampleGroup-8
//Hello Kitty!
}
}

刚说了还有一种继承 HystrixObservableCommand ,相对于HystrixCommand它只提供了observe()和toObservable()方法。那么它存在的意义是啥?

来看看它们的差别:

  • 表面的差别:除了名字不一样,前者的业务逻辑写在run()里,并直接返回结果;后者的命令逻辑写在construct()返回 Observable<R>;

  • 前者默认是线程隔离,后者是信号量隔离,换种说法:前者在新的线程中执行,后者是调用线程中执行。

  • 前者一个实例只能向调用程序发送单条结果数据;后者一个Observable, 其可以发送多条数据。

阅读全文 »

Hystrix-简介

发表于 2017-08-12 | 分类于 hystrix

#Hystrix 简介
“Excuse me? 头戴金冠 北极熊??“
set up-w300

Netflix(世界最大在线影片租赁服务商)开源的延迟容错工具包,用于隔离分布式系统之间的依赖访问。防止级联故障,使得复杂的分布式系统在错误不可避免的条件下仍具有弹性,具有自我保护的能力。

Where does the name from?

set up-w300

功能:

  • 防护并控制因依赖第三方服务导致的延时和故障;
  • 阻止级联故障;
  • 快速失败并且能快速恢复;
  • 支持失败降级和提供优雅的服务降级机制;
  • 支持近实时监控、报警以及操作控制。

应用场景:

在复杂的分布式系统里会有很多依赖,每个依赖都会不可避免地在某些时候发生故障。如果主应用没有隔离这些故障,那么就有被拖垮的风险。(mmdb大面积超时就是最有说服力的🌰)
Hystrix官网给出了这样一个例子:

一个应用依赖30个外部服务,每个服务的可用性为99.99%,那么主应用的可用性即为99.99^30 = 99.7%
0.3%的故障率意味着每月有2+小时的时间服务不可用。可用性是相当差的
当服务依赖数越多时,整体可用性越差

服务正常运行时,如下图所示:

阅读全文 »

Log4j2 格式化处理

发表于 2017-03-14 | 分类于 log4j

PatternFormatter

LogEventPatternConverter

### - LevelPatternConverter
### - ThreadPatternConvter
### - DatePatternConverter

FormattingInfo

PatternLayout

在对PatternLayout的pattern字段进行解析(PatternParser的parse(final String pattern, final boolean alwaysWriteExceptions,final boolean noConsoleNoAnsi)中进行)时,会生成 List formatters
PatternFormatter(LogEventPatternConverter,FormattingInfo) 其中LogEventPatternConverter用来将日志事件LogEvent转换为指定格式的字符串并追加到输出字符串后。

插件的初始化入口在:

PluginManager.collectPlugins里,它会创建一个PluginRegistry单例,其decodeCacheFiles方法用于从META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat文件中加载并解析插件对象,并按类别存储。目前共有6中类型插件:converter,lookup,core,configurationfactory,fileconverter,typeconverter

PatternLayout的处理:

在实例化PatternFormatter时,同时会通过PatternParser将 字符串转换成转换器列表PatternFormater(LogEventPatternConverter,FormattingInfo)。LogEventPatternConverter是转换类插件的抽象类。
下图演示的就是其对应关系:
set up-w888

阅读全文 »

Log4j2源码解析

发表于 2017-03-13 | 分类于 log4j

Log4j2 源码浅析

[TOC]

介绍Log4j2的原理之前,回顾下之前配置文件中提到的几个概念:Configuration、LoggerConfig、Logger、Appender、Layout、Filter。Log4j2的官网上给出了类图,能清晰地看清楚它们之间的关联。
从图中可以看出LoggerContext、Configuration是一对一的关系,在一般的应用中通常也都只存在一个实例(Share one ClassLoader(Standalone Application、Web Application、Java EE Applications、”Shared” Web Applications and REST Service Containers),Not OSGi Applications Logging Separation)。

  1. LoggerContext: 整个日志系统的锚点(承载全局日志上下文信息),类似于Spring的WebApplicationContext
  2. Configuration: 全局配置信息,每个LoggerContext对应一个Configuration
  3. Appender: 追加器,对应配置文件中的<Appenders>下定义的Appender,定义日志输出位置以及日志输出格式,。用户可以自定义Appender,只需继承AbstractAppender并实现append(LogEvent)方法。
    • Layout: 定义日志的输出格式
  4. Filter: 过滤器,在整个日志系统的类图中,多个地方应用。起到一个过滤的作用,用于过滤特定日志级别的日志事件。有多个地方引用Filter:日志事件进入LoggerConfig之前;进入LoggerConfig后调用任何Appender之前;进入LoggerConfig后调用特殊的Appender之前;每个Appender内部。
  5. LoggerConfig: 真正的日志操作实体,对应配置文件中的<Logger>下定义的Logger。含有全局唯一标识(名称),一般对应的是一个包目录名称。
  6. Logger: 壳,每个Logger内部都对应一个LoggerConfig(通过名称来对应)。对日志事件的操作,都交由其对应的LoggerConfig进行处理
    set up-w666
  • 在使用日志系统前,首先需要初始化日志系统:解析日志配置信息Configuration,建立日志上下文LoggerContext(属性、Appender、LogerConfig…)
  • 给定name获取日志对象Logger时,LoggerContext结合上下文信息,返回一个绑定了特定名称的LoggerConfig的Loger
  • 在执行日志操作时,会经过多层过滤(Filter),并且真正的日志操作由LoggerConfig来处理。

因此下面,将从这三方面来分析日志系统。1. 日志系统的初始化;2.获取日志对象;3.日志操作过程。其中,日志系统的初始化较其他两部分稍复杂。

1. 日志系统的初始化过程,获取LoggerContext

初始化的过程关键是 加载并解析配置文件,建立日志上下文。
初始化过程的时序图:
log初始化时序图

当应用首次调动LogManager.getLogger方法时,触发日志系统的初始化:

1
2
3
4
5
public static Logger getLogger(final Class<?> clazz) {
...
//获取LoggerContext,通过其获取指定类名称的Logger
return getContext(clazz.getClassLoader(), false).getLogger(clazz.getName());
}
阅读全文 »

Log4j2配置文件&描述

发表于 2017-03-12 | 分类于 log4j

Log4j2 配置文件及描述

前面介绍Java Log的总体情况,以及两个日志框架和具体日志实现框架间的桥接。本章主要介绍日常工作中用的比较多的Log4j2的配置信息以及解释。

1. 配置文件解析

1. 同步模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<?xml version="1.0" encoding="UTF-8"?>
<!--
全局配置
status : 这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,会看到log4j2内部各种详细输出
monitorInterval : Log4j能够自动检测修改配置文件和重新配置本身, 设置间隔秒数。
-->
<Configuration status="WARN" monitorInterval="1000">
<Properties>
<!-- 配置日志目录 -->
<Property name="LOG_HOME">./logs</Property>
</Properties>
<!--日志输出地址-->
<Appenders>
<!--输出控制台-->
<Console name="Console" target="SYSTEM_OUT">
<!-- 日志格式 -->
<PatternLayout pattern="[%level][%date{yyyy-MM-dd HH:mm:ss.SSS}][%thread][%class][%method][%line]:%message%n"/>
</Console>

<RollingRandomAccessFile name="infoLog" fileName="${LOG_HOME}/info.log"
filePattern="${LOG_HOME}/info.%d{yyyy-MM-dd}.log.gz" append="true"><!--压缩文件名-->
<PatternLayout pattern="[%level][%date{yyyy-MM-dd HH:mm:ss.SSS}][%thread][%class][%method][%line]:%message%n"/>
<!--
%c{precision} | %logger{precision} : name of the logger
%class{precision} | %C{precision}: class name of the caller
%date{pattern} | %d{pattern} :the date of the logging event
%L | %line:line number
%level : the level of the logging event
%m{nolookups}{ansi} | %msg{nolookups}{ansi} | %message{nolookups}{ansi}:application supplied message
%method | %M: method name where the logging request
%t | %tn | %thread | %threadName :name of the thread
%n :
-->
<Filters>
<ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>
<ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
<Policies>
<!--
TimeBased Triggering Policy: The policy to use to determine if a rollover should occur.
interval单位与filePattern相关 yyyy-MM-dd 一天一个文件, yyyy-MM-dd-HH 每小时一个文件
基于时间的触发策略。该策略主要是完成周期性的log文件封存工作。有两个参数:
interval,integer型,指定两次封存动作之间的时间间隔。单位:以日志的命名精度来确定单位
modulate,boolean型,说明是否对封存时间进行调制。若modulate=true,则封存时间将以0点为边界进行偏移计算。
-->
<SizeBasedTriggeringPolicy size="100 MB" />
</Policies>
</RollingRandomAccessFile>
<RollingRandomAccessFile name="errorLog" fileName="${LOG_HOME}/error.log"
filePattern="${LOG_HOME}/error.%d{yyyy-MM-dd}.log.gz" append="true">
<Filters>
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
<PatternLayout pattern="[%level][%date{yyyy-MM-dd HH:mm:ss.SSS}][%thread][%class][%method][%line]:%message%n"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
</RollingRandomAccessFile>
</Appenders>

<!--日志配置-->
<Loggers>
<logger name="experiment" level="info" additivity="false">
<appender-ref ref="infoLog"/>
<appender-ref ref="errorLog"/>
<appender-ref ref="Console"/>
</logger>
<logger name="experiment.test" level="warn" additivity="false">
<appender-ref ref="infoLog"/>
<appender-ref ref="errorLog"/>
<appender-ref ref="Console"/>
</logger>
<logger name="algorithm" level="info" additivity="false">
<appender-ref ref="infoLog"/>
<AppenderRef ref="Console"/>
</logger>

<!-- 配置日志的根节点 -->
<Root level="warn" includeLocation="true">
<AppenderRef ref="errorLog"/>
<AppenderRef ref="Console"/>
</Root>
</Loggers>

</Configuration>

2. 全异步模式

  1. 只需要将系统属性Log4jContextSelector设置为AsyncLoggerContextSelector
  2. 在配置属性文件log4j2.component.properties中配置Log4jContextSelector为AsyncLoggerContextSelector

3.混合模式

todo 修改
不需要改系统属性或者引入属性配置文件,只需要将相应的Loger换成AsyncLoger。

1
2
3
4
5
<logers>
<AsyncLogger name="experiment.test" level="trace" includeLocation="true">
<AppenderRef ref="warnLog"/>
</AsyncLogger>
</logers>
阅读全文 »
prev1…101112next
Zamperini

Zamperini

GitHub E-Mail Weibo
© 2020 Zamperini
由 Hexo 强力驱动
|
主题 — NexT.Gemini v6.0.3