微服务之三:Hystrix(2)

一: 整合Hystrix

1.1概述

Hystrix主要用于保护调用服务的一方,如果被调用的服务发生故障,符合一定条件,就开启短路器,对调用的程序进行隔离
项目准备:

  • spring -Hystrix-service:Eureka服务器。8761
  • spring-Hystrix-provider:服务提供者,8080
    • /hello/{personId}
    • /hello
  • spring-hystrix-invoker:服务调用者

1.2 服务调用者添加依赖(invoker)

<!-- Hystrix依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>

1.3 新建服务类,在服务方法中调用服务

package com.atm.cloud;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@Service
public class PersonService {

    @Autowired
    RestTemplate restTemplate;

    //使用Hystrix的注解,当服务提供者不启动时,直接直接回退方法
    @HystrixCommand(fallbackMethod = "findPersonFallback")
    public Person findPerson(Integer personId) {
        Person person = restTemplate.getForObject(
                "http://atm-eureka-hystrix-provider/person/{personId}",
                Person.class, personId);
        return person;
    }

    // fallBack方法需要和findPerson的参数一致
    public Person findPersonFallback(Integer personId) {
        Person p = new Person();

        p.setId(10);
        p.setAge(personId);
        p.setName("i am fallback!");

        return p;
    }
}

1.3 控制层

package com.atm.cloud;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Configuration
public class MyRouterController {

    @Autowired
    private PersonService personService;

    @RequestMapping(value = "/router", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public Person router() {
        return personService.findPerson(2);
    }

}

1.4 更改启动类

在启动类中加入启动断路器的注解

package com.atm.cloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker//打开断路器
public class HystrixInvokerApp {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(HystrixInvokerApp.class, args);
    }
}

注:
服务类注入了RestTemplate,服务方法使用@HystrixCommand注解进行修饰,并且配置了回退方法。

二: 配置

  • 一般配置(针对单个方法进行配置)
  • 默认配置(针对整个类进行配置)

2.1 一般配置

/**
*  commandKey:之前用作缓存 , 
*  groupKey:用于执行线程,
*  重要的是超时时间的配置, 
*  线程池的配置
*/
@HystrixCommand(
    fallbackMethod = "findPersonFallback", 
    groupKey = "PersonGroupKey", 
    commandKey = "PersonCommandKey", 
    commandProperties = { 
        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000") 
    }, 
    threadPoolProperties = { 
        @HystrixProperty(name = "coreSize", value = "2") 
    })
public Person findPerson(Integer personId) {
    Person person = restTemplate.getForObject(
        "http://atm-eureka-hystrix-provider/person/{personId}",
        Person.class, personId);
    return person;
}

2.2 全局配置

import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;

@Service
@DefaultProperties(defaultFallback = "findPersonFallback")
public class PersonService {
}

// 默认的回退方法,是没有参数的
public Person findPersonFallback() {
    Person p = new Person();

    p.setId(10);
    p.setAge(1);
    p.setName("i am fallback!");

    return p;
}

2.3 缓存注解

  • 新建过滤器
  • 使用缓存注解
  • 多次同一请求(同一请求中调用同样的资源),可考虑使用缓存

缓存与合并请求功能需要先初始化请求上下文才能实现,所以需要新建一个拦截器,拦截所有的访问路

2.3.1 拦截器

package com.atm.cloud.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;

import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;

/*
 * 需要配置一下:让Spring容器知道这个Filter我们需要用到
 * 
 * urlPatterns="/*",拦截全部请求
 * 
 * filterName="hystrixFilter",过滤器名称
 */
@WebFilter(urlPatterns = "/*", filterName = "hystrixFilter")
public class MyFilter implements Filter {

    public void destroy() {

    }

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {

        // 对每一个请求进行拦截,拦截之后干什么呢?

        System.out.println("开始执行过滤器....");
        HystrixRequestContext ctx = HystrixRequestContext.initializeContext();

        try {

            chain.doFilter(request, response);

        } catch (Exception e) {

        } finally {
            ctx.shutdown();
        }
    }

    public void init(FilterConfig config) throws ServletException {

    }

}

上述代码中,最重要的是 HystrixRequestContext ctx = HystrixRequestContext.initializeContext(); 初始化启动上下

2.3.2 启动类

package com.atm.cloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableEurekaClient
// 打开断路器
@EnableCircuitBreaker
// 扫描
@ServletComponentScan
public class HystrixInvokerApp {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(HystrixInvokerApp.class, args);
    }
}

2.3.3 服务层

package com.atm.cloud.cache;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import com.atm.cloud.Person;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;

@Service
public class CacheService {

    @Autowired
    RestTemplate restTemplate;

    // @CacheResult需要配合@HystrixCommand一起使用
    @CacheResult
    @HystrixCommand
    public Person cachePerson(Integer personId) {
        System.out.println("调用CachePerson()...");
        return null;
    }
}

