Spring Cloud(四)--Feign & Turbine集群聚合监控

文章目录

六、Feign 声明式客户端接口

Feign 集成了 Ribbon 和 Hystrix ,并提供了声明式消费者客户端。

  • 声明式客户端:
    只需要定义一个抽象的接口,就可以通过接 口调用远程服务,不需要写具体调用代码。

例如调用后台商品服务,接口可以这样定义:

@FeignClient(name="item-service")
public interface ItemFeignClient{
	@GetMapping("/{orderId}")
	JsonResult<List<Item>> getItems(@PathVarible String orderId);
}

通过注解配置以下3点:

  • 服务id - - 确定调用哪个远程服务
  • 路径 - - 调用一个服务的哪个路径
  • 参数 - - 向这个路径提交什么参数数据
    在这里插入图片描述

1 新建 sp09-feign 项目

1.1 新建springboot项目

在这里插入图片描述
在这里插入图片描述

1.2 编辑pom.xml

  • 添加 sp01-commons 依赖
<dependency>
    <groupId>cn.tedu</groupId>
    <artifactId>sp01-commons</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
  • 排除eureka-client的dataformat
<exclusions>
  <exclusion>
      <artifactId>jackson-dataformat-xml</artifactId>
      <groupId>com.fasterxml.jackson.dataformat</groupId>
  </exclusion>
</exclusions>

1.3 application.yml

spring:
  application:
    name: feign

server:
  port: 3001

eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka


1.4 主程序添加@EnableFeignClients

在这里插入图片描述

1.5 新建ItemFeignClient接口

package cn.tedu.sp09.feign;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.web.util.JsonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.List;

@FeignClient(name="item-service")
public interface ItemFeignClient {
    @GetMapping("/{orderId}")
    JsonResult<List<Item>> getItems(@PathVariable String orderId);

    @PostMapping("/decreaseNumber")
    JsonResult decreaseNumber(@RequestBody List<Item> items);
}

1.6 新建UserFeignClient接口

package cn.tedu.sp09.feign;

import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name="user-service")
public interface UserFeignClient {
    @GetMapping("/{userId}")
    JsonResult<User> getUser(@PathVariable Integer userId);
    @GetMapping("/{userId}/score")
    JsonResult addScore(@PathVariable Integer userId, @RequestParam Integer score);
}

1.7 新建OrderFeignClient接口

package cn.tedu.sp09.feign;

import cn.tedu.sp01.pojo.Order;
import cn.tedu.web.util.JsonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(name="order-service")
public interface OrderFeignClient {
    @GetMapping("/{orderId}")
    JsonResult<Order> getOrder(@PathVariable String orderId);
    @GetMapping("/")
    JsonResult addOrder();
}

1.8 新建FeignController

package cn.tedu.sp09.controller;

import java.util.List;

import cn.tedu.sp09.feign.ItemFeignClient;
import cn.tedu.sp09.feign.OrderFeignClient;
import cn.tedu.sp09.feign.UserFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;

import cn.tedu.web.util.JsonResult;

@RestController
public class FeignController {
    @Autowired
    private ItemFeignClient itemFeignClient;
    @Autowired
    private UserFeignClient userFeignClient;
    @Autowired
    private OrderFeignClient orderFeignClient;

    @GetMapping("/item-service/{orderId}")
    public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
        return itemFeignClient.getItems(orderId);
    }

    @PostMapping("/item-service/decreaseNumber")
    public JsonResult decreaseNumber(@RequestBody List<Item> items) {
        return itemFeignClient.decreaseNumber(items);
    }

    /

    @GetMapping("/user-service/{userId}")
    public JsonResult<User> getUser(@PathVariable Integer userId) {
        return userFeignClient.getUser(userId);
    }

    @GetMapping("/user-service/{userId}/score")
    public JsonResult addScore(@PathVariable Integer userId, Integer score) {
        return userFeignClient.addScore(userId, score);
    }

    /

    @GetMapping("/order-service/{orderId}")
    public JsonResult<Order> getOrder(@PathVariable String orderId) {
        return orderFeignClient.getOrder(orderId);
    }

    @GetMapping("/order-service")
    public JsonResult addOrder() {
        return orderFeignClient.addOrder();
    }
}

1.9 启动测试

调用流程

在这里插入图片描述
在这里插入图片描述
将以上项目启动,访问:

2 feign + ribbon 负载均衡和重试

Feign 集成了 Ribbon,提供了 Ribbon 的默认配置,默认已启动了负载均衡和重试,无需额外配置就可以使用Ribbon。

  • 重试的默认配置参数
MaxAutoRetries=0
MaxAutoRetriesNextServer=1
ReadTimeout=1000
ConnectTimeout=1000

