SpringClub05-feign 集成工具

一、feign 远程调用-声明式的客户端

微服务应用中,ribbon 和 hystrix 总是同时出现,feign 整合了两者,并提供了声明式消费者客户端

  • 用 feign 代替 hystrix+ribbon

1. 新建 sp09-feign 项目

image.png

2. 添加依赖

image.png



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.2.1.RELEASE
         
    
    cn.tedu
    sp09-feign
    0.0.1-SNAPSHOT
    sp09-feign
    Demo project for Spring Boot

    
        1.8
        Hoxton.RELEASE
    

    
        
            org.springframework.boot
            spring-boot-starter-actuator
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
            
                
                    org.junit.vintage
                    junit-vintage-engine
                
            
        
    

    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

3. 修改application.yml文建

spring:
  application:
    name: feign
    
server:
  port: 3001
  
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka

4. 主程序添加 @EnableFeignClients注解

package cn.tedu.sp09;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class Sp09FeignApplication {
    public static void main(String[] args) {
        SpringApplication.run(Sp09FeignApplication.class, args);
    }
}

5. feign 声明式客户端

feign 利用了我们熟悉的 spring mvc 注解来对接口方法进行设置,降低了我们的学习成本。
通过这些设置,feign可以拼接后台服务的访问路径和提交的参数

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

当这样调用该方法:

service.addScore(7, 100);

那么 feign 会向服务器发送请求:

http://用户微服务/7/score?score=100
  • 注意:如果 score 参数名与变量名不同,需要添加参数名设置:
@GetMapping("/{userId}/score") 
JsonResult addScore(@PathVariable Integer userId, @RequestParam("score") Integer s);

5.1 ItemFeignService

package cn.tedu.sp09.service;
import java.util.List;
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 cn.tedu.sp01.pojo.Item;
import cn.tedu.web.util.JsonResult;
@FeignClient("item-service")
public interface ItemFeignService {
    @GetMapping("/{orderId}")
    JsonResult> getItems(@PathVariable String orderId);
    @PostMapping("/decreaseNumber")
    JsonResult decreaseNumber(@RequestBody List items);
}

5.2 UserFeignService

  • 注意,如果请求参数名与方法参数名不同,@RequestParam不能省略,并且要指定请求参数名:
    @RequestParam("score") Integer s
package cn.tedu.sp09.service;
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;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
@FeignClient("user-service")
public interface UserFeignService {
    @GetMapping("/{userId}")
    JsonResult getUser(@PathVariable Integer userId);
    // 拼接路径 /{userId}/score?score=新增积分
    @GetMapping("/{userId}/score") 
    JsonResult addScore(@PathVariable Integer userId, @RequestParam Integer score);

5.3 OrderFeignService

package cn.tedu.sp09.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.web.util.JsonResult;
@FeignClient("order-service")
public interface OrderFeignService {
    @GetMapping("/{orderId}")
    JsonResult getOrder(@PathVariable String orderId);
    @GetMapping("/")
    JsonResult addOrder();

}

5.4 FeignController

package cn.tedu.sp09.controller;
import java.util.List;
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.sp09.service.ItemFeignService;
import cn.tedu.sp09.service.OrderFeignService;
import cn.tedu.sp09.service.UserFeignService;
import cn.tedu.web.util.JsonResult;
@RestController
public class FeignController {
    @Autowired
    private ItemFeignService itemService;
    @Autowired
    private UserFeignService userService;
    @Autowired
    private OrderFeignService orderService;
    @GetMapping("/item-service/{orderId}")
    public JsonResult> getItems(@PathVariable String orderId) {
        return itemService.getItems(orderId);
    }
    @PostMapping("/item-service/decreaseNumber")
    public JsonResult decreaseNumber(@RequestBody List items) {
        return itemService.decreaseNumber(items);
    }
    @GetMapping("/user-service/{userId}")
    public JsonResult getUser(@PathVariable Integer userId) {
        return userService.getUser(userId);
    }
    @GetMapping("/user-service/{userId}/score") 
    public JsonResult addScore(@PathVariable Integer userId, Integer score) {
        return userService.addScore(userId, score);
    }
    @GetMapping("/order-service/{orderId}")
    public JsonResult getOrder(@PathVariable String orderId) {
        return orderService.getOrder(orderId);
    }
    @GetMapping("/order-service")
    public JsonResult addOrder() {
        return orderService.addOrder();
    }
}

6.启动服务,并访问测试

SpringClub05-feign 集成工具_第1张图片

二、feign + ribbon 负载均衡和重试

  • 无需额外配置,feign 默认已启用了 ribbon 负载均衡和重试机制。可以通过配置对参数进行调整

重试的默认配置参数:

ConnectTimeout=1000
ReadTimeout=1000
MaxAutoRetries=0
MaxAutoRetriesNextServer=1

1.application.yml 配置 ribbon 超时和重试

