Spring Cloud-hystrix Feign(九)
前面使用ribbon拦截RestTemplate 实现服务的负载均衡 使用Hystrix进行熔断降级请求缓存 用原生的方式 将会有大量的模板代码,feigin就是rabbon和Histrix的整合 同
使用feign只需要通过接口对服务方的绑定 实现多处调用
使用例子
1.引入Pom依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <!--开启端点 用于dashboard监控--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
2.定义一个接口描述服务信息
@FeignClient("PROVIDER") @RequestMapping("/user") public interface UserService { @RequestMapping(value = "/findById",method = RequestMethod.GET) public User findById(@RequestParam("id") Integer id); @RequestMapping(value = "/findAll",method = RequestMethod.GET) public List<User> findAll(); @RequestMapping(value = "/deleteById",method = RequestMethod.GET) public Boolean deleteById(@RequestParam("id")Integer id); @RequestMapping(value = "/update",method = RequestMethod.POST) public Boolean update(@RequestBody User user); }
@FeignClient 是用于服务发现 发现指定服务
@RequestMapping 为请求指定服务 指定方式
@RequestBody 表示参数是封装在报文体出传输 服务端 接收也需要打RequestBody
3.入口类开启Fegin以及服务发现
@SpringBootApplication @EnableFeignClients //开启feigin @EnableDiscoveryClient //开启服务发现 public class SpringCloudFeignConsumerApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudFeignConsumerApplication.class, args); } }
4.通过注入实现调用
@RestController public class UserContorller { @Autowired UserService userService; @RequestMapping("/findById") @ResponseBody public User findById(Integer id) { return userService.findById(id); } @RequestMapping("/findAll") @ResponseBody public List<User> findAll() { return userService.findAll(); } @RequestMapping("/deleteById") @ResponseBody public Boolean deleteById(Integer id) { return userService.deleteById(1); } @RequestMapping("/update") @ResponseBody public Boolean update() { User user=new User(); user.setId(1); user.setName("测试修改"); User filterUser = userService.findById(user.getId()); filterUser.setName(user.getName()); userService.update(filterUser); return true; } }
继承特性
将接口定义在服务端 服务消费者通过继承方式实现服务订阅。避免 每个服务消费者 都要重复定义接口描述
1.新增一个API项目并被服务端和客户端依赖 同时依赖web 因为需要使用springMVC注解对接口描述
3.pom依赖
<!--定义接口约束 需要用到springMVC的注解--> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </dependency> </dependencies> <!--用普通jar打包的方式--> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <executions> <execution> <id>attach-sources</id> <goals> <goal>jar</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
jar包打包方式 都改为传统的打包方式 否则 其他依赖会报错 找不到 符号 因为存在2个入口类
3.api项目添加一个接口描述
@RequestMapping("/user") public interface UserService { @RequestMapping(value = "/findById",method = RequestMethod.GET) public User findById(@RequestParam("id") Integer id); @RequestMapping(value = "/findAll",method = RequestMethod.GET) public List<User> findAll(); @RequestMapping(value = "/deleteById",method = RequestMethod.GET) public Boolean deleteById(@RequestParam("id")Integer id); @RequestMapping(value = "/update",method = RequestMethod.POST) public Boolean update(@RequestBody User user); }
4.更改provider
@Controller @RequestMapping("/user") public class UserContorller implements UserService { List<User> users; public UserContorller() { users = new ArrayList<>(); users.add(new User(1, "小明", 1)); users.add(new User(2, "小香", 0)); users.add(new User(3, "小方", 0)); users.add(new User(4, "小张", 0)); users.add(new User(6, "小李", 0)); } @RequestMapping("/findById") @ResponseBody public User findById(Integer id) { return users.stream().filter(c -> c.getId().intValue() == id.intValue()).findAny().get(); } @RequestMapping("/findAll") @ResponseBody public List<User> findAll() { return users; } @RequestMapping("/deleteById") @ResponseBody public Boolean deleteById(Integer id) { return users.removeIf(c -> c.getId().intValue() == id.intValue()); } @RequestMapping("/update") @ResponseBody public Boolean update(@RequestBody User user) { User filterUser = findById(user.getId()); filterUser.setName(user.getName()); return true; } }
5.更改consumer Service继承api的Service
@FeignClient("PROVIDER") public interface UserService extends com.liqiang.api.UserService { }
6.consumer调用注入Service
@RestController public class UserContorller { @Autowired UserService userService; @RequestMapping("/findById") @ResponseBody public User findById(Integer id) { return userService.findById(id); } @RequestMapping("/findAll") @ResponseBody public List<User> findAll() { return userService.findAll(); } @RequestMapping("/deleteById") @ResponseBody public Boolean deleteById(Integer id) { return userService.deleteById(1); } @RequestMapping("/update") @ResponseBody public Boolean update() { User user=new User(); user.setId(1); user.setName("测试修改"); User filterUser = userService.findById(user.getId()); filterUser.setName(user.getName()); userService.update(filterUser); return true; } }
继承的缺点 接口修改 会导致调用端的构建失败 不同通过版本控制可以避免
Ribbon配置
全局配置
通过ribbon.<属性>=..
ribbon: ConnectTimeout: 500 ReadTimeout=5000
指定服务配置及重试
<服务名>.ribbon.<属性>=... 服务名为@FeginClient配置
#对指定服务设置rabbion全局参数 Fegin接口@FeignClient("PROVIDER") PROVIDER: ribbon: #配置首台服务器重试1次 MaxAutoRetries: 1 #配置其他服务器重试两次 MaxAutoRetriesNextServer: 2 #链接超时时间 ConnectTimeout: 500 #请求处理时间 ReadTimeout: 2000 #每个操作都开启重试机制 OkToRetryOnAllOperations: true
hystrix配置
全局配置
与hystrix配置一样
#配置断路器超时时间,默认是1000(1秒) hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 12000
通过指令配置
hystrix.command.<commandKey> commandKey默认以方法名作为标识
禁用hystrix
全部禁用
feign: hystrix: enabled: true #启用hystrix false为禁用
指定服务禁用
@Configuration public class DisableHystrixConfiguration { @Bean @Scope("prototype") public Feign.Builder feignBuilder() { return Feign.builder(); } }
@FeignClient(value = "PROVIDER",configuration = DisableHystrixConfiguration.class) public interface UserService extends com.liqiang.api.UserService { }
服务降级
1.增加一个Service的接口类
public class UserServiceFaillBack implements UserService { @Override public User findById(Integer id) { return null; } @Override public List<User> findAll() { return null; } @Override public Boolean deleteById(Integer id) { return false; } @Override public Boolean update(User user) { return false; } }
@FeignClient(value = "PROVIDER",configuration = DisableHystrixConfiguration.class,fallback = UserServiceFaillBack.class) public interface UserService extends com.liqiang.api.UserService { }
请求压缩
feign: compression: request: enabled: true #feign请求gizp压缩 减少网络损耗 response: enabled: true #feign响应gizp压缩 减少网络损耗 #启用压缩 压缩指定格式 超过对应大小的才开始压缩 #feign.compression.request.enabled=true #feign.compression.request.mime-types=text/xml,application/xml,application/json #指定格式 #feign.compression.request.min-request-size=2048 #指定大小
日志配置
对服务开启日志输出
#还需要在application配置Logger.Level 详情区主类去看
logging:
level:
com:
liqiang:
feginService:
HelloExtendService: DEBUG
还要在主类设置日志界别
class SpringcloudFeiginConsumerApplication { /*** * 日志可选级别 * • NONE: 不记录任何信息。 * • BASIC: 仅记录请求方法、URL以及响应状态码和执行时间。 * • HEADERS: 除了记录BASIC级别的信息之外, 还会记录请求和响应的头信息。 // * • FULL: 记录所有请求与响应的明细, 包括头信息、 请求体、 元数据等。 // * @return */ @Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } public static void main(String[] args) { SpringApplication.run(SpringcloudFeiginConsumerApplication.class, args); } }
可能遇到的问题
如果遇到feign注解无效,同时配置了scan 包扫描。 可以在@EnableFeignClients("你的feign包")或者application.properties 配置feign.client.package=com.crb.ocms.stock