2.3.4 控制层

package com.atm.cloud.cache;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.atm.cloud.Person;

@RestController
public class CacheController {

    @Autowired
    private CacheService cacheService;

    @RequestMapping(value = "/cache", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public String router() {

        for (int i = 0; i < 5; i++) {
            cacheService.cachePerson(1);
        }

        return "访问成功";
    }
}

访问控制层的接口可以看出,服务方法只调用了一次,其他次都是直接从缓存中获取的。
缓存的注解主要有以下三个:

  • @CacheResult:该注解修饰方法,表示被注释的方法返回结果将会被缓存,但是余下和@HystrixConmmand一起使用
  • @CacheRemove:用于修饰方法,让缓存失效,需要与CacheResult的缓存Key关联
  • CacheKey:用于修饰方法参数,表示该缓存作为缓存的key

2.4 删除缓存

  • 配置命令key
  • 使用注解删除缓存
@Service
public class CacheService {

    // 调用这个方法将设置缓存
    @CacheResult
    @HystrixCommand(commandKey = "cacheKey")
    public String getCache(Integer id) {
        System.out.println("查询缓存()....");
        return "";
    }

    // 调用这个方法将设清除缓存
    @CacheRemove(commandKey = "cacheKey")
    @HystrixCommand
    public String removePerson(Integer id) {
        System.out.println("删除()....");
        return "";
    }
}

三:Feign与Hystrix整合

Feign对Hystrix提供了支持,编写接口和实现依赖

3.0 服务提供者(provider)

package com.atm.cloud;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyRestController {

    @GetMapping("/tmhello")
    @ResponseBody
    public String timeOutHello() throws InterruptedException {
        // 此方法处理最少需要1s
        Thread.sleep(1000);
        return "TimeOut Hello";
    }

}

3.1 服务调用者添加依赖(invoker)

<!-- Feign -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>

3.2 application.yml文件配置(invoker)

  • 开启Feign开关
server:
 port: 9000
spring:
 application:
  name: atm-eureka-hystrix-invoker
feign:
  hystrix:
    enabled: true
hystrix:
  command:
    ##全局方法使用default
    HelloClient#toHello():
      execution:
        isolation:
          thread: 
          ##超时时间
            timeoutInMilliseconds: 500
      circuitBreaker:
      ##每秒3次请求
        requestVolumeThreshold: 3
eureka:
 client:
  serviceUrl:
   defaultZone: http://localhost:8761/eureka/

3.3 修改启动类,添加注解(invoker)

package com.atm.cloud;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker//打开Hystrix断路器
@ServletComponentScan//扫描缓存
@EnableFeignClients//打开Feign注解
public class HystrixInvokerApp {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        new SpringApplicationBuilder(HystrixInvokerApp.class).web(true).run(
                args);
    }
}

3.4 创建访问接口(invoker)

package com.atm.cloud.feignCtr;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

//当HellClien访问atm-eureka-hystrix-provider失败时,需要有一个后备服务
//后备服务需要实现该接口
@FeignClient(name = "atm-eureka-hystrix-provider", fallback = HelloClientFallBack.class)
public interface HelloClient {

    @RequestMapping(method = RequestMethod.GET, value = "/tmhello")
    String toHello();
}

3.5 执行回退类(invoker)

package com.atm.cloud.feignCtr;

import org.springframework.stereotype.Component;

@Component
public class HelloClientFallBack implements HelloClient {

    public String toHello() {
        return "fallBack timeOut Hello";
    }

}

3.6 控制类(invoker)

package com.atm.cloud.feignCtr;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.netflix.hystrix.HystrixCircuitBreaker;
import com.netflix.hystrix.HystrixCommandKey;

@RestController
public class FeignController {

    @Autowired
    private HelloClient helloClient;

    @RequestMapping(value = "tmHello", method = RequestMethod.GET)
    public String tmHello() {
        String result = helloClient.toHello();
        HystrixCircuitBreaker breaker = HystrixCircuitBreaker.Factory
                .getInstance(HystrixCommandKey.Factory
                        .asKey("HelloClient#toHello()"));
        System.out.println("断路器状态:" + breaker.isOpen());
        return result;
    }
}

3.7 服务调用者(invoker)

package com.atm.cloud.feignCtr;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

public class HttpClientMain {

    public static void main(String[] args) throws Exception{
        final CloseableHttpClient httpclient = HttpClients.createDefault();
        final String url = "http://localhost:9000/tmHello";

        for(int i = 0; i < 6; i++) {
            Thread t = new Thread() {

                @Override
                public void run() {
                    try {
                        HttpGet httpget = new HttpGet(url);
                        HttpResponse response = httpclient.execute(httpget);
                        System.out.println(EntityUtils.toString(response.getEntity()));
                    } catch (Exception e ) {
                        e.printStackTrace();
                    }
                }               
            };
            t.start();
        }
        Thread.sleep(15000);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值