├── README.md ├── src ├── main │ ├── webapp │ │ ├── index.jsp │ │ └── WEB-INF │ │ │ ├── spring-servlet.xml │ │ │ └── web.xml │ ├── resources │ │ ├── logback.xml │ │ └── applicationContext.xml │ └── java │ │ └── com │ │ └── xianlinbox │ │ └── hystrix │ │ ├── model │ │ ├── Contact.java │ │ ├── Customer.java │ │ └── Address.java │ │ ├── filter │ │ └── HystrixRequestContextServletFilter.java │ │ ├── dao │ │ ├── AddressDao.java │ │ ├── ContactDao.java │ │ ├── ContactHystrixCommand.java │ │ ├── AddressHystrixCommand.java │ │ └── HystrixCommandAdvice.java │ │ ├── controller │ │ └── CustomerController.java │ │ └── service │ │ └── CustomerService.java └── test │ └── java │ ├── com │ └── xianlinbox │ │ └── hystrix │ │ └── service │ │ └── CustomerServiceTest.java │ └── demo │ ├── SemaphoreEchoCommand.java │ ├── ThreadEchoCommand.java │ ├── DemoTest.java │ ├── CommandCollapserGetValueForKey.java │ ├── CollapseEchoHystrixCommand.java │ └── HystrixCommandTest.java └── .gitignore /README.md: -------------------------------------------------------------------------------- 1 | ###A Demo with Spring MVC 4 and Hystrix 2 | -------------------------------------------------------------------------------- /src/main/webapp/index.jsp: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Hello World!