不做任何配置,访问:
http://localhost:3001/item-service/35
可以看到此时会在8001、8002轮询访问:
在这里插入图片描述
也会进行重试:
在这里插入图片描述
在这里插入图片描述
当8002延迟后,会更换8001继续访问,而当访问8001也失败了,本次访问就宣告失败。
在这里插入图片描述

编辑 application.yml 配置 ribbon

# 通用配置,对所有服务都有效
ribbon:
  ReadTimeout: 1000
# 只对商品服务有效
item-service:
  ribbon:
    MaxAutoRetries: 1
    MaxAutoRetriesNextServer: 2
    ReadTimeout: 500

访问 http://localhost:3001/item-service/35 查看效果。

3 feign + hystrix 降级

3.1 feign 启用 hystrix

feign 集成了 hystrix,但 默认不启用 hystrix,feign 不推荐启用hystrix(网关那一节会分析原因)。

启用 hystrix,添加基础配置:

1) 添加Hystrix完整依赖

(feign 没有包含完整的 hystrix 依赖)

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2) 添加yml配置 feign.hystrix.enabled=true ,在Feign中启用Hystrix
feign:
  hystrix:
    enabled: true
3) 主程序添加@EnableCircuitBreaker

在这里插入图片描述

3.2 添加降级代码

3.2.1 添加单独的降级类并实现声明式客户端接口
ItemFeignClientFB
package cn.tedu.sp09.feign;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.web.util.JsonResult;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class ItemFeignClientFB implements ItemFeignClient{

    @Override
    public JsonResult<List<Item>> getItems(String orderId) {
        return JsonResult.err("无法获取订单商品列表");
    }

    @Override
    public JsonResult decreaseNumber(List<Item> items) {
        return JsonResult.err("无法修改商品库存");
    }
}

OrderFeignClientFB
package cn.tedu.sp09.feign;

import cn.tedu.sp01.pojo.Order;
import cn.tedu.web.util.JsonResult;
import org.springframework.stereotype.Component;

@Component
public class OrderFeignClientFB implements OrderFeignClient{
    @Override
    public JsonResult<Order> getOrder(String orderId) {
        return JsonResult.err("无法获取商品订单");
    }

    @Override
    public JsonResult addOrder() {
        return JsonResult.err("无法保存订单");
    }
}

UserFeignClientFB
package cn.tedu.sp09.feign;

import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
import org.springframework.stereotype.Component;

@Component
public class UserFeignClientFB implements UserFeignClient{
    @Override
    public JsonResult<User> getUser(Integer userId) {
        return JsonResult.err("无法获取用户信息");
    }

    @Override
    public JsonResult addScore(Integer userId, Integer score) {
        return JsonResult.err("无法增加用户积分");
    }
}

3.2.2 在 feign 远程接口中指定降级类

远程调用失败, 会执行降级类中的代码。

ItemFeignClient
...
@FeignClient(name="item-service",fallback = ItemFeignClientFB.class)
public interface ItemFeignClient {
	...
}
OrderFeignClient
...
@FeignClient(name="order-service",fallback = OrderFeignClientFB.class)
public interface OrderFeignClient {
	...
}
UserFeignClient
...
@FeignClient(name="user-service",fallback = UserFeignClientFB.class)
public interface UserFeignClient {
	...
}
3.2.3 启动测试

4 feign + hystrix 监控和熔断测试

4.1 配置actuator,添加依赖

查看pom.xml文件,已添加 actuator 依赖。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

4.2 配置yml,暴露 hystrix.stream 监控端点

management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream

4.3 启动测试

此时只有ping… ,需要访问其他服务产生监控数据
在这里插入图片描述

5 order service 调用商品服务和用户服务

在这里插入图片描述
可以将 sp09-feign 项目关闭,后面不再使用。

5.1 修改sp04项目,完成远程调用

5.1.1 编辑pom.xml,添加依赖:feign、hystrix完整依赖、actuator

在这里插入图片描述

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
5.1.2 编辑 application.yml

未配置 ribbon 重试 和 hystrix 超时,采用默认值。

# 启用 hystrix
feign:
  hystrix:
    enabled: true
# 暴露 hystrix.stream 监控端点
management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream
5.1.3 主程序添加注解
@EnableCircuitBreaker - - 启用 hystrix
@EnableFeignClients - - 启用 feign
5.1.4 添加声明式客户端接口,指定降级类
ItemFeignClient
package cn.tedu.sp04.order.feign;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.web.util.JsonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.List;

@FeignClient(name="item-service", fallback = ItemFeignClientFB.class)
public interface ItemFeignClient {
    @GetMapping("/{orderId}")
    JsonResult<List<Item>> getItems(@PathVariable String orderId);