  • ribbon.xxx 全局配置
  • item-service.ribbon.xxx 对特定服务实例的配置
spring:
  application:
    name: feign
    
server:
  port: 3001
  
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
      
ribbon:
  ConnectTimeout: 1000
  ReadTimeout: 1000
  
item-service:
  ribbon:
    MaxAutoRetries: 1
    MaxAutoRetriesNextServer: 2
    ConnectTimeout: 1000
    ReadTimeout: 500

2.启动服务,访问测试

http://localhost:3001/item-service/35

三、feign + hystrix 降级

feign 默认没有启用 hystrix,添加配置,启用 hystrix

1. application.yml 添加配置

feign:
  hystrix:
    enabled: true

启用 hystrix 后,访问服务
http://localhost:3001/item-service/35
默认1秒会快速失败,没有降级方法时,会显示白板页
白板页

1.1 可以添加配置,暂时减小降级超时时间,以便后续对降级进行测试

feign:
  hystrix:
    enabled: true
    
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 500

2. 指定降级类

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

2.1 ItemFeignService

@FeignClient(name="item-service", fallback = ItemFeignServiceFB.class)
public interface ItemFeignService {

2.2 UserFeignService

@FeignClient(name="user-service", fallback = UserFeignServiceFB.class)
public interface UserFeignService {

2.3 OrderFeignService

@FeignClient(name="order-service",fallback = OrderFeignServiceFB.class)
public interface OrderFeignService {

2.4 降级类

降级类需要实现远程接口

2.4.1 ItemFeignServiceFB

package cn.tedu.sp09.service;
import java.util.List;
import org.springframework.stereotype.Component;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.web.util.JsonResult;
@Component
public class ItemFeignServiceFB implements ItemFeignService {
    @Override
    public JsonResult> getItems(String orderId) {
        return JsonResult.err("无法获取订单商品列表");
    }
    @Override
    public JsonResult decreaseNumber(List items) {
        return JsonResult.err("无法修改商品库存");
    }
}

2.4.2UserFeignServiceFB

package cn.tedu.sp09.service;
import org.springframework.stereotype.Component;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
@Component
public class UserFeignServiceFB implements UserFeignService {
    @Override
    public JsonResult getUser(Integer userId) {
        return JsonResult.err("无法获取用户信息");
    }
    @Override
    public JsonResult addScore(Integer userId, Integer score) {
        return JsonResult.err("无法增加用户积分");
    }
}

2.4.3 OrderFeignServiceFB

package cn.tedu.sp09.service;
import org.springframework.stereotype.Component;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.web.util.JsonResult;
@Component
public class OrderFeignServiceFB implements OrderFeignService {
    @Override
    public JsonResult getOrder(String orderId) {
        return JsonResult.err("无法获取商品订单");
    }
    @Override
    public JsonResult addOrder() {
        return JsonResult.err("无法保存订单");
    }
}

3. 启动服务,访问测试

http://localhost:3001/item-service/35
访问测试

四、feign + hystrix 监控和熔断测试

1.pom.xml 添加 hystrix 起步依赖


    org.springframework.cloud
    spring-cloud-starter-netflix-hystrix

2. 主程序添加 @EnableCircuitBreaker

package cn.tedu.sp09;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableCircuitBreaker
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class Sp09FeignApplication {
    public static void main(String[] args) {
        SpringApplication.run(Sp09FeignApplication.class, args);
    }
}

3. 配置 actuator依赖


    org.springframework.boot
    spring-boot-starter-actuator

4. application.yml暴露 hystrix.stream 监控端点

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

5. 启动服务,查看监控端点

http://localhost:3001/actuator
监控端点

5.1 hystrix dashboard

启动 hystrix dashboard 服务,填入 feign 监控路径,开启监控
访问 http://localhost:4001/hystrix

  • 填入 feign 监控路径:
    http://localhost:3001/actuator/hystrix.stream
  • 访问微服务,以产生监控数据

http://localhost:3001/item-service/35
http://localhost:3001/user-service/7
http://localhost:3001/user-service/7/score?score=100
http://localhost:3001/order-service/123abc
http://localhost:3001/order-service/
监控

5.2 熔断测试

  • 用 ab 工具,以并发50次,来发送20000个请求
ab -n 20000 -c 50 http://localhost:3001/item-service/35 
  • 断路器状态为 Open,所有请求会被短路,直接降级执行 fallback 方法

压力测试

五、order service 调用商品库存服务和用户服务

修改 sp04-orderservice 项目,添加 feign,调用 item service 和 user service

1. 添加依赖

  • actuator
  • feign
  • hystrix


    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.2.1.RELEASE
         
    
    cn.tedu
    sp04-orderservice
    0.0.1-SNAPSHOT
    sp04-orderservice
    Demo project for Spring Boot

    
        1.8
    

    
        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
            
                
                    org.junit.vintage
                    junit-vintage-engine
                
            
        
        
            cn.tedu
            sp01-commons
            0.0.1-SNAPSHOT
        
        
            org.springframework.cloud
            
