Skip to content

Commit

Permalink
hystrix-javanica: initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
dmgcodevil committed Mar 6, 2014
1 parent 0de2eb6 commit c9a73cd
Show file tree
Hide file tree
Showing 46 changed files with 2,763 additions and 0 deletions.
169 changes: 169 additions & 0 deletions hystrix-contrib/hystrix-javanica/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# hystrix-javanica

Java language has a great advantages over other languages such as reflection and annotations.
All modern frameworks such as Spring, Hibernate, myBatis and etc. seek to use this advantages to the maximum.
The idea of introduction annotations in Hystrix is obvious solution for improvement. Currently using Hystrix involves writing a lot of code that is a barrier to rapid development. You likely be spending a lot of time on writing a Hystrix commands. Idea of the Javanica project is make easier using of Hystrix by the introduction of support annotations.

First of all in order to use hystrix-javanica you need to add hystrix-javanica dependency in your project.

Example for Maven:
```xml
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
<version>x.y.z</version>
</dependency>
```

To implement AOP functionality in the project was used AspectJ library. If in your project already used AspectJ then you need to add hystrix aspect in aop.xml as below:
```xml
<aspects>
...
<aspect name="com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixAspect"/>
...
</aspects>
```
More about AspectJ configuration read [here] (http://www.eclipse.org/aspectj/doc/next/devguide/ltw-configuration.html)


If you use Spring AOP in your project then you need to add specific configuration using Spring aop namespace in order to make Spring capable to manage aspects which were written using AspectJ and declare HystrixAspect as Spring bean like below:

```xml
<aop:aspectj-autoproxy/>
<bean id="hystrixAspect" class="com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixAspect"></bean>
```

It doesn't matter which approach you use to create proxies in Spring, javanica works fine with JDK and CGLIB proxies. If you use another framework for aop which supports AspectJ and uses other libs (Javassist for instance) to create proxies then let us know what lib you use to create proxies and we'll try to add support for this library in near future.

More about Spring AOP + AspectJ read [here] (http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/aop.html)

# How to use

## Hystrix command

To run method as Hystrix command you need to annotate method with @HystrixCommand annotation, for example
```java
@HystrixCommand(fallbackMethod = "defaultUser")
public User getUserById(String id) {
return userResource.getUserById(id);
}

private User defaultUser(String id) {
return new User();
}
```
In example above the 'getUserById' method will be processed [synchronously](https://github.com/Netflix/Hystrix/wiki/How-To-Use#wiki-Synchronous-Execution) as Hystrix command. Fallback method can be private or public. Method 'defaultUser' will be used to process fallback logic in a case of any errors. If you need to run 'defaultUser' as command then you can annotate it with HystrixCommand annotation as below:
```java
@HystrixCommand(fallbackMethod = "defaultUser")
public User getUserById(String id) {
return userResource.getUserById(id);
}

@HystrixCommand
private User defaultUser(String id) {
return new User();
}
```

If fallback method was marked with @HystrixCommand then this method also can has own fallback method, as in the example below:
```java
@HystrixCommand(fallbackMethod = "defaultUser")
public User getUserById(String id) {
return userResource.getUserById(id);
}

@HystrixCommand(fallbackMethod = "defaultUserSecond")
private User defaultUser(String id) {
return new User();
}

@HystrixCommand
private User defaultUserSecond(String id) {
return new User("def", "def");
}
```

Its important to remember that Hystrix command and fallback should be placed in the same class.

To process Hystrix command asynchronously you should return an instance of AsyncCommand in your command method as in the exapmple below:
```java
@HystrixCommand
public Future<User> getUserByIdAsync(final String id) {
return new AsyncCommand<User>() {
@Override
public User invoke() {
return userResource.getUserById(id);
}
};
}
```

The return type of command method should be Future that indicates that a command should be executed [asynchronously] (https://github.com/Netflix/Hystrix/wiki/How-To-Use#wiki-Asynchronous-Execution).

Command properties can be set using commandProperties field like below:
```java

@HystrixCommand(fallbackMethod = "defaultUser",
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500")
})
public User getUserById(String id) {
return userResource.getUserById(id);
}
```


Javanica sets command properties using Hystrix ConfigurationManager.
For the example above Javanica behind the scenes performs next action:
```java
ConfigurationManager.getConfigInstance().setProperty("hystrix.command.getUserById.execution.isolation.thread.timeoutInMilliseconds", "500");
```

More about Hystrix command properties [command](https://github.com/Netflix/Hystrix/wiki/Configuration#wiki-CommandExecution) and [fallback](https://github.com/Netflix/Hystrix/wiki/Configuration#wiki-CommandFallback)

## Hystrix collapser

Suppose you have some command which calls should be collapsed in one backend call. For this goal you can use HystrixCollapser annotation.

Hystrix command:
```java
@HystrixCommand
public User getUserById(String id) {
return userResource.getUserById(id);
}
```

Asynchronous call:
```java
@HystrixCollapser(commandMethod = "getUserById",
collapserProperties = {@HystrixProperty(name = "timerDelayInMilliseconds", value = "200")})
public Future<User> getUserByIdCollapserAsync(String id) {
return CollapserResult.async();
}
```

Synchronous call:
```java
@HystrixCollapser(commandMethod = "getUserById",
collapserProperties = {@HystrixProperty(name = "maxRequestsInBatch", value = "3")})
@Override
public User getUserByIdCollapser(String id) {
return CollapserResult.sync();
}
```

Lines:
```java
return CollapserResult.async();
return CollapserResult.sync();
```
In examples above don't affect the result of a collapser call. This just to avoid ```return null;``` statement in code.
It's important to remember that Hystrix command and сollapser should be placed in the same class.

Read more about Hystrix request collapsing [here] (https://github.com/Netflix/Hystrix/wiki/How-it-Works#wiki-RequestCollapsing)

#Development Status and Future

```java
todo
```
38 changes: 38 additions & 0 deletions hystrix-contrib/hystrix-javanica/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'

aspectjVersion = '1.7.4'

dependencies {
compile project(':hystrix-core')

compile "org.aspectj:aspectjweaver:$aspectjVersion"
compile "org.aspectj:aspectjrt:$aspectjVersion"

compile 'com.google.guava:guava:15.0'
compile 'commons-collections:commons-collections:3.2.1'
compile 'org.apache.commons:commons-lang3:3.1'
testCompile 'junit:junit-dep:4.10'
testCompile 'org.springframework:spring-core:4.0.0.RELEASE'
testCompile 'org.springframework:spring-context:4.0.0.RELEASE'
testCompile 'org.springframework:spring-aop:4.0.0.RELEASE'
testCompile 'org.springframework:spring-test:4.0.0.RELEASE'
testCompile 'cglib:cglib:3.1'
}

eclipse {
classpath {
// include 'provided' dependencies on the classpath
plusConfigurations += configurations.provided
downloadSources = true
downloadJavadoc = true
}
}

idea {
module {
// include 'provided' dependencies on the classpath
scopes.COMPILE.plus += configurations.provided
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Copyright 2012 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.hystrix.contrib.javanica.annotation;

import com.netflix.hystrix.HystrixCollapser.Scope;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* This annotation is used to mark some methods to collapse some commands into a single backend dependency call.
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HystrixCollapser {

/**
* Specifies a collapser key.
* <p/>
* default => the name of annotated method.
*
* @return collapser key.
*/
String collapserKey() default "";

/**
* Defines what scope the collapsing should occur within.
* <p/>
* default => the {@link Scope#REQUEST}.
*
* @return {@link Scope}
*/
Scope scope() default Scope.REQUEST;

/**
* Specifies name of a method which has @HystrixCommand annotation and should be collapsed.
*
* @return method name.
*/
String commandMethod();

/**
* Specifies collapser properties.
*
* @return collapser properties
*/
HystrixProperty[] collapserProperties() default {};

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/**
* Copyright 2012 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.hystrix.contrib.javanica.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
* This annotation used to specify some methods which should be processes as hystrix commands.
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HystrixCommand {

/**
* The command group key is used for grouping together commands such as for reporting,
* alerting, dashboards or team/library ownership.
* <p/>
* default => the runtime class name of annotated method
*
* @return group key
*/
String groupKey() default "";

/**
* Hystrix command key.
* <p/>
* default => the name of annotated method. for example:
* <code>
* ...
* @HystrixCommand
* public User getUserById(...)
* ...
* the command name will be: 'getUserById'
* </code>
*
* @return command key
*/
String commandKey() default "";

/**
* The thread-pool key is used to represent a
* HystrixThreadPool for monitoring, metrics publishing, caching and other such uses.
*
* @return thread pool key
*/
String threadPoolKey() default "";

/**
* Specifies a method to process fallback logic.
* A fallback method should be defined in the same class where is HystrixCommand.
* Also a fallback method should have same signature to a method which was invoked as hystrix command.
* for example:
* <code>
* @HystrixCommand(fallbackMethod = "getByIdFallback")
* public String getById(String id) {...}
*
* private String getByIdFallback(String id) {...}
* </code>
* Also a fallback method can be annotated with {@link HystrixCommand}
* <p/>
* default => see {@link com.netflix.hystrix.contrib.javanica.command.GenericCommand#getFallback()}
*
* @return method name
*/
String fallbackMethod() default "";

/**
* Specifies command properties.
*
* @return command properties
*/
HystrixProperty[] commandProperties() default {};
}

Loading

0 comments on commit c9a73cd

Please sign in to comment.