    @PostMapping("/decreaseNumber")
    JsonResult decreaseNumber(@RequestBody List<Item> items);
}

UserFeignClient
package cn.tedu.sp04.order.feign;

import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name="user-service", fallback = UserFeignClientFB.class)
public interface UserFeignClient {
    @GetMapping("/{userId}")
    JsonResult<User> getUser(@PathVariable Integer userId);

    @GetMapping("/{userId}/score")
    JsonResult addScore(@PathVariable Integer userId, @RequestParam Integer score);
}

5.1.5 添加单独的降级类并实现声明式客户端接口
ItemFeignClientFB
package cn.tedu.sp04.order.feign;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.web.util.JsonResult;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;
@Component
public class ItemFeignClientFB implements ItemFeignClient{
    @Override
    public JsonResult<List<Item>> getItems(String orderId) {
        //模拟有缓存数据,返回缓存
        if(Math.random()<0.5) {
            return JsonResult.ok().data(
                    Arrays.asList(new Item[] {
                            new Item(1,"缓存aaa",2),
                            new Item(2,"缓存bbb",1),
                            new Item(3,"缓存ccc",3),
                            new Item(4,"缓存ddd",1),
                            new Item(5,"缓存eee",5)
                    })
            );
        }
        return JsonResult.err("无法获取订单商品列表");
    }

    @Override
    public JsonResult decreaseNumber(List<Item> items) {
        return JsonResult.err("无法修改商品库存");
    }
}

UserFeignClientFB
package cn.tedu.sp04.order.feign;

import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
import org.springframework.stereotype.Component;

@Component
public class UserFeignClientFB implements UserFeignClient{
    @Override
    public JsonResult<User> getUser(Integer userId) {
        if(Math.random()<0.4) {
            return JsonResult.ok(new User(userId, "缓存name"+userId, "缓存pwd"+userId));
        }
        return JsonResult.err("无法获取用户信息");
    }

    @Override
    public JsonResult addScore(Integer userId, Integer score) {
        return JsonResult.err("无法增加用户积分");
    }
}

5.1.6 修改 OrderServiceImpl,使用客户端接口调用远程服务
package cn.tedu.sp04.order.service;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.User;
import cn.tedu.sp04.order.feign.ItemFeignClient;
import cn.tedu.sp04.order.feign.UserFeignClient;
import cn.tedu.web.util.JsonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.service.OrderService;

import lombok.extern.slf4j.Slf4j;

import java.util.List;

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private ItemFeignClient itemFeignClient;
    @Autowired
    private UserFeignClient userFeignClient;

    @Override
    public Order getOrder(String orderId) {
        //调用 user-service 获取用户信息
        //(id写死,真实项目需获取目前登录用户Id)
        JsonResult<User> user = userFeignClient.getUser(8);

        //调用 item-service 获取商品信息
        JsonResult<List<Item>> items = itemFeignClient.getItems(orderId);

        Order order = new Order();
        order.setId(orderId);
        order.setUser(user.getData());
        order.setItems(items.getData());
        return order;
    }

    @Override
    public void addOrder(Order order) {
        //调用 item-service 减少商品库存
        itemFeignClient.decreaseNumber(order.getItems());

        //调用 user-service 增加用户积分
        userFeignClient.addScore(order.getUser().getId(),1000);

        log.info("保存订单:"+order);
    }

}

5.1.7 order-service 配置启动参数,启动两台服务器
  • 启动参数 : --server.port=8201 --server.port=8202
    在这里插入图片描述
    在这里插入图片描述
5.1.8 启动测试

七、hystrix + turbine 集群聚合监控

在这里插入图片描述
Turbine 可以聚合多台服务器的监控数据,聚合后交给 hystrix dashboard 来集中展示和监控。

1 搭建 Turbine 服务:

添加 Turbine 依赖

编辑 yml 配置:

turbine.app-config :指定要聚合的服务 - - service1,service2…
turbine.cluster-name-expression:给聚合后的数据起一个集群名,一般用 “default”

主程序添加注解 @EnableTurbine

2 新建 sp10-turbine 项目

2.1 新建springboot项目

在这里插入图片描述
在这里插入图片描述

2.2 查看 pom.xml

已添加 turbine 依赖。

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>

2.3 编辑 application.yml

spring:
  application:
    name: turbine

server:
  port: 5001

eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka

turbine:
  app-config: order-service
  cluster-name-expression: new String("default")

2.4 主程序添加 @EnableTurbine

在这里插入图片描述

2.5 访问测试

turbine聚合了order-service两台服务器的hystrix监控信息:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值