1. LoggerFactory, Logger
(1)Slf4j:各种日志框架(如java logging, logback, log4j)一个抽象层,是一个简单的外观模块封装(为一组接口提供一个一致的界面,从而使得复杂的子系统与用户端分离解耦)。它允许你在后台使用任意的日志类库实现。日志是一项基本功能,如果你的应用中引用的第三方库使用了各种日志类库,会导致你应用中加入很多不同的日志类库。若都使用slf4j则可以避免这样的情况,你只需要一个日志类库实现即可。logback是一个原生的slf4j实现,log4j, JDK14, SimpleLogger也实现了slf4j。
(2)LoggerFactory:Slf4j的日志工厂。使用LoggerFactory.getLogger()方法来获取一个Logger实例。本质上它是ILoggerFactory实例的包装,这个ILoggerFactory实例会编译时绑定到具体日志库实现,getILoggerFactory()可以获取这个绑定的日志工厂实现。简单的说就是通过工厂类,提供一个用户的接口。用户可以通过这个外观接口,直接使用API实现日志的记录。而后面的具体实现由Slf4j来寻找加载。寻找的过程,就是通过类加载加载那个叫org/slf4j/impl/StaticLoggerBinder.class的文件,只要实现了这个文件的日志实现系统,都可以作为一种实现方式。如果找到很多种方式,那么就寻找一种默认的方式。
(3)Logger:日志记录接口,核心接口。使用时会返回后台具体的日志实例实现。日志级别有TRACE, DEBUG, INFO, WARN, ERROR,级别由低到高,trace(), debug(),info(),warn()和error()方法会把相应级别的日志输出到配置的appender上,支持{}占位符参数。应用中只会打印等于或大于当前配置的级别的日志。
2. LoggerContext, ch.qos.logback.classic.Logger, Level
(1)LoggerContext:Logback上下文,是ILoggerFactory工厂实现。在你的应用中可以用LoggerFactory.getILoggerFactory()获取它,getLogger()负责创建Logger实例。LoggerContext 像胶水一样把许多logback-classic组件粘合到一起,向外统一提供日志服务。通过调用StatusPrinter.print()可以打印LoggerContext的内部状态。内部状态信息在诊断与日志有关的问题时非常有用。Logback的主要优点:更快的实现、广泛充分的测试、自然原生的实现了SLF4j、支持XML或Groovy方式的配置、高效地自动重新加载配置文件、能从I/O错误中优雅地恢复、自动删除旧的日志存档、自动压缩日志文件存档、支持审慎模式(多个JVM实例上的FileAppender可以安全地写入同一个日志文件)、Lilith日志事件查看器可以处理大数据量的日志数据、条件化的配置文件可以指定不同的开发模式、更丰富的过滤器、多功能的SiftingAppender、异常堆栈包含程序库包的数据、logback-access集成到Servlet容器时提供了通过Http访问日志的功能。主要方法:
getLogger()/getLoggerList()
addListener()/getCopyOfListenerList()/removeListener()
getFrameworkPackages()/isPackagingDataEnable()/getMaxCallerDataDepth()
addTurboFilter()/getTurboFilterList()/resetTurboFilterList()
getMaxCallerDataDepth()/setMaxCallerDataDepth()
putProperty()/getProperty()
getStatusManager()/setStatusManager()
start()/isStarted()/stop()/reset()
(2)Logger:Logback的日志记录实例的实现。主要方法:
trace()/debug()/info()/warn()/error()
getLevel()/setLevel()/getEffectiveLevel()/getLoggerContext()/isAttached()
addAppender()/getAppender()/iteratorForAppenders()/callAppenders()/detachAppender()/detachAndStopAllAppenders()
isAdditive()/setAdditive():设置logger的日志记录是否发送到其所有祖先的appenders,默认为true。
readResolve()
(2)Level:定义了日志级别常数。除了TRACE, DEBUG, INFO, WARN, ERROR,还有最高级别的OFF表示关闭所有日志级别输出功能,最低级别的ALL表示打开所有日志级别。root logger的默认级别为DEBUG。
(3)日志记录的性能问题:日志消息用SLF4j的{}参数化格式,只有当请求被发送给appender时消息才会被格式化。不要用+号连接字符串的日志消息,这种字符串连接会造成性能消耗,不管消息是否被记录。避免在紧密循环里使用日志记录。记录写入本地文件里的耗时一般大约在9至12微秒。如果目的地是远程服务器上的数据库时,会增加几个毫秒。用Level.OFF关闭整个日志记录后,记录请求的消耗包括一次方法调用和一次整数比较。在CPU为3.2Ghz的Pentium D电脑上,一般需要20纳秒。
3. Configurator
BasicConfigurator, JoranConfigurator, JMXConfigurator
(1)
(2)BasicConfigurator:基本的配置器,没找到loback配置文件默认使用它进行配置。允许编程方式对Loback进行初始化和配置。它关联一个ConsoleAppender到根logger,这个appender的Layout是TTLLLayout。
(3)JoranConfigurator:Joran配置器,用于给logback-classic添加特定的配置规则。Logback的默认配置机制是调用JoranConfigurator对classpath上的默认配置文件进行处理。也可以在程序中以编程方式调用JoraConfigurator的doConfigure()对Loback进行初始化和配置。
(4)JMXConfigurator:允许通过JMX配置logback。包括用默认配置文件重新加载logback配置、用指定的URL或文件重新加载配置、设置logger的级别、取得logger的级别、
(4)
(5)
(6)
(7)
(8)
(9)
(10)
(10)
(11)
(12)
4. OutputStreamAppender
ConsoleAppender, FileAppender
(1)OutputStreamAppender:把事件添加到java.io.OutputStream,该类提供其他appender所需的基本服务。Appender最重根据方法是doAppend(),参数通常可能是“ILoggingEvent”类型。它负责以适当的格式将记录事件输出到合适的设备。其他方法大多是getter/setter,比如添加过滤器addFilter,添加对应级别的日志消息addError, addInfo, addStatus, addWarn, addWarn。Appender是最终负责输出记录事件的组件。然而它们可以把实际格式化的任务委托给Layout或Encoder对象。每个layout/encoder都关联到一个且仅一个appender。用户通常不直接实例化OutputStreamAppender对象。由于java.io.OutputStream一般无法被方便地映射到字符串,所以无法在配置文件里指定目标OutputStream对象。简而言之你不能在配置文件里配置OutputStreamAppender。它是另外三个appender的超类:ConsoleAppender、FileAppender及其直接子类RollingFileAppender。
(2)ConsoleAppender:将日志事件添加到控制台的appender。按照用户指定的encoder对事件进行格式化。更准确地说是System.out或System.err,默认为前者。encoder指定事件输出方式,target属性指定是"System.out"或"System.err"。
(3)FileAppender:把记录事件添加到文件。目标文件通过file选项指定。append属性确定是追加还是清空文件是,默认为true。prudent指定是否使用审慎模式,这允许多个JVM上的FileAppender安全地写入同一个文件,默认为false。prudent模式写记录事件时,大约消耗非prudent模式的三倍。
5. RollingFileAppender
FixedWindowRollingPolicy, TimeBasedRollingPolicy, SizeAndTimeBasedRollingPolicy
SizeBasedTriggeringPolicy, SizeAndTimeBasedFNATP
(1)RollingFileAppender:继承FileAppender,能够滚动记录文件。能在不同的时间内记录到不同的文件。必须同时设置RollingPolicy和TriggeringPolicy属性,前者表示具体的文件命名滚动策略,后者表示滚动的触发策略。可用属性有file, append, encoder, rollingPolicy, triggeringPolicy, prudent。
(2)FixedWindowRollingPolicy:固定大小窗口的滚动策略。fileNamePattern表示归档(滚动)记录文件的文件名模式,以.zip或.gz结尾还表示自动压缩归档。该选项是必需的,且必需在模式的某处包含标志“%i”。minIndex属性指定窗口索引%i的最小值,maxIndex指定窗口索引%i的最大值。由于固定窗口滚动策略需要的文件重命名操作与窗口大小一样多,所以强烈建议不要使用太大的窗口大小。当用户指定过大的窗口大小时,当前的代码会自动将窗口大小设为12。RollingPolicy的rollover()方法完成对当前记录文件的归档工作。getActiveFileName()方法计算当前记录文件的文件名。getCompressionMode()负责决定压缩模式,setParent()方法设置一个对其父appender的引用。
(3)TimeBasedRollingPolicy:基于时间的滚动和触发策略。例如根据日或月,或许是最受流行的滚动策略。同时实现了RollingPolicy接口和TriggeringPolicy接口,既负责滚动也负责触发滚动。必需属性fileNamePattern定义滚动(归档)记录文件的名字。其值应当包含文件名及“%d”格式转换符。“%d”可以包含一个java.text.SimpleDateFormat指定的日期时间模式,缺省为yyyy-MM-dd。可选属性maxHistory控制被保留的归档文件的最大数量,超出数量就删除旧文件。
(4)SizeAndTimeBasedFNATP:同时基于大小和时间的文件命名滚动和触发策略。它是TimeBasedRollingPolicy的子组件。
(5)SizeBasedTriggeringPolicy:基于文件大小的触发策略。如果如果超过maxFileSize属性指定的大小,则触发当前活动文件的滚动。
6. SocketAppender
SocketAppender/SSLSocketAppender, ServerSocketAppender/SSLServerSocketAppender, SimpleSocketServer/SimpleSSLSocketServer
(1)SocketAppender/SSLSocketAppender:发送一个ILoggingEvent(具体为ILoggingEventVO实现)到远程的日志服务器,通常是一个SocketNode。而SSLSocketAppender则支持SSL。多个SocketAppender实例可以把各自的记录输出到一个格式固定的中央记录服务器。它不关联layout,因为它运行在TCP层,把序列化的事件发送到远程SocketNode。属性有remoteHost, port, reconnectionDelay连接重试间隔, includeCallerData是否包含调用者的数据。由于TCP有缓存,为避免数据丢失,一般在退出程序之前显式地调用SocketAppender的close()方法或调用LoggerContext的stop()方法就可以了。
(2)SimpleSocketServer/SimpleSSLSocketServer:一个简单的基于SocketNode的服务器。子类SimpleSSLSocketServer则支持SSL。可以支持多个SocketAppender客户端。接收到事件后,按照本地服务器的记录策略进行记录。它有两个参数,参数port是监听端口,configFile是xml策略配置文件,可以JoranConfigurator解析。它维护一个SockNode列表,每个SocketNode是一个单独线程,用于读取远程客户端发来的记录事件。
(3)ServerSocketAppender/SSLServerSocketAppender:服务端的socket appender。与SocketAppender客户央连接到远程日志服务器并发送日志不同,SeverSocketAppender是被动侦听远程客户端的TCP连接,把产生的日志事件分发到每个远程客户端,如果没有任何客户端连接,则产生的日志将被丢弃。SSLServerSocketAppender则支持SSL连接。属性address表示监听的网络接口地址,port为端口,includeCallerData属性表示是否包含调用者的数据。ssl属性表示SSL配置。
7. SMTPAppender, SyslogAppender
(1)SMTPAppender:邮件传输appender。在固定大小的缓冲里积累记录时间,当用户指定的事件发生后,就通过email发出这些事件。默认情况下,email发送是由级别为ERROR或更高级别的记录事件触发的。主要属性SMTPHost, SMTPPort, To, From, Subject, BufferSize, Evaluator, Username, Password, STARTTLS, charsetEncoding等。
(2)SyslogAppender:向远程syslog守护进程发送日志消息。属性syslogHost服务器名,port端口默认为514,facility标识消息来源,sufficPattern指定消息中非标准部分的格式,默认为 [%thread] %logger %msg。
8. DBAppender
DataSourceConnectionSource, DriverManagerConnectionSource, JNDIConnectionSource
(1)DBAppender:把记录事件写入数据库的三张表。三张表分别是logging_event、logging_event_property和logging_event_exception。在使用DBAppender之前,这三张表必须已经被创建。目前支持所有主流数据库MySQL, Oracle, PostgreSQL, MS SQL Server, SQLite, Sybase, HSQLDB, H2D。实验表明在“标准”PC上写入一个事件到数据库大约需要10毫秒。如果使用连接池则降到大约1毫秒。请注意大多数JDBC驱动程序已经提供了连接池支持。有多种方法让logback使用DBAppender,取决于连接数据的工具和数据库本身。配置DBAppender的关键问题是设置ConnectionSource对象。
(2)ConnectionSource:该接口提供了一种可插拔地、透明地获取数据库连接的方式。现在有三种具体实现DataSourceConnectionSource、DriverManagerConnectionSource和JNDIConnectionSource。其中DriverManagerConnectionSource指定driverClass/url/user/password,以传统的JDBC方式来配置数据库连接,可以指定连接池以提高性能(如c3p0),每次调用getConnection()方法时建立一个新连接。DataSourceConnectionSource以指定数据源的方式通过javax.sql.DataSource连接数据库,JNDIConnectionSource,从JNDI变量里取得javax.sql.DataSource并用它取得java.sql.Connection。
9. SiftingAppender, AsyncAppender
(1)SiftingAppender:筛选式的appender。能按照给定的运行时属性,对记录进行分离或筛选。例如能根据用户会话对记录事件进行分离,这样每个用户生成的记录会进入不同的记录文件。它内置并且管理多个appender,这些appenders会根据MDC的键/值对作为鉴别分离器(discriminator)来动态创建。每个appender需要在配置文件里的SiftingAppender定义的内部指定。
(2)AsyncAppender:异步地记录ILoggingEvent日志事件。它仅仅是一个事件分发器,必须关联一个appender才能工作。它会把事件缓存在BlockingQueue中,并创建一个工作线程从队列头部不断地取出事件,分发给关联的appender。如果队列达到80%的满容量,后续TRACE, DEBUG和INFO级别的事件将被删除,这能提高性能。在应用程序关闭或重启前,必须关闭AsyncAppender以便清空队列,可能通过关闭LoggerContext来关闭所有的appenders。主要属性queueSize, discardingThreshold, includeCallerData, maxFlushTime, neverBlock。
(3)自定义Appender:继承AppenderBase就可以轻松地写自己的appender。AppenderBase处理过滤器、状态信息和大多数appender共享的其他功能。派生类只需要实现一个方法append(Object eventObject)。
10. Encoder, Layout
LayoutWrappingEncoder, PatternLayoutEncoder
PatternLayout, HTMLLayout, TTLLLayout, EchoLayout
(1)LayoutWrappingEncoder:Encoder负责把事件转换为字节数组并把字节数组写出到合适的输出流,核心方法是encode(), headerBytes(), footerBytes()。在以前的logback中appender依赖layout提供输出格式的灵活性,它只能把事件转换成字符串。因为有大量现存代码是基于layout接口的,所以我们需要想办法让encoder与layout实现互操作。LayoutWrappingEncoder连接了encoder和layout,它实现encoder接口,并且包裹了一个layout,layout负责把事件转换成字符串。
(2)PatternLayoutEncoder:它扩展了LayoutWrappingEncoder,且仅使用PatternLayout,按字符串模式来格式化日志消息。主要方法getPattern/setPattern, getCharset/setCharset, getLayout/setLayout, encode()。
(3)PatternLayout:Layout负责把事件转换成字符串,核心方法 doLayout(), getContentType(), getFileHeader(), getFileFooter(),只处理ILoggingEvent类型的事件,ILggingEvent事件中包含了日志消息的所有内容。PatternLayout按printf()风格的的转换符把事件格式化成字符串数据输出。主要的转换符说明(前加%),logger{length},class, date{pattern},file,caller,line,msg,method,n,level,relative,thread,mdc,throwable,nopexception,marker,property。
(4)HTMLLayout:把事件格式化成HTML表格形式,表格的每行对应于一个记录事件。HTMLLayout经常和SMTPAppender一起使用,让发出的email是好看的HTML。
(5)TTLLLayout:一个固定格式的Layout。输出格式等价于PatternLayout有固定格式%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n。创建这个Layout是为了提高性能,因为PatternLayout首次运行时通常需要40毫秒来加载格式解析类,如果对性能要求高,可以考虑用TTLLLayout。
(6)EchoLayout:直接输出事件对象,末尾添加一个行分隔符。
11. Filter
LevelFilter, ThresholdFilter, EvaluatorFilter
(1)LevelFilter:根据记录级别对记录事件进行过滤。如果事件的级别等于配置的级别,过滤器会根据onMatch和onMismatch属性指定匹配或不匹配时的响应(接受或拒绝事件)。Filter的核心方法是decide(),参数是一个ILoggingEvent实例。它返回FilterReply的一个枚举值,即DENY、NEUTRAL和ACCEPT其中之一。
(2)ThresholdFilter:临界值过滤器。过滤掉低于指定临界值的事件。当记录的级别等于或高于临界值时,ThresholdFilter的decide()方法会返回NEUTRAL;当记录级别低于临界值时,事件会被拒绝。
(3)EvaluatorFilter:封装了EventEvaluator,评估是否符合指定的条件。而EventEvaluator是抽象类,需要具体实现。其中GEventEvaluator实现以Groovy布尔表达式作为求值条件、JaninoEventEvaluator以Java布尔表达式求值。
12. TurboFilter
DuplicateMessageFilter, DynamicThresholdFilter, MarkerFilter, MDCFilter, ReconfigureOnChangeFilter
(1)TurboFilter:与上述过滤器的工作原理相似,但有两个区别。TurboFilter对象被绑定到记录上下文。因此不仅当指定的appender被使用时会调用TurboFilter,而且每次执行记录请求时也会调用它们。它们的作用范围(scope)比关联到appender的过滤器大。另外,它们在LoggingEvent创建之前被调用。TurboFilter对象过滤记录请求时不需要先初始化记录事件。TurboFilter是用来高性能地过滤记录事件的,设置可以发生在记录事件被创建之前。
(2)DuplicateMessageFilter:检查重复消息,超过一定重复次数时,抛弃重复的消息。allowedRepetitons属性指定允许的重复次数。它对原始的消息(参数还没替换之前的日志消息字符串)采用简单的字符串相等性比较。不检测消息是否相似,即使只有个别字符不同也不检测。为了检测重复消息,它需要在内部缓存里保持对旧消息的引用。缓存大小由cacheSize属性决定,默认为100。
(3)DynamicThresholdFilter:根据MDC的键与级别临界值之间的关联关系进行过滤。
(4)MarkerFilter:检查与记录事件相关联的某个特定marker是否存在,从而进行过滤。
(5)MDCFilter:检查在MDC里是否存在一个给定值。
(6)ReconfigureOnChangeFilter:当配置文件修改时,重新配置LoggerContext。
13. MDC
(1)MDC:映射诊断环境。在多线程方式实现的分布式系统中,不同的线程处理不同的客户端。为了区分不同客户端的日志消息,一个轻量级的技术是为客户端的每个记录请求添加唯一戳。MDC是这种技术的一种变体。用户把环境(context)信息放进MDC。MDC类都是静态方法,核心的有put/get/remove/clear。开发者可以把信息放进一个诊断环境,之后用其他logback组件获取这些信息。MDC是基于每个线程进行管理的。子线程自动继承其父的映射诊断环境的一个副本。典型地,当开始为新的客户端请求服务时,开发者会向MDC里插入恰当的环境信息,比如客户端id、客户端IP地址、请求参数等等。Logback组件会自动在每个记录条目里包含这些信息。
1. LogManager, Logger, LoggerContext, LoggerConfig
(1)LoggerManager:Log4j系统的锚点,类似于Slf4j中的LoggerFactory(若通过slf4j适配器来使用Log4j,则使用LoggerFactory)。应用从LogManager.getLogger()处获取一个命名的Logger。LogManager内部则是查找出一个合理的LoggerContext,然后从中获取到Logger。当Logger被创建时它将与LoggerConfig关联,LoggerConfig包含当前Logger类路径,母包路径,根LoggerConfig。通过LogManager.getContext()获取内部或外面配置进去的LoggerContext。Log4j使用了插件式的架构,整体架构比Logback要庞大和复杂。
(2)LoggerContext:日志实现的锚点,LogManager中依据不同的应用环境可能存在多个有效的LoggerContext。每一个LoggerContext 都有一个有效的Configuration,Configuration包含所有的Appender 、Filter、LoggerConfig以及StrSubstitutor引用。
(2)Logger:继承自AbstractLogger并实现了所需的方法。当配置被修改后,它将与不同的LoggerConfig相关联,这导致其行为也被改变。
(4)LoggerConfig:在Logger被声明时创建,它包含了一组用于处理事件的Appender引用,以及一组用于过滤传递给Appender事件的Filter。它被分配一个日志级别,内建的级别包含TRACE, DEBUG, INFO, WARN, ERROR 和 FATAL。
2. Configuration
DefaultConfiguration, JsonConfiguration, PropertiesConfiguration, XmlConfiguration, YamlConfiguration
(1)
(2)Configuration:主要包括找不到配置时默认使用的DefaultConfiguration,以及JsonConfiguration, PropertiesConfiguration, XmlConfiguration, YamlConfiguration。
3. Appender, Layout, Filter
(1)Appender:主要有
OutputStreamAppender, WriterAppender, ConsoleAppender, FileAppender, RollingFileAppender, RandomAccessFileAppender, RollingRandomAccessFileAppender
MemoryMappedFileAppender, JdbcAppender, JpaAppender, FlumeAppender, JeroMqAppender, JmsAppender, KafkaAppender, NoSqlAppender
HttpAppender, SmtpAppender, SocketAppender, SyslogAppender
AsyncAppender, FailoverAppender, RewriteAppender, RoutingAppender, ScriptAppenderSelector
(2)TriggeringPolicy:主要有CompositeTriggeringPolicy, CronTriggeringPolicy, OnStartupTriggeringPolicy, SizeBasedTriggeringPolicy, TimeBasedTriggeringPolicy。
(3)Layout:主要有CsvLogEventLayout, CsvParameterLayout, GelfLayout, HtmlLayout, JsonLayout, MessageLayout, PatternLayout, Rfc5424Layout, SerializedLayout, SyslogLayout, XmlLayout, YamlLayout。
(4)Filter:主要有BurstFilter, CompositeFilter, DynamicThresholdFilter, LevelRangeFilter, MapFilter, MarkerFilter, RegexFilter, ScriptFilter, StructuredDataFilter, ThreadContextMapFilter, ThresholdFilter, TimeFilter。
应用领域:客户端负载均衡,用在各种分布式存储、请求路由工具中实现负载均衡、容错、缓存和批处理
1. ILoadBalancer
BaseLoadBalancer, NoOpLoadBalancer, DynamicServerListLoadBalancer, ZoneAwareLoadBalancer
(1)ILoadBalancer:负载均衡器的通用接口。通常需要一组可路由的Server列表,操作有addServers(),getAllServers(),getReachableServers(),从服务器列表中按负载均衡策略选择一台服务器的操作chooseServer(),标记一台服务器失效的操作markServerDown()。Ribbon负载均衡器可以用在不同的客户端中,比如各种Http Client(例如Apache HttpClient, Feign, Spring RestTemplate),Eureka Client,TCP/UDP客户端、自定义的客户端等。
(2)NoOpLoadBalancer:一个noOp负载均衡器实现,即不做任何的负载均衡。
(3)BaseLoadBalancer:基本的负载均衡器实现。可以设置任意一组服务器列表,可以设置一个Ping用于检测服务器是否可用。主要成员和操作:
IClientConfig:用户设置的配置信息,用于对负载均衡器及其客户端进行配置,如初始服务器列表、连接超时配置等。默认实现类为DefaultClientConfigImpl。
IRule:负载均衡路由策略,缺省为RoundRobinRule。
IPing:用于向server发送ping以确定server是否可用。缺省为DummyPing。
List
LoadBalancerStats: 负载均衡器的运行信息
init()/initWithNiwsConfig()/close()
addServer()/addServers()
chooseServer()/markServerDown()
getAllServers()/getRechableServers()/getServerByIndex()/getServerList()
addServerListChangeListener()/addServerStatusChangeListener()
forceQuickPing()/cancelPingTask()
(3)DynamicServerListLoadBalancer:基于动态服务器列表的负载均衡器实现,继承自BaseLoadBalancer。它维护一个动态的服务器列表,在运行时列表可以动态更新。它还可以设置一个过滤器,根据过滤规则过滤掉不符合的服务器。Eureka Client默认使用该LB。主要成员和操作:
ServerList:动态服务列表获取策略,缺省为ConfigurationBasedServerList,即从IClientConfig配置中读取服务器列表
ServerListFilter:过滤器,缺省为无过滤器
ServerUpdater:服务列表更新策略,缺省为PollingServerListUpdater,即周期性的轮询方式来更新。
getServerListImpl()/setServerListImpl()
getFilter()/setFilter()
getServerListUpdater()/setServerListUpdater()
setServerList()/setServerListForZones()
updateAllServerList()/updateListOfServers()
forceQuickPing()/stopServerListRefreshing()
(4)ZoneAwareLoadBalancer:基于Zone区域的LB实现,继承自DynamicServerListLoadBalancer。在选择服务器时可避开一个zone区域内的全部服务器。度量zone可用性的主要指标是平均有效请求数,它通过使用负载均衡器的客户端对每个zone进行收集,一般是zone内未处理请求个数除以全部可用实例个数,这个值越大表示zone的可用性越差。如果有zone的平均有效请求数达到配置的阈值,它将从有效服务器列表中删除。如果有多个zone的平均有效请求数达到阈值,则删除平均有效请求数最大的那个zone。在剩余的zone列表中根据其实例个数使用PPS抽样方式选择一个zone,然后通过指定的负载均衡策略从该zone中选择一台服务器。对每个请求,重复上述过程,这样每个zone相关的负载均衡过程都是实时的,指标统计信息也会不断地更新,以用作下一次负载均衡过程。一般我们都用这个LB,可以用LoadBalancerBuilder的流式接口来快速构造。
2. IClientConfig, ServerList, ServerListFilter, ServerListUpdater
DefaultClientConfigImpl, ConfigurationBasedServerList, ZoneAffinityServerListFilter, ServerListSubsetFilter, PollingServerListUpdater, LoadBalancerStats
(1)IClientConfig:客户端配置接口。缺省实现为DefaultClientConfigImpl。定义客户端各种K/V配置,用于初始化客户端或LB的各种执行环境,例如可以配置LB的连接超时时间、重试等。配置的key为IClientConfigKey类型。其中CommonClientConfigKey类中包含了一组通用的客户端配置key。
(2)ServerList:服务列表获取策略通用接口。两个方法getInitialListOfServers(),getUpdateListOfServers()。它既可以是静态的(直接提供一组固定的服务地址),也可以是动态的(从注册中心中定期查询服务地址列表,例如Eureka)。LB缺省使用ConfigurationBasedServerList实现,它从IClientConfig配置中加载服务列表。配置的格式为
(3)ZoneAffinityServerListFilter:zone亲缘型过滤器。即过滤掉不在同一个zone的服务器(通过Server.getZone()判断),从而优先选择同一个zone下的服务器。这个过滤器可以通过在IClientConfig中激活CommonClientConfigKey.EnableZoneAffinity或CommonClientConfigKey.EnableZoneExclusivity配置项来打开,缺省是关闭的。Eureka Client使用的DefaultNIWSServerListFilter过滤器就是这个类的子类,它根据zone亲缘性和其他一个额外属性进行过滤。
(4)ServerListSubsetFilter:服务器子集过滤器,限制LB只使用服务列表的一个固定的可用子集。继承自ZoneAffinityServerListFilter。比较适用于服务列表比较庞大的情况,这时客户端与每个服务器都保持连接是没有必要的。它会定期地比较整个网络失效数和并发连接数,在固定子集中移除不健康的服务器,随机选择子集之外的服务器添加进去。可以通过CommonClientConfigKey.NIWSServerListFilterClassName这个配置项直接指定这个过滤器类。子集大小默认为20,可以通过配置项
compare():根据服务器健康情况排序子集列表,先按服务器的网络失效数排序,再按并发连接数排序
initWithNiwsConfig():使用指定的客户端配置来初始化该过滤器
getFilteredListOfServers():在服务器列表中获取一个稳定的可用子集列表。服务器不健康的判定条件包括:
1)并发连接数超过了客户端配置
2)网络失效数超过了客户端配置
3)如果上述移除的服务器数量少于强制的移除比率
(5)PollingServerListUpdater:轮询方式刷新动态服务器列表。构造时可以指定轮询频率。主要的操作start()/stop(), getLastUpdate(), getDurationSinceLastUpdateMs(), getCoreThreads(),getNumberMissedCycles()。Eureka则使用自己EurekaNotificationServerListUpdater实现,默认30秒刷新一次本地服务列表。
(6)LoadBalancerStats:LB的实时运行信息,记录了LB中每个服务器的运行统计信息,这些运行信息可以被用来作为LB策略的输入。
3. IRule
RoundRobinRule, RandomRule, BestAvailableRule, AvailabilityFilteringRule, WeightedResponseTimeRule, RetryRule,ZoneAvoidanceRule
(1)RoundRobinRule:轮询策略。使用Round Robin方式轮询index,选择index对应位置的server。基本操作choose(), getLoadBalancer()/setLoadBalancer()。它通常被作为默认的负载均衡策略,也作为其他高级策略的降级选择。
(2)RandomRule: 随机策略。随机选择一个server。
(3)BestAvailableRule:最佳可用策略。选择一个最小的并发请求的server,逐个考察Server,如果Server被标记为断路了则忽略,在选择其中ActiveRequestsCount最小的server。
(4)AvailabilityFilteringRule:可用性过滤策略。过滤掉那些因为一直连接失败的被标记为断路的后端server,并过滤掉那些有高并发连接数的后端server(active connections超过配置的阈值),并发连接数阈值配置项为
(5)WeightedResponseTimeRule:响应时间加权的轮询策略。根据响应时间分配一个weight,响应时间越长,weight越小,被选中的可能性越低。一个后台线程定期的从stats里面读取评价响应时间,为每个server计算一个weight。Weight的计算也比较简单,responsetime减去每个server自己平均的responsetime是server的权重。当刚开始运行,没有形成statas时,使用robin策略选择server。
(6)RetryRule:重试策略,对已有的策略添加重试逻辑。对选定的负载均衡策略机器使用重试机制。在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server。
(7)ZoneAvoidanceRule:zone可用性选择策略。复合判断server所在区域的性能和server的可用性选择server。使 用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前一个判断判定一个zone的运行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate用于过滤掉连接数过多的Server。
4. IPing
DummyPing, NoOpPing, PingConstant, PingUrl
(1)DummyPing:直接返回true,并实现了initWithNiwsConfig方法。
(2)NoOpPing:不去ping,直接返回true,即可用。
(3)PingConstant:固定返回某服务是否可用,默认返回true,即可用。
(4)PingUrl:Http客户端使用的IPing实现。真实的去ping某个url,判断其是否alive。Eureka Client则使用NIWSDiscoveryPing,它并未实际去ping服务器,只是假设服务可用。
5. LoadBalancerBuilder, LoadBalancerCommand
(1)LoadBalancerBuilder:LB构造器,提供流式接口快速地创建各种LB。buildDynamicServerListLoadBalancer(),buildDynamicServerListLoadBalancerWithUpdater(), buildFixedServerListLoadBalancer(),buildLoadBalancerFromConfigWithReflection()。
(2)LoadBalanderCommand:LB响应式风格的命令实现。对LB的执行流创建可观察序列,基于RxJava来实现。withLoadBalancer()关联具体的LB,submit()方法提交一个对远程Server异步执行网络操作ServerOperation,返回Observable流,每次subscribe这个流,都会对服务器列表中的Server,以负载均衡方式执行这个网络操作ServerOperation.call(比如执行这个server地址处的一个REST服务)。
6. LoadBalancingHttpClient, LoadBalancingTcpClient, LoadBalancingUdpClient
RibbonTransport, RibbonTransportFactory
(1)LoadBalancingHttpClient:负载均衡的Http客户端,可以连接不同的远程服务器,直接基于Netty HttpClient实现的内部缓存了RxNetty的HttpClient,带有一个全局大小限制和每个Server连接数限制的连接池。它必须用LoadBalancingHttpClient.Builder来创建,可以指定重试补偿策略、IClientConfig配置列表、LB实例、连接清除调度器、重试处理器等。主要方法:
createRxClient(): 为指定Server创建一个RxNetty HttpClient。注意只创建了一个Client对象,并没有创建client连接。
requestToOperation():把一个HttpClientRequest转换成可异步执行的ServerOperation。
setHostHeader(): 为指定HttpClientRequest设置"Host"头信息。
submit(request): 向LB选择的Server提交一个http请求去执行,返回Observable流。如果没有可用server,将从Observable发送一个错误。
submit(req, errorHandler, reqConfig): 向LB选择的Server提交一个请求,使用指定的LB错误重试策略和Ribbon IClientConfig配置。如果没有可用server,将从返回的Observable发送一个错误。
submit(req, config): 向LB选择的Server提交一个http请求,使用指定的Netty ClientConfig。如果没有可用的server,将从Observable发送一个错误。
submit(server, request): 向指定Server提交一个请求。
submit(server, req, reqConfig): 向指定Server提交一个请求,使用指定的Ribbon IClientConfig配置。
subscribe(): 提交Observable流的执行。
(2)LoadBalancingTcpClient:负载均衡的Tcp客户端。创建时指定IClientConfig配置,重试处理器、Pipeline配置器、连接池清除的调度线程池。主要操作:
connect(): 连接LB选择的Server。
subscribe(): 提交Observable流的执行。
getConnectionIdleTimeoutMills/getMaxConcurrentRequests/getPoolStrategy/isPoolEnabled
getClientConfig/getResponseTimeout/getLoadBalancerContext
(3)LoadBalancingUdpClient:负载均衡的Udp客户端。与Tcp客户端类似。
(4)RibbonTransport:Ribbonw传输客户端。用于创建上述三种协议的客户端。方法分别是newHttpClient(), newTcpClient(), newUdpClient(),创建时可以选择指定的LB方式,和IClentConfig配置。
(5)RibbonTransportFactory:一个依赖注入友好的Ribbon传输客户端工厂,默认实现为DefaultRibbonTransportFactory 。它能基于IClientConfig配置或直接的配置字符串来创建相应的客户端。方法内部都是委托给RibbonTransport来完成。构造参数用@Inject标注了,可以从外部注入。
7. HttpResourceGroup, HttpRequestTemplate, HttpResourceObservableCommand
(1)HttpResourceGroup:一个资源模板式的Ribbon Http客户端,带有LB功能。它通过HttpRequestTemplate方式来创建Htpp请求,底层使用了RxNetty(RxJava+Netty)来实现。它包含一组Http服务器资源,对服务器列表上的一个具体Url资源请求则用HttpRequestTemplate表示。请求Http资源时会自动在多台服务器之间进行负载均衡。它必须用HttpResourceGroup.Builder来创建,创建时可以用ClientOptions指定该客户端的配置参数,如远程服务器列表、连接超时时间、自动重试次数等。getClient()获取底下的底层HttpClient,getHeaders()获取http头信息列表。newTemplateBuilder()返回一个创建HttpRequestTempate资源对象的builder。
(2)HttpRequestTemplate
(3)HttpResourceObservableCommand:响应式风格的Http资源请求命令,是一个HystrixObservableCommand,带有Hystrix的各种功能。
8. @ClientProperties, @ResourceGroup
@TemplateName, @Http, @Hystrix, @CacheProvider, @ContentTransformerClass
@Content, @Var
(1)@ClientProperties:注解式的Http客户端的配置属性列表,每个属性为@Property,可以设置的属性与ClientOptions中的一致。Ribbon Http客户端和请求模板可以通过使用这些注解标注的Java接口来创建。这个Java接口一般是一个服务定义,类似于Spring中的Controller。它包含一组服务方法,每个方法都对应远程的一个REST资源。@ClientProperties, @ResourceGroup注解表示Http客户端HttpResourceGroup的属性,标注在接口定义上。@TemplateName, @Http等注解表示一个HttpResourceTemplate请求的属性,标注在每个方法上。Ribbon.from()通过动态代理方式创建这个接口的对象,每个接口方法的调用被直接转换成Ribbon Http请求,方法上带有@Content的参数,会通过指定的ContentTransformer转换器转换成ByteBuf格式传给底层Netty Http POST请求,返回的数据限制为ByteBuf类型,它是HTTP响应负荷的副本。
(2)@ResourceGroup:指定HttpResourceGroup类型或其子类型。标注在接口定义上。
(3)@TemplateName:指定Http请求模板名称。
(4)@Http:指定Http请求的参数。包括method, uri, headers,每个header用@Header来定义。
(5)@Hystrix:指定Http请求的cacheKey,降级处理器类型fallbackHander,响应验证器类型validator。
(6)@CacheProvider:指定cache和key和provider。
(7)@ContentTransformerClass:指定方法参数的转换器类型。
(8)@Content:Http POST请求的参数数据,标注在方法参数上。
(9)@Var:Http GET请求的参数,标注在方法参数上。
9. Ribbon
RibbonResourceFactory, ClientOptions
(1)Ribbon:用于创建HttpResourceGroup客户端, HttpResourceGroup.Builder,或带注解服务接口的动态代理。创建时可以指定客户端名称和ClientOptions配置信息,它默认委托给RibbonResourceFactory来完成工作。在有DI的应用中,为了有更好的可配置性,建议直接用RibbonResourceFactory。主要方法createHttpResourceGroup(), createHttpResourceGroupBuilder(), from()则创建动态代理。
(2)RibbonResourceFactory:Ribbon资源的抽象工厂,用于创建HttpResourceGroup或带注解接口的动态代理。默认实现为DefaultResourceFactory。对DI可以绑定DefaultResourceFactory,或者实现你自己的资源工厂。
(3)ClientOptions:创建Http客户端的配置信息。包括服务器地址列表、连接池空闲时的驱逐时间、连接超时时间、后续是否重定向、是否激活LB、最大自动重试次数、下一服务地址最大重试次数、每台机器最大连接数、最大总连接数、读数据超时时间、是否所有操作都重试。这些配置值也可以通过from()从IClientConfig配置中获取。
应用领域:服务网关,提供动态路由、监控、负载分配、多区域弹性、安全认证/权限控制、WAF防火墙、压力测试等边缘服务
1. ZuulServlet
FilterProcessor, ZuulServletFilter, ContextLifecycleFilter
(1)ZuulServlet:核心的Servlet单例,类似SpringMvc的DispatcherServlet,所有的Request都要经过ZuulServlet的处理。用于初始化和启动执行所有的ZuulFilter链。在service()方法中通过preRoute(),route()和postRoute()运行所有的filters,并传递ServletRequest和ServletResponse对象,保存到该请求的RequestContext中。具体执行过程直接被委托给FilterProcessor去执行。
(2)FilterProcessor:执行所有filer链的核心类,单例。根据filter的类型(pre,route,post),通过FilterLoader查找所有已注册的过滤器,并运行它们。zuul的大部分功能都是通过一系列filter来实现,其作用可以类比Servlet框架的Filter,或者AOP。是责任链模式的应用。核心方法preRoute()运行pre类型的过滤器,route()运行route类型的过滤器,postRoute()运行post类型的过滤器。runFilters()运行所有指定类型的过滤器。
(3)ZuulServletFilter:标准的Servlet filter,功能与ZuulServlet一样,只不过它是在Servlet规范的filter中运行zuul,而不是在servlet中。
(4)ContextLifecycleFilter:管理RequestContext的生命周期,主要是清除当前的RequestContext信息。请求上下文RequestContext通过ThreadLocal存储,需要在请求完成后删除该对象。RequestContext提供了执行filter Pipeline所需要的Context,因为Servlet是单例多线程,这就要求RequestContext即要线程安全又要Request安全。context使用ThreadLocal保存,这样每个worker线程都有一个与其绑定的RequestContext,因为worker仅能同时处理一个Request,这就保证了Request Context即是线程安全的由是Request安全的。
2. ZuulFilter
RequestContext, FilterRegistry, FilterLoader, FilterFileManager
(1)ZuulFilter:过滤器抽象基类,子类实现各种边缘服务功能。Zuul的过滤器之间没有直接相互通信,他们之间通过一个RequestContext的静态类来进行数据传递。请求的ServletRequest和ServletResponse对象保存在全局的RequestContext类中。RequestContext类中有ThreadLocal变量来记录每个请求所需要传递的状态数据。你可以编写自己的过滤器,通过FilterRegistry来显式注册。也可以用Groovy来编写过滤器,这些过滤器文件被放在Zuul Server上的特定目录下面,Zuul会定期轮询这些目录,编译后的过滤器会动态的加载到Zuul Server中以便过滤请求。Zuul中定义了四种标准过滤器类型,这些过滤器类型对应于请求的典型生命周期。包括PRE,ROUTING, POST, ERROR四种类型的filter。子类需要实现的方法:
filterType(): 指定filter类型,可以是"pre"在路由之前调用, "route"路由时调用, "post"路由完之后调用, "error"用于错误处理。
filterOrder(): 指定filter顺序,数字越小表示顺序越高,越先执行。
shouldFilter(): 是否需要执行该filter,true表示执行,false表示不执行。
run(): 需要执行的具体操作。期间可以从全局RequestContext中取出当前请求的ServletRequest和ServletResponse对象,以便完成各种功能。
(2)RequestContext:请求上下文,是一个ConcurrentHashMap容器。保存当前请求的Request和Response对象,还可以添加自己的状态数据。当一个请求到达ZuulServlet时,会创建一个线程局部的RequestContext,用于在filter链中进行数据传递。主要的方法如下,含义显而易见:
getCurrentContext()
get()/set()
getRequest()/setRequest()
getResponse()/setResponse()
getThrowable()/setThrowable()
getRouteHost()/setRouteHost()/removeRouteHost()
getFilterExecutionSummary()/setFitlerExecutionSummary()
getResponseBody()/setResponseBody()
getResponseStatusCode()/setResponseStatusCode()
addZuulRequestHeader()/getZuulRequestHeaders()
addZuulResponseHeader()/getZuulResponseHeaders()
addOriginResponseHeader()/getOriginResponseHeaders()
getRequestQueryParams()/setRequestQueryParams()
(3)FilterRegistry:过滤器注册表,单例。用于注册用户自定义的filter,每个filter在注册时有一个唯一的名称。put()注册filter,get()获取指定名称的filter。getAllFilters()获取所有filter。
(4)FilterLoader:过滤器装载器,单例。从FilterRegistry中加载显式注册的filter,同时还编译加载文件中的groovy filter。并提供查找filter的功能。
getFilter(): 按指定的名称和groovy源码获取对应的filter。
getFiltersByType(): 按filter类型查找filter列表。
putFilter(): 从文件中加载和编译filter,并且添加到当前filter列表中。
(5)FilterFileManager:Groovy filter文件管理器。它会定期轮询指定的目录列表,然后通过FilterLoader加载或更新所有的groovy filters。用户需要在应用启动时用FilterRegistry.put()显式注册自定义的过滤器,用FilterFileManager.init()注册所有放在特定目录中的groovy filters。然后配置好ZuulServlet或ZuulServletFilter即可。
3. StaticResponseFilter, SurgicalDebugFilter
(1)StaticResponseFilter:一类特殊的过滤器基类,类型为"static",顺序为0。直接从Zuul生成响应,而不是将请求转发到后端服务。子类需要实现uri()和responseBody()。如果本filter被执行,则"route"类型的filter会被绕过,uri()返回一个匹配请求URI的uri,匹配的uri请求将返回reponseBody()中的字符串。
(2)SurgicalDebugFilter:将特定请求路由到分隔的调试集群或主机。子类需要实现patternMatches(),它返回true时表示将请求路由到zuul.debug.vip或zuul.debug.host配置指定的调试主机。
4. FilterScriptManagerServlet
(1)FilterScriptManagerServlet:上传、下载、管理groovy filter脚本文件的Servlet。包括上传脚本到注册表,从注册表下载脚本,列出脚本的修改,把一个特定脚本修改设为激活可用的。
应用领域:服务容错、隔离、熔断、降级、限流、调用监控
1. HystrixCommand, HystrixObservableCommand
(1)HystrixCommand:使用命令模式包装对依赖服务的调用,并带有超时容错、性能统计、断路和隔离等功能。适用于依赖关系预期返回单个响应的情形。它本质是一个阻塞式的命令,但通过observe()/toObservable()提供异步的Observable。HystrixObservableCommand命令则直接提供了非阻塞式的命令,适用于返回多个响应。每个命令在单独线程中/信号授权下执行。可配置依赖调用超时时间,超时时间一般设为比99.5%平均时间略高即可。当调用超时时直接返回或执行fallback逻辑。为每个依赖提供一个小的线程池(或信号),如果线程池已满调用将被立即拒绝,默认不采用排队,以便快速失败。依赖调用结果分成功,失败抛出异常,超时,线程拒绝,短路。请求失败(异常,拒绝,超时,短路)时执行fallback降级逻辑。提供熔断器组件,可以自动运行或手动调用,停止当前依赖一段时间(10秒),熔断器默认错误率阈值为50%,超过将自动运行。主要方法:
execute():执行命令,同步阻塞,最后返回从依赖关系接收到的单个响应或者在发生错误时抛出异常。等价于调用queue().get()。
queue():执行命令,异步非阻塞,返回一个可以从依赖关系获得单个响应的future对象。execute()和queue()是HystrixCommand特有的方法。
run():抽象方法,实现对依赖服务的调用逻辑。除了HystrixBadRequestException异常之外,所有从run()方法抛出的异常都算作失败,并触发降级getFallback()和断路器逻辑。
construct():返回一个Observable对象,通过订阅该Observerable可以获取多个返回结果。
observe():返回一个hot observable,调用该方法时直接执行command逻辑。
toObservable():返回一个cold observable,在subscribe返回的Observable时真正执行Command逻辑。
getFallback():执行降级逻辑返回其响应。降级逻辑不依赖于任何网络,从内存中获取响应。如果非要通过网络去获取Fallback,最好再用一个HystrixCommand来包装。
resumeWithFallback():如果observe()或toObservable()失败,本方法的子类实现,提供降级执行并恢复的功能。
getCommandKey()/getCommandGroup()/getCacheKey()/getThreadPoolKey()/getMetrics():获取命令的相关属性
isCircuitBreakerOpen()/isRequestCachingEnabled():断路器或响应的缓存是否打开
isExecutionComplete()/isSuccessfulExecution()/isFailedExecution()/isResponseRejected()/isResponseTimedOut():判断命令执行的状态
isExecutedInThread()/isResponseFromCache()/isResponseFromFallback():判断执行命令是否在独立线程中,以及响应的来源
isResponseSemaphoreRejected()/isResponseThreadPoolRejected()/isResponseShortCircuited():判断执行命令响应失败原因
(2)主要的配置项:
1)依赖命名CommandKey:创建命令时可用setter配置命令key。每个CommandKey代表一个依赖抽象,相同的依赖要使用相同的CommandKey名称。依赖隔离的根本就是对相同CommandKey的依赖做隔离。
2)依赖分组CommandGroup:创建时用setter配置。命令分组用于对依赖操作分组,便于统计,汇总等。是每个命令最少配置的必选参数,在不指定ThreadPoolKey的情况下,字面值用于对不同依赖的线程池/信号区分.
3)线程池隔离ThreadPoolKey:用于依赖调用线程池的区分。当对同一业务依赖做隔离时使用CommandGroup做区分,但是对同一依赖的不同远程调用(如一个是redis 一个是http),可以使用HystrixThreadPoolKey做隔离区分。线程池属性HystrixThreadPoolProperties:包括coreSize默认为10,maxQueueSize默认为-1,queueSizeRejectionThreshold默认为5。
4)请求缓存Request-Cache:实现getCacheKey(),直接返回缓存的响应。
5)信号量隔离Semaphore:创建命令时可配置为信号量隔离方式,默认为线程池隔离。对本地代码或可以快速响应的远程调用(如redis, memcached),可以直接使用信号量隔离,降低线程隔离开销。
(3)断路器工作过程:由HystrixCircuitBreaker完成。假设电路上的依赖请求量达到一定阈值HystrixCommandProperties.circuitBreakerRequestVolumeThreshold()(默认为20),并假设依赖请求错误百分比超过阈值HystrixCommandProperties.circuitBreakerErrorThresholdPercentage()(默认为50%),然后断路器从CLOSED转换到OPEN打开,所有针对它的请求将短路。经过一段时间休眠HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds()(默认为5000ms),下一个单个请求是通过(这是HALF-OPEN半开状态)。如果请求失败,断路器将在睡眠窗口持续时间内返回到OPEN状态。如果请求成功断路器将重新转换到CLOSED。
(4)结果缓存处理:通过重载getCacheKey()实现结果的缓存。通过实现同类请求结果的缓存,可以在同一个请求Context中有效降低对依赖的实际调用次数。
(5)常用的降级模式:
1)快速失败,即无任何fallback行为,run()中失败抛异常直接出错。
2)无声失败,在getFallback中返回空响应,如null,或空list,空map。
3)默认值失败,在getFallback中返回一个静态的默认值(如true)。
4)残缺式失败,即降级时组装一个包含多个字段的复合对象,一些字段可能来自于其他请求的状态,一些可能设置成默认值。常用的字段值有cookie, 请求参数和头部,前一个请求的响应等。
5)通过远程缓存降级。有些依赖服务失败时,可能在远程缓存如redis中有已缓存的结果,getFallback中可以发一次远程缓存请求返回缓存结果。不过这次请求需要再包装成一个Command,执行fallback的线程一定要跟主线程区分开,也就是重新命名一个ThreadPoolKey。
6)主从式降级。如果从服务本身就作为主服务的降级后备,则getFallback中通过Command访问从服务获取降级结果。如果从服务是作为主服务的对等后备(比如新版代码和旧版代码的切换),则通常做一个动态配置的开关,主从命令被封装在一个外观命令后面,外观命令使用信号量隔离方式,并通过配置开关选择主从命令来执行。
2. HystrixCollapser
(1)HystrixCollapser:命令合并器。用于合并多个请求到一个单一的HystrixCommand中执行,基于时间窗口和一个可选的最大合并个数。通过的时间窗口值是10ms。主要方法:
createCommand():工厂方法,创建一个需要执行的命令HystrixCommand,在当前批次中执行。配置参数maxRequestsInBatch每个批次最大请求数默认为Integer.MAX_VALUE,timerDelayInMilliseconds等待时间窗口默认为10ms,超过该值创建新batch请求。requestCacheEnabled是否启用cache默认为true。
execute():同步执行调用。
queue()/observe()/toObservable():异步执行调用。
getCacheKey()/getCollapserKey()/getMetrics()/getScope():获取相应配置属性值。
getRequestArgument():传给HystrixCommand的请求参数。
mapResponseToRequests():实现Response和Request的映射。
3. HystrixRequestContext, HystrixRequestLog, HystrixPlugins
(1)HystrixRequestContext:请求上下文,管理当前请求scope(而不只是线程scope)的状态和生命周期。这样单个请求的多个线程可以共享请求范围内的缓存、日志事件、命令合并器中的自动化批次命令执行。通过在请求之前用HystrixRequestContext.initializeContext()初始化一个请求上下文,比如在Servlet环境中,最后用shutdown()关闭上下文。
(2)HystrixRequestLog:记录HystrixCommand的执行和日志事件,在当前请求环境中(由HystrixRequestContext定义)。主要方法getAllExecutedCommands(),getCurrentRequest(),getExecutedCommandsAsString()。
(3)HystrixPlugins:插件机制。用于动态修改Hystrix的行为。通过注册HystrixPlugins,它们将应用到所有的HstrixCommand, HystrixObservableCommand, HystrixCollapser上。
4. HystrixMetrics
HystrixCommandMetrics, HystrixThreadPoolMetrics, HystrixCollapserMetrics
(1)Hystrix命令的监控统计:由HystrixMetrics的各个子类来记录。主要的指标有totalRequests, errorCount, errorPercentage, cumulativeCount, currentConcurrentExecutionCount, executionTimeMean, executionTimePercentile, rollingCount, totalTimePercentile等。
应用领域:系统监控指标的测量和收集,采用多维时间序列数据。而Dropwizard Metrics是单维的层次化数据
1. Registry, Spectator, Id
(1)Registry:测量注册表,管理一组测量指标值。DefaultRegistry是缺省的实现。通过Id来创建各种测量指标。测量分为两类,一类是主动式的,在事件发生时被调用,Counter测量活动发生速率, Timer测量活动的时间, DistributionSummary测量事件的采样分布情况。一类是被动式的,Gauge测量一个当前值,例如当前连接数,线程数。
(2)Spectator:静态工厂,用来访问全局共享的组合注册表CompositeRegistry,里面可以添加多个Registry。
(3)Id:标识测量指标数据,一个Id包含一个名称和多个tag,从多维的视角对测量数据进行描述。
2. Meter
Counter, IntervalCounter
Timer, BucketTimer, PercentileTimer
LongTaskTimer
Gauge
DistributionSummary, BucketCounter, BucketDistributionSummary, PercentileDistributionSummary
(1)Counter:计数器。用于测量某些事件发生的速率,它记录单个时间归一化统计量。例如每秒请求数、插入操次数。相当于Dropwizard Metrics中的Counter和Meter功能的合体。操作count(), increment(), measure()返回对这个指标有采样集。IntervalCounter除了计数还记录最近一次更新的时间
(2)Timer:短时间型定时器。定时器用于测量一些事件需要多长时间。短时通常是一分钟以下。操作record()执行指定时间较短的操作,并记录其耗时,count()表示record()记录的次数,totalTime()返回纳秒级耗时,measure()返回采样的的数据集。短时间型定时器适用于大量短时的事件,这样定时会被频繁地更新。BucketTimer是基于桶更新的定时器,即定时的值保存在一个桶中。PercentileTimer用来测量百分位数的定时器,定时值保存在桶中。
(3)LongtTaskTimer:长时间型定时器。测试多个长时运行任务的耗时。主要方法activeTasks(), duration(), start(), stop()。
(4)Gauge:量规,测量某个时间点的单个值。例如当前队列大小、线程数。set()设置当前值,value()返回当前值。设置之后便不能再更新。
(5)DistributionSummary:分布概要,测量事件的采样分布。例如请求的载荷大小。操作record(), count(), totalAmount()。实现类BucketCounter基于桶函数来递增的计数器。BucketDistributionSummary基于桶函数来管理的一组采样分布概要。PercentileDistributionSummary估算百分位数的分布概要。
3. PolledMeter, ThreadPoolMonitor
(1)PolledMeter:轮询式的量规,用于定期地测量和监控某个可以更新的值,如单调递增的整型值(int或long),集合或map的大小、单调递增的计数。必须通过PolledMeter.Builder来创建。主要操作monitorMonotonicCounter(), monitorSize(), monitorValue()。注册表会持有PolledMeter对象的弱引用。如果它被垃圾回收了,则会自动从注册表中删除。
(2)ThredPoolMonitor:监控和报告ThreadPoolExecutor的各个指标。attach()方法关联指定的注册表和ThreadPoolExecutor,指标值都是调用ThreadPoolExecutor的相关方法来获取。包括:
threadpool.taskCount: ThreadPoolExecutor.getTaskCount()
threadpool.completedTaskCount: ThreadPoolExecutor.getCompletedTaskCount()
threadpool.currentThreadsBusy: ThreadPoolExecutor.getActiveCount()
threadpool.maxThreads: ThreadPoolExecutor.getMaximumPoolSize()
threadpool.poolSize: ThreadPoolExecutor.getPoolSize()
threadpool.corePoolSize: ThreadPoolExecutor.getCorePoolSize()
threadpool.queueSize: ThreadPoolExecutor.getQueue().size()
4. Clock, ManualClock
(1)Clock:系统时钟。monotonicTime()获取单调时间,即系统流逝的时间,纳秒单位。wallTime()获取挂钟时间,毫秒单位。ManualClock实现是手动时钟,允许用户显式的控制时间,通常用于单元测试。
5. BufferPoolMeter, MemoryPoolMeter, Jmx
(1)BufferPoolMeter:通过JMX BufferPoolMXBean,获取jvm缓存池度量情况,measure()就获取这两个指标值。包括:
jvm.buffer.count: 总的缓存个数
jvm.buffer.memoryUsed: 已使用的缓存字节数
(2)MemoryPoolMeter:通过JMX MemoryPoolMXBean,获取jvm内存度量情况。measure()就获取这些指标值。包括:
jvm.memory.used: 已使用内存字节数
jvm.memory.committed: 保证可用的内存字节数
jvm.memory.max: 最大内存字节数
(3)Jmx:工具类。registerStandardMXBeans()用于向注册表注册BufferPoolMeter和MemoryPoolMeter这两个度量指标。
6. GcEvent, GcLogger
(1)GcEvent:表示一个GC日志事件。包括JMX GarbageCollectorMXBean发送的GarbageCollectionNotificationInfo信息、GC名称、开始时间、GC类型(OLD, YOUNG)。
(2)GcLogger:通过JMX GarbageCollectorMXBean来收集GC日志信息,并向全局注册表注册相关度量指标。start()开始收集,stop()结束收集。getLogs()返回当前的GC日志事件列表。度量指标包括:
jvm.gc.maxDataSize: 年老代内存池的最大字节数,一个Gauge。
jvm.gc.liveDataSize: Full GC之后的年老代占用字节数,一个Gauge。
jvm.gc.promotionRate:提升速率的Counter。每秒从年经代提升到年老代的字节数,相当于abs(oldGen.sizeAfterGC - oldGen.sizeBeforeGC)
jvm.gc.allocationRate: 分配速率的Counter。每秒分配的年经代字节数。相当于youngGen.sizeBeforeGC - youngGen.sizeAfterGC
jvm.gc.pause: 统计GC事件的暂停毫秒时间的Timer,该指标数据关联一个GC Action的tag,一个触发收集GC Cause的tag。其中max属性表示最大暂停时间,count表示每秒暂停次数,totalTime表示全部暂停的总耗时(秒数)。
jvm.gc.concurrentPhaseTime: 统计CMS并发收集的毫秒耗时的Timer。其中max属性表示CMS最大时间,count表示每秒CMS次数,totalTime表示全部CMS的总耗时(秒数)。
7. Spectator Log4j
(1)SpectatorAppender:跟踪Log4j 2日志消息测量指标的appender,在Log4j 2配置文件中将此
log4j.numMessages: 日志产生速率,一个Counter。显示每秒传给appender的日志消息数据。维信息appender表示appender名称,logLevel表示事件的标准日志级别。
log4j.numStackTraces: 有堆栈信息的消息产生速率,一个Counter。只有在SpectatorAppender的ignoreExceptions标志设为false时才会收集。维信息appender表示appender名称,logLevel表示消息的级别,exception表示异常的类型,file表示异常抛出的文件名。
(2)其他的Spectator扩展:包括把Spectator集成到aws, spark, spring web等系统的工具。