                spring-cloud-starter-netflix-eureka-client
            
        
        
            org.springframework.boot
            spring-boot-starter-actuator
        
        
            org.springframework.cloud
            
                spring-cloud-starter-netflix-hystrix
            
        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                Hoxton.RELEASE
                pom
                import
            
        
    

2. 修改 application.yml 文件

  • ribbon 重试和 hystrix 超时这里没有设置,采用了默认值
spring:
  application:
    name: order-service
    
server:
  port: 8201  
  
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
      
feign:
  hystrix:
    enabled: true
    
management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream

3. 主程序添加 @EnableFeignClients 注解

package cn.tedu.sp04;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
//@EnableDiscoveryClient
//@SpringBootApplication
@EnableFeignClients
@SpringCloudApplication
public class Sp04OrderserviceApplication {
    public static void main(String[] args) {
        SpringApplication.run(Sp04OrderserviceApplication.class, args);
    }
}

4. 指定降级类

4.1 ItemFeignService

package cn.tedu.sp04.order.feignclient;
import java.util.List;
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 cn.tedu.sp01.pojo.Item;
import cn.tedu.web.util.JsonResult;
@FeignClient(name="item-service", fallback = ItemFeignServiceFB.class)
public interface ItemFeignService {
    @GetMapping("/{orderId}")
    JsonResult> getItems(@PathVariable String orderId);
    @PostMapping("/decreaseNumber")
    JsonResult decreaseNumber(@RequestBody List items);
}

4.2 UserFeignService

package cn.tedu.sp04.order.feignclient;
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;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
@FeignClient(name="user-service", fallback = UserFeignServiceFB.class)
public interface UserFeignService {
    @GetMapping("/{userId}")
    JsonResult getUser(@PathVariable Integer userId);
    @GetMapping("/{userId}/score") 
    JsonResult addScore(@PathVariable Integer userId, @RequestParam Integer score);
}

4.3ItemFeignServiceFB

  • 获取商品列表的降级方法,模拟使用缓存数据
package cn.tedu.sp04.order.feignclient;
import java.util.Arrays;
import java.util.List;
import org.springframework.stereotype.Component;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.web.util.JsonResult;
@Component
public class ItemFeignServiceFB implements ItemFeignService {
    @Override
    public JsonResult> 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 items) {
        return JsonResult.err("无法修改商品库存");
    }
}

5. 降级类

5.1 UserFeignServiceFB

  • 获取用户信息的降级方法,模拟使用缓存数据
package cn.tedu.sp04.order.feignclient;
import org.springframework.stereotype.Component;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
@Component
public class UserFeignServiceFB implements UserFeignService {
    @Override
    public JsonResult 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.2 OrderServiceImpl

package cn.tedu.sp04.order.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.sp01.service.OrderService;
import cn.tedu.sp04.order.feignclient.ItemFeignService;
import cn.tedu.sp04.order.feignclient.UserFeignService;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private ItemFeignService itemService;
    @Autowired
    private UserFeignService userService;
    @Override
    public Order getOrder(String orderId) {
        //调用user-service获取用户信息
        JsonResult user = userService.getUser(7);
        //调用item-service获取商品信息
        JsonResult> items = itemService.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减少商品库存
        itemService.decreaseNumber(order.getItems());
        
        //TODO: 调用user-service增加用户积分
        userService.addScore(7, 100);
        log.info("保存订单:"+order);
    }
}

6. order-service 配置启动参数,启动两台服务器

  • --server.port=8201
  • --server.port=8202

7. 启动服务,访问测试

SpringClub05-feign 集成工具_第2张图片

hystrix dashboard 监控 order service 断路器

监控

六、hystrix + turbine 集群聚合监控

hystrix dashboard 一次只能监控一个服务实例,使用 turbine 可以汇集监控信息,将聚合后的信息提供给 hystrix dashboard 来集中展示和监控

1. 新建 sp10-turbine 项目

SpringClub05-feign 集成工具_第3张图片

2.添加依赖

image.png



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.2.1.RELEASE
         
    
    cn.tedu
    sp10-turbine
    0.0.1-SNAPSHOT
    sp10-turbine
    Demo project for Spring Boot

    
        1.8
        Hoxton.RELEASE
    

    
        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-turbine
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
            
                
                    org.junit.vintage
                    junit-vintage-engine
                
            
        
    

    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

3.修改 application.yml 文件

spring:
  application:
    name: turbin
    
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")` 

4. 主程序

添加 @EnableTurbine@EnableDiscoveryClient 注解

package cn.tedu.sp10;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.turbine.EnableTurbine;
@EnableTurbine
@EnableDiscoveryClient
@SpringBootApplication
public class Sp10TurbineApplication {
    public static void main(String[] args) {
        SpringApplication.run(Sp10TurbineApplication.class, args);
    }
}

5.访问测试

监控

你可能感兴趣的:(intellij-idea,springboot)