zoukankan      html  css  js  c++  java
  • Spring Cloud微服务系列文,Hystrix与Eureka的整合

        和Ribbon等组件一样,在项目中,Hystrix一般不会单独出现,而是会和Eureka等组件配套出现。在Hystrix和Eureka整合后的框架里,一般会用到Hystrix的断路器以及合并请求等特性,而在Web框架里,大多会有专门的缓存组件,所以不怎么会用到Hystrix的缓存特性。

     1 准备Eureka服务器项目

        HystrixEurekaServer项目承担着Eureka服务器的作用,这部分的代码关键点如下。

        第一,在pom.xml里,通过如下关键代码引入Eureka服务器组件的依赖包。 

    1    <dependency>
    2        <groupId>org.springframework.cloud</groupId>
    3        <artifactId>spring-cloud-starter-eureka-server</artifactId>
    4    </dependency>

        第二,在application.yml里,指定了本项目的主机名和端口号,并指定了对外提供eureka服务的Url路径,代码如下。    

    1    server:
    2      port: 8888
    3    eureka:
    4      instance:
    5        hostname: localhost
    6      client:
    7        register-with-eureka: false
    8        fetch-registry: false 
    9        serviceUrl:
    10          defaultZone: http://localhost:8888/eureka/  

        第三,在ServerStarter.java里,编写启动Eureka服务的代码,这里请注意,在第2和第3行里,通过注解声明了本类是Eureka服务器的启动类。    

    1    //省略必要的package和import的代码
    2    @EnableEurekaServer
    3    @SpringBootApplication
    4    public class ServerStarter 
    5    {
    6          public static void main( String[] args ){  
    7              SpringApplication.run(ServerStarter.class, args);  
    8        }
    9    }

        注:本部分给出的是Eureka服务器省略版的代码,完整代码大家可以参见另外一篇博文,架构师入门:搭建基本的Eureka架构(从项目里抽取)。    

    2 准备Eureka服务器项目

        在pom.xml里,我们除了指定Eureka的依赖包以外,还了指定了Hystrix的依赖包,关键代码如下。其中,前4行指定的是Eureka的依赖包,后4行指定的是Hystrix的依赖包。    

    1    <dependency>
    2            <groupId>org.springframework.cloud</groupId>
    3            <artifactId>spring-cloud-starter-eureka</artifactId>
    4        </dependency>
    5    <dependency>
    6            <groupId>org.springframework.cloud</groupId>
    7            <artifactId>spring-cloud-starter-hystrix</artifactId>
    8        </dependency>

        而在application.yml里,指定了本项目的服务端口是1111,对外提供的项目名是hystrixEureka,以及是向第一部分指定的Eureka服务器注册,代码如下。   

    1    server:
    2      port: 1111
    3    spring:
    4      application:
    5        name: hystrixEureka
    6    eureka:  
    7      client:
    8        serviceUrl:
    9          defaultZone: http://localhost:8888/eureka/

        

    3 在服务提供者项目里引入断路器机制

         在服务提供者的启动类ServiceProviderApp.java里,我们是通过加入@EnableCircuitBreaker注解来启动断路器,代码如下。     

    1    //省略必要的package和import代码
    2    @SpringBootApplication
    3    @EnableEurekaClient
    4    @EnableCircuitBreaker
    5    @ServletComponentScan
    6    public class ServiceProviderApp 
    7    {
    8        public static void main( String[] args ){ 
    9             SpringApplication.run(ServiceProviderApp.class, args);  
    10       }
    11    }

        在Controller.java这个控制器类里,我们是在第9行里,通过调用service类提供的方法来返回具体的OrderDetail信息,代码如下。    

    1    //省略必要的package和import代码
    2    @RestController
    3    public class Controller {
    4            @Autowired
    5        private OrderDetailService service;   
    6        //对外提供服务的getOrderDetailById方法 
    7            @RequestMapping(value = "/getOrderDetailById/{orderId}", method = RequestMethod.GET)
    8            public OrderDetail getOrderDetailById(@PathVariable("orderId")   String orderId) throws Exception {
    9            return service.getOrderDetailByID(orderId);
    10        }
    11    }

        在OrderDetailService.java里,我们用HashMap这个数据结构来模拟数据库,以此来模拟从数据库读OrderDetail的方式,提供了“根据ID找相应对象的服务”,代码如下。    

    1    //省略必要的package和import代码
    2    @Service
    3    public class OrderDetailService {
    4        static HashMap<String,String> orderDB = new HashMap<String,String> ();
    5        static //通过static代码,模拟数据库中存储的OrderDetail信息
    6        {
    7            orderDB.put("1","Peter");
    8            orderDB.put("2","Tom");
    9            orderDB.put("3","Mike");
    10        }
    11    //在方法之前,通过注解引入Hystrix,并指定回退方法
    12        @HystrixCommand(fallbackMethod = "getFallback")
    13        public OrderDetail getOrderDetailByID(String id) throws Exception
    14        {
    15            OrderDetail orderDetail = new OrderDetail();        
    16            if("error".equals(id) ) //如果输入是error,则故意抛出异常
    17            {throw new Exception(); }
    18            //模拟地从数据库里得到信息并返回
    19            orderDetail.setOrderId(id);
    20            orderDetail.setOrderOwner(orderDB.get(id));
    21            return orderDetail;
    22        }
    23        //定义Hystrix的回退方法
    24        public OrderDetail getFallback(String orderId) {
    25            OrderDetail orderDetail = new OrderDetail();
    26            orderDetail.setOrderId("error");
    27            orderDetail.setOrderOwner("error");        
    28            System.out.println("In fallbackForOrderDetail function");    
    29            return orderDetail;
    30        }
    31    }

         在第13行的getOrderDetailByID方法之前,我们在第12行通过fallbackMethod定义了回退方法,在这个方法的第16行里,我们定义了如果输入是error,那么则将抛出异常,以此触发回退方法getFallback。而在第24行定义的回退方法里,我们将返回一个ID和Owner都是error的OrderDetail对象。本类用到的OrderDetail模型类定义如下。    

    1    public class OrderDetail{
    2           private String orderId;//订单id
    3         private String orderOwner; //订单所有人 
    4        //省略必要的get和set方法
    5    }

        至此我们完成了开发工作,启动HystrixEurekaServer和HystrixEurekaserviceProvider后,如果在浏览器中输入http://localhost:1111/getOrderDetailById/1,能看到如下的输出,这说明走的是正常的流程。

    {"orderId":"1","orderOwner":"Peter"}

        但如果输入的是http://localhost:1111/getOrderDetailById/error,那么会在OrderDetailService类的getOrderDetailByID方法里抛出异常,从而走Hystrix的回退流程,由此会输入如下的语句。

    {"orderId":"error","orderOwner":"error"}

        在这个案例中,我们是在“提供者服务”的模块引入hytrix断路器,而不是在“服务调用”模块,这和项目中的常规做法相符,因为启动断路器的场景一般是“提供服务模块的流量超载”。    

    4 在服务调用者项目里引入合并请求机制

        这里我们将在HystrixEurekaserviceCaller项目里,调用HystrixEurekaserviceProvider里定义的服务,在调用时,我们将合并5秒内发送的请求。

        步骤一,在控制器类Controller.java里,初始化Hystrix请求上下文,并通过Future对象多次发送请求,代码如下。   

    1    //省略必要的package和import代码
    2    @Configuration
    3    @RestController
    4    public class Controller {         
    5          @Autowired
    6         private OrderDetailService service;//提供服务的service类
    7    //在这个方法里,将演示合并请求的效果
    8        @RequestMapping(value = "/mergeDemo", method = RequestMethod.GET)
    9        public List<OrderDetail> hystrixMergeDemo() throws Exception {
    10        //初始化Hystrix请求上下文         
    11            HystrixRequestContext context = HystrixRequestContext
    12                    .initializeContext();
    13            //通过定义三个Future对象,调用三次请求
    14            Future<OrderDetail> f1 = service.getOneOrderDetail("1");
    15            Future<OrderDetail> f2 = service.getOneOrderDetail("2");
    16            Future<OrderDetail> f3 = service.getOneOrderDetail("3");
    17            OrderDetail o1 = f1.get();
    18            OrderDetail o2 = f2.get();
    19            OrderDetail o3 = f3.get();
    20            //把三个返回对象组装到一个List中并返回
    21            List<OrderDetail> orderDetailList = new ArrayList<OrderDetail>();
    22            orderDetailList.add(o1);
    23            orderDetailList.add(o2);
    24            orderDetailList.add(o3);
    25            //释放上下文
    26            context.shutdown();        
    27            return orderDetailList;
    28         }
    29    }

         在上文的hystrixMergeDemo方法里,我们首先在第11行初始化Hystrix请求上下文,随后在第14到16行调用了三次getOneOrderDetail方法,并在第17到19行里,通过Furure类型对象的get方法,把三次调用的结果分别赋予三个OrderDetail类型的对象。

        之后,通过第21到24行的代码,把三个OrderDetail对象组装成一个List<OrderDetail>类型的orderDetailList对象,并在第27行返回了orderDetailList对象。

        这里虽然我们是发出了三次调用请求,但从后文的讲解里,我们能看到这三次请求其实是被合并处理的。由于在合并请求时,Hystrix处理类会把请求暂存在Hystrix请求上下文里,所以这里我们一定得通过类似于第11行的代码初始化上下文,否则将无法得到合并请求的结果。

        步骤二,之前我们看到,在Controller类里,是调用OrderDetailService类的方法来查询多个订单,所以合并请求的代码是写在这个类里的,我们来看下代码。    

    1    //省略必要的package和import代码
    2    @Component
    3    public class OrderDetailService {
    4        // 合并处理收集5秒内的请求
    5        @HystrixCollapser(batchMethod = "getMoreOrderDetails",collapserProperties = {@HystrixProperty(name ="timerDelayInMilliseconds",value = "5000")})
    6        public Future<OrderDetail> getOneOrderDetail(String id) {
    7            System.out.println("in getOneOrderDetail");
    8            return null;
    9        }
    10        @Bean
    11        @LoadBalanced
    12        public RestTemplate getRestTemplate()
    13        { return new RestTemplate();      }
    14    //这里是合并请求的代码    
    15        @HystrixCommand
    16        public List<OrderDetail> getMoreOrderDetails(List<String> orderIds) {
    17            System.out.println("in getMoreOrderDetails," + orderIds.size());
    18            List<OrderDetail> list = new ArrayList<OrderDetail>();
    19            RestTemplate template = getRestTemplate();
    20            //通过for循环,调用服务提供者的方法得到OrderDetail对象
    21            for (String orderId : orderIds) {
    22                OrderDetail orderDetail = new OrderDetail();  
    23                orderDetail = template.getForObject("http://localhost:1111/getOrderDetailById/{orderId}", OrderDetail.class,orderId);
    24                list.add(orderDetail);
    25            }
    26            return list;
    27        }
    28    }

        在第6行里,我们定义了只查询一个对象的getOneOrderDetail方法,在定义该方法的注解里,我们指定了会把在5秒内调用该方法的请求合并到getMoreOrderDetails方法里。

        在第16行的getMoreOrderDetails方法里,我们是通过第21行到第25行的for循环,依次遍历待查询的orderId,并通过第23行的getForObject方法,调用服务提供者的getOrderDetailById方法,得到对应的OrderDetail对象,并添加到List<OrderDetail>类型的list对象中。最后,是通过第26行的代码,返回包含多次请求结果的list对象。

        当我们启动Eureka服务器(HystrixEurekaServer)、服务提供者(HystrixEurekaserviceProvider)和服务调用者(HystrixEurekaserviceCaller)三个项目后,可以在浏览器里输入如下的请求,以此来查看合并请求的效果。

    http://localhost:8080/mergeDemo

        上述请求的输出如下,我们能看到3个OrderDetail对象。从上述的讲解能看出,这3个对象其实是通过一次合并后的请求得到的。

    [{"orderId":"1","orderOwner":"Peter"},{"orderId":"2","orderOwner":"Tom"},{"orderId":"3","orderOwner":"Mike"}]

        本文谢绝转载。其它和Spring Cloud相关的博文如下:

        

  • 相关阅读:
    django 项目需要注意的一些点
    VUE之路
    Oracle 表格碎片的查看方法
    RHEL 6.x or 7.x 使用分区绑定ASM 磁盘的方法
    RMAN 修复主库 nologging 操作导致物理备库的坏块
    Oracle 数据库19c 回退降级到 11.2.0.4 方案
    如何评估oracle 数据库rman全备和增量备份大小
    在将Oracle GI和DB升级到19c或降级到以前的版本之前需要应用的补丁 (Doc ID 2668071.1)
    Oracle 数据库坏块处理
    opatch auto 安装11.2.0.4.20190115 PSU遇到 OUI-67133: Execution of PRE script failed,with returen value 1 报错
  • 原文地址:https://www.cnblogs.com/JavaArchitect/p/10129378.html
Copyright © 2011-2022 走看看