4 | 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse 2 | .classpath 3 | .project 4 | .settings/ 5 | 6 | # Intellij 7 | *.ipr 8 | .idea/ 9 | *.iml 10 | *.iws 11 | 12 | # Mac 13 | .DS_Store 14 | 15 | # Maven 16 | log/ 17 | target/ 18 | build/ 19 | out/ 20 | 21 | # Gradle 22 | .gradle/ 23 | activemq-data/* 24 | 25 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/main/java/com/xianlinbox/hystrix/model/Contact.java: -------------------------------------------------------------------------------- 1 | package com.xianlinbox.hystrix.model; 2 | 3 | public class Contact { 4 | private String phone; 5 | private String mobile; 6 | private String email; 7 | 8 | public String getPhone() { 9 | return phone; 10 | } 11 | 12 | public void setPhone(String phone) { 13 | this.phone = phone; 14 | } 15 | 16 | public String getMobile() { 17 | return mobile; 18 | } 19 | 20 | public void setMobile(String mobile) { 21 | this.mobile = mobile; 22 | } 23 | 24 | public String getEmail() { 25 | return email; 26 | } 27 | 28 | public void setEmail(String email) { 29 | this.email = email; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/com/xianlinbox/hystrix/service/CustomerServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.xianlinbox.hystrix.service; 2 | 3 | import com.xianlinbox.hystrix.model.Customer; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import static org.hamcrest.CoreMatchers.equalTo; 8 | import static org.junit.Assert.assertThat; 9 | 10 | public class CustomerServiceTest { 11 | private CustomerService service; 12 | 13 | @Before 14 | public void setUp() throws Exception { 15 | service = new CustomerService(); 16 | } 17 | 18 | @Test 19 | public void testGetCustomer() throws Exception { 20 | Customer customer = service.getCustomer("1234"); 21 | assertThat(customer.getId(), equalTo("1234")); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/spring-servlet.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main/java/com/xianlinbox/hystrix/model/Customer.java: -------------------------------------------------------------------------------- 1 | package com.xianlinbox.hystrix.model; 2 | 3 | public class Customer { 4 | private String id; 5 | private String name; 6 | private Contact contact; 7 | private Address address; 8 | 9 | public Customer(String id, String name) { 10 | this.id = id; 11 | this.name = name; 12 | } 13 | 14 | public void setContact(Contact contact) { 15 | this.contact = contact; 16 | } 17 | 18 | public void setAddress(Address address) { 19 | this.address = address; 20 | } 21 | 22 | public String getName() { 23 | return name; 24 | } 25 | 26 | public Contact getContact() { 27 | return contact; 28 | } 29 | 30 | public Address getAddress() { 31 | return address; 32 | } 33 | 34 | public String getId() { 35 | return id; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/xianlinbox/hystrix/filter/HystrixRequestContextServletFilter.java: -------------------------------------------------------------------------------- 1 | package com.xianlinbox.hystrix.filter; 2 | 3 | import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; 4 | 5 | import javax.servlet.*; 6 | import java.io.IOException; 7 | 8 | public class HystrixRequestContextServletFilter implements Filter { 9 | @Override 10 | public void init(FilterConfig filterConfig) throws ServletException { 11 | } 12 | 13 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 14 | throws IOException, ServletException { 15 | HystrixRequestContext context = HystrixRequestContext.initializeContext(); 16 | try { 17 | chain.doFilter(request, response); 18 | } finally { 19 | context.shutdown(); 20 | } 21 | } 22 | 23 | @Override 24 | public void destroy() { 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/xianlinbox/hystrix/model/Address.java: -------------------------------------------------------------------------------- 1 | package com.xianlinbox.hystrix.model; 2 | 3 | public class Address { 4 | private String country; 5 | private String province; 6 | private String city; 7 | private String detail; 8 | 9 | public String getCountry() { 10 | return country; 11 | } 12 | 13 | public void setCountry(String country) { 14 | this.country = country; 15 | } 16 | 17 | public String getProvince() { 18 | return province; 19 | } 20 | 21 | public void setProvince(String province) { 22 | this.province = province; 23 | } 24 | 25 | public String getCity() { 26 | return city; 27 | } 28 | 29 | public void setCity(String city) { 30 | this.city = city; 31 | } 32 | 33 | public String getDetail() { 34 | return detail; 35 | } 36 | 37 | public void setDetail(String detail) { 38 | this.detail = detail; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/xianlinbox/hystrix/dao/AddressDao.java: -------------------------------------------------------------------------------- 1 | package com.xianlinbox.hystrix.dao; 2 | 3 | import com.xianlinbox.hystrix.model.Address; 4 | import org.apache.http.client.fluent.Request; 5 | import org.codehaus.jackson.map.ObjectMapper; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.IOException; 10 | 11 | public class AddressDao { 12 | private Logger logger = LoggerFactory.getLogger(AddressDao.class); 13 | 14 | public Address getAddress(String customerId) throws IOException { 15 | logger.info("Get address for customer {}", customerId); 16 | String response = Request.Get("http://localhost:9090/customer/" + customerId + "/address") 17 | .connectTimeout(1000) 18 | .socketTimeout(1000) 19 | .execute() 20 | .returnContent() 21 | .asString(); 22 | 23 | return new ObjectMapper().readValue(response, Address.class); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/xianlinbox/hystrix/dao/ContactDao.java: -------------------------------------------------------------------------------- 1 | package com.xianlinbox.hystrix.dao; 2 | 3 | import com.xianlinbox.hystrix.model.Contact; 4 | import org.apache.http.client.fluent.Request; 5 | import org.codehaus.jackson.map.ObjectMapper; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.IOException; 10 | 11 | public class ContactDao { 12 | private Logger logger = LoggerFactory.getLogger(ContactDao.class); 13 | 14 | public Contact getContact(String customerId) throws IOException { 15 | logger.info("Get contact for customer {}", customerId); 16 | String response = Request.Get("http://localhost:9090/customer/" + customerId + "/contact") 17 | .connectTimeout(1000) 18 | .socketTimeout(1000) 19 | .execute() 20 | .returnContent() 21 | .asString(); 22 | 23 | return new ObjectMapper().readValue(response, Contact.class); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/xianlinbox/hystrix/controller/CustomerController.java: -------------------------------------------------------------------------------- 1 | package com.xianlinbox.hystrix.controller; 2 | 3 | import com.xianlinbox.hystrix.model.Customer; 4 | import com.xianlinbox.hystrix.service.CustomerService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.PathVariable; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RequestMethod; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | @RestController 12 | public class CustomerController { 13 | 14 | @Autowired 15 | private CustomerService customerService; 16 | 17 | @RequestMapping(value = "/customers/{customerId}", method = RequestMethod.GET) 18 | public Customer getCustomer(@PathVariable String customerId) { 19 | // return new CustomerService().getCustomer(customerId); 20 | return customerService.getCustomerThroughDao(customerId); 21 | } 22 | 23 | public void setCustomerService(CustomerService customerService) { 24 | this.customerService = customerService; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/demo/SemaphoreEchoCommand.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import com.netflix.hystrix.HystrixCommand; 4 | import com.netflix.hystrix.HystrixCommandGroupKey; 5 | import com.netflix.hystrix.HystrixCommandKey; 6 | import com.netflix.hystrix.HystrixCommandProperties; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | public class SemaphoreEchoCommand extends HystrixCommand { 11 | private Logger logger = LoggerFactory.getLogger(ThreadEchoCommand.class); 12 | 13 | private String input; 14 | 15 | protected SemaphoreEchoCommand(String input) { 16 | super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("Semaphore Echo")) 17 | .andCommandKey(HystrixCommandKey.Factory.asKey("Echo")) 18 | .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() 19 | .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE) 20 | .withExecutionIsolationSemaphoreMaxConcurrentRequests(2))); 21 | this.input = input; 22 | } 23 | 24 | @Override 25 | protected String run() throws Exception { 26 | logger.info("Run command with input: {}", input); 27 | Thread.currentThread().sleep(100); 28 | return "Echo: " + input; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/xianlinbox/hystrix/dao/ContactHystrixCommand.java: -------------------------------------------------------------------------------- 1 | package com.xianlinbox.hystrix.dao; 2 | 3 | import com.netflix.hystrix.HystrixCommand; 4 | import com.netflix.hystrix.HystrixCommandGroupKey; 5 | import com.xianlinbox.hystrix.model.Contact; 6 | import org.apache.http.client.fluent.Request; 7 | import org.codehaus.jackson.map.ObjectMapper; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | public class ContactHystrixCommand extends HystrixCommand { 12 | private Logger logger = LoggerFactory.getLogger(ContactHystrixCommand.class); 13 | private String customerId; 14 | 15 | public ContactHystrixCommand(String customerId) { 16 | super(HystrixCommandGroupKey.Factory.asKey("Contact")); 17 | this.customerId = customerId; 18 | } 19 | 20 | @Override 21 | public Contact run() throws Exception { 22 | logger.info("Get contact for customer {}", customerId); 23 | String response = Request.Get("http://localhost:9090/customer/" + customerId + "/contact") 24 | .connectTimeout(1000) 25 | .socketTimeout(1000) 26 | .execute() 27 | .returnContent() 28 | .asString(); 29 | 30 | return new ObjectMapper().readValue(response, Contact.class); 31 | } 32 | 33 | @Override 34 | protected Contact getFallback() { 35 | logger.info("Met error, using fallback value: {}", customerId); 36 | return null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/xianlinbox/hystrix/dao/AddressHystrixCommand.java: -------------------------------------------------------------------------------- 1 | package com.xianlinbox.hystrix.dao; 2 | 3 | import com.netflix.hystrix.HystrixCommand; 4 | import com.netflix.hystrix.HystrixCommandGroupKey; 5 | import com.xianlinbox.hystrix.model.Address; 6 | import org.apache.http.client.fluent.Request; 7 | import org.codehaus.jackson.map.ObjectMapper; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | public class AddressHystrixCommand extends HystrixCommand
{ 12 | private Logger logger = LoggerFactory.getLogger(AddressHystrixCommand.class); 13 | private String customerId; 14 | 15 | public AddressHystrixCommand(String customerId) { 16 | super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("Address"))); 17 | this.customerId = customerId; 18 | } 19 | 20 | @Override 21 | public Address run() throws Exception { 22 | logger.info("Get address for customer {}", customerId); 23 | String response = Request.Get("http://localhost:9090/customer/" + customerId + "/address") 24 | .connectTimeout(1000) 25 | .socketTimeout(1000) 26 | .execute() 27 | .returnContent() 28 | .asString(); 29 | 30 | return new ObjectMapper().readValue(response, Address.class); 31 | } 32 | 33 | @Override 34 | protected Address getFallback() { 35 | logger.info("Met error, using fallback value: {}", customerId); 36 | return null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/xianlinbox/hystrix/dao/HystrixCommandAdvice.java: -------------------------------------------------------------------------------- 1 | package com.xianlinbox.hystrix.dao; 2 | 3 | import com.netflix.hystrix.HystrixCommand; 4 | import com.netflix.hystrix.HystrixCommandGroupKey; 5 | import com.netflix.hystrix.HystrixCommandKey; 6 | import org.aspectj.lang.ProceedingJoinPoint; 7 | 8 | public class HystrixCommandAdvice { 9 | private String groupName; 10 | private String commandName; 11 | 12 | public Object runCommand(final ProceedingJoinPoint pjp) { 13 | return wrapWithHystrixCommnad(pjp).execute(); 14 | } 15 | 16 | private HystrixCommand wrapWithHystrixCommnad(final ProceedingJoinPoint pjp) { 17 | return new HystrixCommand(setter()) { 18 | @Override 19 | protected Object run() throws Exception { 20 | try { 21 | return pjp.proceed(); 22 | } catch (Throwable throwable) { 23 | throw (Exception) throwable; 24 | } 25 | } 26 | 27 | @Override 28 | protected Object getFallback() { 29 | return null; 30 | } 31 | }; 32 | } 33 | 34 | private HystrixCommand.Setter setter() { 35 | return HystrixCommand.Setter 36 | .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupName)) 37 | .andCommandKey(HystrixCommandKey.Factory.asKey(commandName)); 38 | } 39 | 40 | public void setGroupName(String groupName) { 41 | this.groupName = groupName; 42 | } 43 | 44 | public void setCommandName(String commandName) { 45 | this.commandName = commandName; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/demo/ThreadEchoCommand.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import com.netflix.hystrix.*; 4 | import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategyDefault; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | public class ThreadEchoCommand extends HystrixCommand { 9 | public static final HystrixCommandKey COMMAND_KEY = HystrixCommandKey.Factory.asKey("Echo"); 10 | private Logger logger = LoggerFactory.getLogger(ThreadEchoCommand.class); 11 | 12 | private String input; 13 | 14 | protected ThreadEchoCommand(String input) { 15 | super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("EchoGroup")) 16 | .andCommandKey(COMMAND_KEY) 17 | .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("EchoThreadPool")) 18 | .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() 19 | .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)) 20 | .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter() 21 | .withCoreSize(10)) 22 | ); 23 | this.input = input; 24 | } 25 | 26 | @Override 27 | protected String run() throws Exception { 28 | logger.info("Run command with input: {}", input); 29 | Thread.currentThread().sleep(1); 30 | return "Echo: " + input; 31 | } 32 | 33 | @Override 34 | protected String getCacheKey() { 35 | return input; 36 | } 37 | 38 | public static void flushCache(String cacheKey) { 39 | HystrixRequestCache.getInstance(COMMAND_KEY, 40 | HystrixConcurrencyStrategyDefault.getInstance()).clear(cacheKey); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/xianlinbox/hystrix/service/CustomerService.java: -------------------------------------------------------------------------------- 1 | package com.xianlinbox.hystrix.service; 2 | 3 | import com.xianlinbox.hystrix.dao.AddressDao; 4 | import com.xianlinbox.hystrix.dao.AddressHystrixCommand; 5 | import com.xianlinbox.hystrix.dao.ContactDao; 6 | import com.xianlinbox.hystrix.dao.ContactHystrixCommand; 7 | import com.xianlinbox.hystrix.model.Customer; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | public class CustomerService { 12 | private Logger logger = LoggerFactory.getLogger(CustomerService.class); 13 | 14 | private ContactDao contactDao; 15 | private AddressDao addressDao; 16 | 17 | public Customer getCustomer(String customerId) { 18 | logger.info("Get Customer {}", customerId); 19 | try { 20 | Customer customer = new Customer(customerId, "xianlinbox"); 21 | customer.setContact(new ContactHystrixCommand(customerId).execute()); 22 | customer.setAddress(new AddressHystrixCommand(customerId).execute()); 23 | return customer; 24 | } catch (Exception e) { 25 | e.printStackTrace(); 26 | } 27 | return null; 28 | } 29 | 30 | public Customer getCustomerThroughDao(String customerId) { 31 | logger.info("Get Customer {}", customerId); 32 | try { 33 | Customer customer = new Customer(customerId, "xianlinbox"); 34 | customer.setContact(contactDao.getContact(customerId)); 35 | customer.setAddress(addressDao.getAddress(customerId)); 36 | return customer; 37 | } catch (Exception e) { 38 | e.printStackTrace(); 39 | } 40 | return null; 41 | } 42 | 43 | public void setContactDao(ContactDao contactDao) { 44 | this.contactDao = contactDao; 45 | } 46 | 47 | public void setAddressDao(AddressDao addressDao) { 48 | this.addressDao = addressDao; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/demo/DemoTest.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import com.netflix.hystrix.HystrixCommand; 4 | import com.netflix.hystrix.HystrixEventType; 5 | import com.netflix.hystrix.HystrixRequestLog; 6 | import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; 7 | import org.junit.Test; 8 | 9 | import java.util.concurrent.Future; 10 | 11 | import static junit.framework.TestCase.assertTrue; 12 | import static org.junit.Assert.assertEquals; 13 | 14 | public class DemoTest { 15 | @Test 16 | public void testCollapser() throws Exception { 17 | HystrixRequestContext context = HystrixRequestContext.initializeContext(); 18 | try { 19 | Future f1 = new CommandCollapserGetValueForKey(1).queue(); 20 | Future f2 = new CommandCollapserGetValueForKey(2).queue(); 21 | Future f3 = new CommandCollapserGetValueForKey(3).queue(); 22 | Future f4 = new CommandCollapserGetValueForKey(4).queue(); 23 | 24 | assertEquals("ValueForKey: 1", f1.get()); 25 | assertEquals("ValueForKey: 2", f2.get()); 26 | assertEquals("ValueForKey: 3", f3.get()); 27 | assertEquals("ValueForKey: 4", f4.get()); 28 | 29 | // assert that the batch command 'GetValueForKey' was in fact 30 | // executed and that it executed only once 31 | assertEquals(1, HystrixRequestLog.getCurrentRequest().getExecutedCommands().size()); 32 | HystrixCommand command = HystrixRequestLog.getCurrentRequest().getExecutedCommands().toArray(new HystrixCommand[1])[0]; 33 | // assert the command is the one we're expecting 34 | assertEquals("GetValueForKey", command.getCommandKey().name()); 35 | // confirm that it was a COLLAPSED command execution 36 | assertTrue(command.getExecutionEvents().contains(HystrixEventType.COLLAPSED)); 37 | // and that it was successful 38 | assertTrue(command.getExecutionEvents().contains(HystrixEventType.SUCCESS)); 39 | } finally { 40 | context.shutdown(); 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 11 | 12 | Hystrix Demo 13 | 14 | 15 | HystrixRequestContextServletFilter 16 | HystrixRequestContextServletFilter 17 | com.xianlinbox.hystrix.filter.HystrixRequestContextServletFilter 18 | 19 | 20 | 21 | HystrixRequestContextServletFilter 22 | /* 23 | 24 | 25 | 26 | contextConfigLocation 27 | classpath:applicationContext.xml 28 | 29 | 30 | 31 | org.springframework.web.context.ContextLoaderListener 32 | 33 | 34 | 35 | 36 | HystrixMetricsStreamServlet 37 | HystrixMetricsStreamServlet 38 | com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet 39 | 40 | 41 | 42 | HystrixMetricsStreamServlet 43 | /hystrix.stream 44 | 45 | 46 | 47 | spring 48 | 49 | org.springframework.web.servlet.DispatcherServlet 50 | 51 | 1 52 | 53 | 54 | 55 | spring 56 | /* 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/main/resources/applicationContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 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 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/test/java/demo/CommandCollapserGetValueForKey.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import com.netflix.hystrix.HystrixCollapser; 4 | import com.netflix.hystrix.HystrixCommand; 5 | import com.netflix.hystrix.HystrixCommandGroupKey; 6 | import com.netflix.hystrix.HystrixCommandKey; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collection; 12 | import java.util.List; 13 | 14 | class CommandCollapserGetValueForKey extends HystrixCollapser, String, Integer> { 15 | private static Logger logger = LoggerFactory.getLogger(CommandCollapserGetValueForKey.class); 16 | private final Integer key; 17 | 18 | public CommandCollapserGetValueForKey(Integer key) { 19 | this.key = key; 20 | } 21 | 22 | @Override 23 | public Integer getRequestArgument() { 24 | return key; 25 | } 26 | 27 | @Override 28 | protected HystrixCommand> createCommand(final Collection> requests) { 29 | return new BatchCommand(requests); 30 | } 31 | 32 | @Override 33 | protected void mapResponseToRequests(List batchResponse, Collection> requests) { 34 | logger.info("Mapping response to request"); 35 | int count = 0; 36 | for (CollapsedRequest request : requests) { 37 | request.setResponse(batchResponse.get(count++)); 38 | } 39 | } 40 | 41 | private static final class BatchCommand extends HystrixCommand> { 42 | private final Collection> requests; 43 | 44 | private BatchCommand(Collection> requests) { 45 | super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")) 46 | .andCommandKey(HystrixCommandKey.Factory.asKey("GetValueForKey"))); 47 | this.requests = requests; 48 | } 49 | 50 | @Override 51 | protected List run() { 52 | logger.info("Mapping response to request"); 53 | ArrayList response = new ArrayList(); 54 | for (CollapsedRequest request : requests) { 55 | logger.info("Run request: {}", request.getArgument()); 56 | 57 | // artificial response for each argument received in the batch 58 | response.add("ValueForKey: " + request.getArgument()); 59 | } 60 | return response; 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /src/test/java/demo/CollapseEchoHystrixCommand.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import com.netflix.hystrix.HystrixCollapser; 4 | import com.netflix.hystrix.HystrixCollapserKey; 5 | import com.netflix.hystrix.HystrixCommand; 6 | import com.netflix.hystrix.HystrixCommandGroupKey; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collection; 12 | import java.util.List; 13 | 14 | public class CollapseEchoHystrixCommand extends HystrixCollapser, String, String> { 15 | private Logger logger = LoggerFactory.getLogger(CollapseEchoHystrixCommand.class); 16 | private String input; 17 | 18 | public CollapseEchoHystrixCommand(String input) { 19 | super(HystrixCollapser.Setter 20 | .withCollapserKey(HystrixCollapserKey.Factory.asKey("Echo Collapse"))); 21 | this.input = input; 22 | } 23 | 24 | @Override 25 | public String getRequestArgument() { 26 | logger.info("Get argument"); 27 | return input; 28 | } 29 | 30 | @Override 31 | protected HystrixCommand> createCommand(Collection> collapsedRequests) { 32 | logger.info("Create batch command"); 33 | return new BatchCommand(collapsedRequests); 34 | } 35 | 36 | @Override 37 | protected void mapResponseToRequests(List batchResponse, Collection> collapsedRequests) { 38 | logger.info("Mapping response to Request"); 39 | int count = 0; 40 | for (CollapsedRequest request : collapsedRequests) { 41 | request.setResponse(batchResponse.get(count++)); 42 | } 43 | 44 | } 45 | 46 | private class BatchCommand extends HystrixCommand> { 47 | private Collection> requests; 48 | 49 | public BatchCommand(Collection> requests) { 50 | super(HystrixCommandGroupKey.Factory.asKey("Batch")); 51 | this.requests = requests; 52 | } 53 | 54 | @Override 55 | protected List run() throws Exception { 56 | logger.info("Run batch command"); 57 | List responses = new ArrayList(); 58 | for (CollapsedRequest request : requests) { 59 | logger.info("Run request: {}", request.getArgument()); 60 | responses.add("Echo: " + request.getArgument()); 61 | } 62 | return responses; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/demo/HystrixCommandTest.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import com.netflix.hystrix.HystrixCommand; 4 | import com.netflix.hystrix.HystrixEventType; 5 | import com.netflix.hystrix.HystrixRequestLog; 6 | import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; 7 | import org.junit.Test; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import rx.Observable; 11 | import rx.Observer; 12 | import rx.util.functions.Action1; 13 | 14 | import java.util.concurrent.Future; 15 | 16 | import static junit.framework.TestCase.assertTrue; 17 | import static org.hamcrest.CoreMatchers.equalTo; 18 | import static org.junit.Assert.assertEquals; 19 | import static org.junit.Assert.assertThat; 20 | 21 | public class HystrixCommandTest { 22 | private Logger logger = LoggerFactory.getLogger(HystrixCommandTest.class); 23 | 24 | @Test 25 | public void synchronousExecute() throws Exception { 26 | ThreadEchoCommand command = new ThreadEchoCommand("xianlinbox"); 27 | String result = command.execute(); 28 | assertThat(result, equalTo("Echo: xianlinbox")); 29 | } 30 | 31 | @Test 32 | public void asynchronousExecute() throws Exception { 33 | ThreadEchoCommand command = new ThreadEchoCommand("xianlinbox"); 34 | Future result = command.queue(); 35 | while (!result.isDone()) { 36 | logger.info("Do other things ..."); 37 | } 38 | assertThat(result.get(), equalTo("Echo: xianlinbox")); 39 | } 40 | 41 | @Test 42 | public void reactiveExecute1() throws Exception { 43 | ThreadEchoCommand command1 = new ThreadEchoCommand("xianlinbox"); 44 | Observable result = Observable.just(command1); 45 | result.subscribe(new Action1() { 46 | @Override 47 | public void call(ThreadEchoCommand s) { 48 | logger.info("Command called. Result is:{}", s.execute()); 49 | } 50 | }); 51 | 52 | Thread.sleep(1000); 53 | } 54 | 55 | 56 | @Test 57 | public void reactiveExecute2() throws Exception { 58 | ThreadEchoCommand command1 = new ThreadEchoCommand("xianlinbox-1"); 59 | ThreadEchoCommand command2 = new ThreadEchoCommand("xianlinbox-2"); 60 | 61 | Observable observable = Observable.from(command1, command2); 62 | Observer observer = new Observer() { 63 | @Override 64 | public void onCompleted() { 65 | logger.info("Command Completed"); 66 | } 67 | 68 | @Override 69 | public void onError(Throwable e) { 70 | logger.error("Command failled", e); 71 | } 72 | 73 | @Override 74 | public void onNext(ThreadEchoCommand command) { 75 | logger.info("Command finished,result is {}", command.execute()); 76 | } 77 | }; 78 | observable.subscribe(observer); 79 | 80 | Thread.sleep(1000); 81 | } 82 | 83 | @Test 84 | public void semaphoresCommandExecute() throws Exception { 85 | SemaphoreEchoCommand command = new SemaphoreEchoCommand("xianlinbox"); 86 | assertThat(command.execute(), equalTo("Echo: xianlinbox")); 87 | } 88 | 89 | @Test 90 | public void semaphoresCommandMultiExecute() throws Exception { 91 | for (int i = 0; i < 5; i++) { 92 | final SemaphoreEchoCommand command = new SemaphoreEchoCommand("xianlinbox-" + i); 93 | Thread thread = new Thread(new Runnable() { 94 | @Override 95 | public void run() { 96 | command.queue(); 97 | } 98 | }); 99 | thread.start(); 100 | } 101 | Thread.sleep(1000); 102 | } 103 | 104 | @Test 105 | public void requestCache() throws Exception { 106 | HystrixRequestContext context = HystrixRequestContext.initializeContext(); 107 | try { 108 | ThreadEchoCommand command1 = new ThreadEchoCommand("xianlinbox"); 109 | ThreadEchoCommand command2 = new ThreadEchoCommand("xianlinbox"); 110 | 111 | assertThat(command1.execute(), equalTo("Echo: xianlinbox")); 112 | assertThat(command1.isResponseFromCache(), equalTo(false)); 113 | assertThat(command2.execute(), equalTo("Echo: xianlinbox")); 114 | assertThat(command2.isResponseFromCache(), equalTo(true)); 115 | } finally { 116 | context.shutdown(); 117 | } 118 | 119 | context = HystrixRequestContext.initializeContext(); 120 | try { 121 | ThreadEchoCommand command3 = new ThreadEchoCommand("xianlinbox"); 122 | assertThat(command3.execute(), equalTo("Echo: xianlinbox")); 123 | assertThat(command3.isResponseFromCache(), equalTo(false)); 124 | } finally { 125 | context.shutdown(); 126 | } 127 | } 128 | 129 | @Test 130 | public void flushCacheTest() throws Exception { 131 | HystrixRequestContext context = HystrixRequestContext.initializeContext(); 132 | try { 133 | ThreadEchoCommand command1 = new ThreadEchoCommand("xianlinbox"); 134 | ThreadEchoCommand command2 = new ThreadEchoCommand("xianlinbox"); 135 | 136 | assertThat(command1.execute(), equalTo("Echo: xianlinbox")); 137 | assertThat(command1.isResponseFromCache(), equalTo(false)); 138 | assertThat(command2.execute(), equalTo("Echo: xianlinbox")); 139 | assertThat(command2.isResponseFromCache(), equalTo(true)); 140 | 141 | ThreadEchoCommand.flushCache("xianlinbox"); 142 | ThreadEchoCommand command3 = new ThreadEchoCommand("xianlinbox"); 143 | assertThat(command3.execute(), equalTo("Echo: xianlinbox")); 144 | assertThat(command3.isResponseFromCache(), equalTo(false)); 145 | } finally { 146 | context.shutdown(); 147 | } 148 | } 149 | 150 | 151 | @Test 152 | public void collapseCommandTest() throws Exception { 153 | HystrixRequestContext context = HystrixRequestContext.initializeContext(); 154 | 155 | try { 156 | Future result1 = new CollapseEchoHystrixCommand("xianlinbox-1").queue(); 157 | Future result2 = new CollapseEchoHystrixCommand("xianlinbox-2").queue(); 158 | Future result3 = new CollapseEchoHystrixCommand("xianlinbox-3").queue(); 159 | 160 | assertThat(result1.get(),equalTo("Echo: xianlinbox-1")); 161 | assertThat(result2.get(),equalTo("Echo: xianlinbox-2")); 162 | assertThat(result3.get(),equalTo("Echo: xianlinbox-3")); 163 | 164 | assertEquals(1, HystrixRequestLog.getCurrentRequest().getExecutedCommands().size()); 165 | } finally { 166 | context.shutdown(); 167 | } 168 | } 169 | 170 | } 171 | --------------------------------------------------------------------------------