手把手搭建springCloud框架(三)——Ribbon,Feign和Hystrix

1,010 阅读8分钟

前言

大家好啊,明天就是国庆节了,今天再发一篇博客。

其实本篇博客,是连接上一篇博客来的。

手把手搭建springCloud框架(二)——eureka客户端+swagger+mybatisPlus: juejin.cn/post/684490…

因此,本篇博文,是从上一篇博文创建的项目发展而来,请先搭建上一篇博文所有的项目后,再来搭建本项目。

内容

上一篇博文的项目目录:

本篇博文搭建成功后的项目目录:

因此,需要注意的是以下内容,我将一一贴出来,给到大家。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.blog</groupId>
	<artifactId>permission-service</artifactId>
	<version>0.0.1-permissionService-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>permission-service</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.1.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!--引入mybatis依赖-->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.2.0</version>
		</dependency>
		<!--引入mybatisPlus依赖-->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatisplus-spring-boot-starter</artifactId>
			<version>1.0.5</version>
		</dependency>
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus</artifactId>
			<version>2.1.8</version>
		</dependency>
		<!-- 模板引擎 代码生成 -->
		<dependency>
			<groupId>org.apache.velocity</groupId>
			<artifactId>velocity</artifactId>
			<version>1.7</version>
		</dependency>
		<!--  mysql驱动器依赖-->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.6</version>
		</dependency>
		<!--springboot的web依赖-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!--Herbnate实体依赖-->
		<dependency>
			<groupId>javax.persistence</groupId>
			<artifactId>persistence-api</artifactId>
			<version>1.0.2</version>
		</dependency>
        <!--常用基础Jar包,构建自己的thrift连接池-->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-pool2</artifactId>
		</dependency>
		<!-- 引入Lombok -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<scope>provided</scope>
		</dependency>
		<!--spring默认使用yml中的配置,但有时候要用传统的xml或properties配置-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
		<!-- 解析json数据的依赖-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.1.41</version>
		</dependency>
		<!-- jwt认证协议依赖,token授权依赖 -->
		<dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt</artifactId>
			<version>0.9.0</version>
		</dependency>

		<!--这是权限认证依赖,这个需要屏蔽,不然使用eureka到swagger会需要登录-->
		<!--<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>-->

		<!-- 这三个依赖,是为了序列化对象转换为json -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-core</artifactId>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-annotations</artifactId>
		</dependency>
<!--********************************************************************************-->
		<!--springCloud启动依赖-->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter</artifactId>
		</dependency>
		<!--srpingCloud的eureka服务注册依赖,client端-->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
			<version>2.0.0.RELEASE</version>
		</dependency>
		<!--srpingCloud的eureka服务注册依赖,server端,是因为本服务既要充当生产者,又要充当消费者-->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
			<version>2.0.0.RELEASE</version>
		</dependency>
		<!--springCloud的fiegn依赖-->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-feign</artifactId>
		</dependency>
		<!--springCloud的ribbon依赖-->
		<dependency>
			<groupId>com.netflix.ribbon</groupId>
			<artifactId>ribbon</artifactId>
			<version>2.2.2</version>
		</dependency>
		<!--histrix依赖-->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-hystrix</artifactId>
		</dependency>
		<!--Hystrix 的一个类库-->
		<dependency>
			<groupId>com.netflix.hystrix</groupId>
			<artifactId>hystrix-javanica</artifactId>
		</dependency>
		<!--新增依赖,swaggerUI-->
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>2.7.0</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>2.7.0</version>
		</dependency>
	</dependencies>

	<!-- 引入spring cloud的依赖,不能少,主要用来管理Spring Cloud生态各组件的版本 -->
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Edgware.RELEASE</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>

		<resources>
			<resource>
				<!--需要打包的目录-->
				<directory>src/main/resources</directory>
				<!--目录中的文件类型-->
				<includes>
					<include>**/*.xml</include>
					<include>**/*.properties</include>
				</includes>
			</resource>
			<resource>
				<directory>src/main/java</directory>
				<includes>
					<include>**/*.properties</include>
					<include>**/*.xml</include>
				</includes>
				<filtering>false</filtering>
			</resource>
		</resources>

		<finalName>permission</finalName>
	</build>


</project>

application.properties

#服务名
spring.application.name=permission
#端口
server.port=3333
################################################################################################################
#数据库信息
spring.datasource.url: jdbc:mysql://localhost:3306/myblog?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username: root
spring.datasource.password: 123456
spring.datasource.driver-class-name: com.mysql.jdbc.Driver
#################################################################################################################
spring.jmx.default-domain=${spring.application.name}
spring.jpa.open-in-view=false
# springframework.web日志以DEBUG级别输出
logging.level.org.springframework.web=info
###############################################################################################################
# 如果是放在src/main/java目录下 classpath:/com/blog/*/mapper/*Mapper.xml
# 如果是放在resource目录 classpath:/mapper/*Mapper.xml
mybatis-plus.mapper-locations=classpath:/mapper/*Mapper.xml
#实体扫描,多个package用逗号或者分号分隔
mybatis-plus.type-aliases-package=com.blog.*.entity
#驼峰下划线转换
mybatis-plus.global-config.db-column-underline=true
#刷新mapper 调试神器
mybatis-plus.global-config.refresh-mapper=false
#数据库大写下划线转换
mybatis-plus.global-config.capital-mode=true
#################################################################################################################
#配置Swagger相关信息(从eureka跳到swagger页面)
eureka.instance.prefer-ip-address=true
eureka.instance.hostname=:${spring.cloud.client.ipAddress}
eureka.instance.status-page-url:http://${spring.cloud.client.ipAddress}:${server.port}/swagger-ui.html
####################################################################################################################
#eureka注册中心路径
eureka.client.service-url.defaultZone=http://localhost:1111/eureka/
#表示本服务发送给eureka服务端的心跳时间,设置时间不能太长,不然会导致eureka检测不到服务从而会驱逐服务下线
eureka.instance.leaseRenewalIntervalInSeconds: 2
#当本服务掉线后,eureka服务端取掉本服务的缓存的时间
eureka.instance.leaseExpirationDurationInSeconds: 4
#注册到eureka上的服务的名字
eureka.instance.instance-id=http://${spring.cloud.client.ipAddress}:${server.port}/${spring.application.name}
#######################################################################################################################
#逻辑删除配置(下面3个配置),不是真实删除数据,只是逻辑删除数据
mybatis-plus.global-config.logic-delete-value=1
mybatis-plus.global-config.logic-not-delete-value=0
mybatis-plus.global-config.sql-injector=com.baomidou.mybatisplus.mapper.LogicSqlInjector
##############################################################################################################
mybatis-plus.global-config.meta-object-handler=com.blog.permission.mabatisPlus.MyMetaObjectHandler
mybatis-plus.configuration.map-underscore-to-camel-case=true
mybatis-plus.configuration.cache-enabled=false
mybatis-plus.configuration.jdbc-type-for-null=null
mybatis-plus.typeAliasesPackage=com.blog.permission.entity
mybatis-plus.global-config.id-type=3
mybatis-plus.global-config.field-strategy=2
mybatis-plus.global-config.key-generator=com.baomidou.mybatisplus.incrementer.OracleKeyGenerator
##############################################################################################################
#默认的超时时间设置
hystrix.metrics.polling-interval-ms=60000
#启动Hystrix
hystrix.metrics.enabled=true
##############################################################################################################
#将日志放到项目里面的logs文件夹的permission.log文件中
logging.file=logs/permission.log
##############################################################################################################

PermissionApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.SpringCloudApplication;
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.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@EnableFeignClients      //feign注解
@ComponentScan(basePackages = "com.blog.permission")
@EnableAsync   //支持多线程
@EnableSwagger2   //swagger注解
@EnableWebMvc    //配合WebMvcConfig配置文件,目的是为了使用webMVC
@SpringBootApplication
@EnableEurekaClient   //注册到eureka上的注解
@EnableCircuitBreaker  //Hystrix熔断机制的注解
public class PermissionApplication {

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

接下来,是要添加一个测试Feign和Hystrix的接口:

controller层

代码:

@ApiOperation(value = "测试feign的接口")
    @PostMapping("/testForFeign")
    public Result testForFeign(@ApiParam(value = "用户id")@RequestParam(required = false) String userId){
        return userService.testForFeign(userId);
    }

service层

  @Override
    public Result testForFeign(String userId) {
        Result result=perimissionFallback.get(userId);
        return result;
    }

新增:perimissionFallback.java

import com.blog.permission.feign.PermissionClient;
import com.blog.permission.method.Result;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
@Service
public class PerimissionFallback {
    @Resource
    private PermissionClient permissionClient;

    private static final Logger logger = LoggerFactory.getLogger(PerimissionFallback.class);

    @HystrixCommand(fallbackMethod = "getFallBack")
    public Result get(String userId) {
        Result result=permissionClient.get(userId);
        return result;
    }

    public Result getFallBack(String userId) {
        logger.info("调用接口:permission/login/get 失败,降级处理");
        return new Result().fallBack("permission/login/get",userId);
    }
}

代码里面的注解:@HystrixCommand(fallbackMethod = "getFallBack"),这个就是引入了Hystrix的方法降级,降级的方法就是getFallBack这个方法,图片中我用红色箭头标出来了,很容易理解。

新增:PermissionClient.java

import com.blog.permission.method.Result;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;


@Component
@FeignClient(value = "permission")
public  interface PermissionClient {

    //这是跨服务调用permission服务的“permission/login/get”接口
    @RequestMapping(value = "/login/get", method = RequestMethod.GET)
    public  Result get(@RequestParam("userId") String userId);
}

可以看一下,这个跨服务想要调用的方法是这样的:

对比一下上面的代码,会发现有什么不同的地方呢?

好的,现在可以开始启动服务了。

表示服务已经启动成功,打开网页,输入网址:localhost:1111,如果不知道这个网址是怎么了,请查看我之前的一篇系列博文:

手把手搭建springCloud框架(一)——eureka的服务端(server)的搭建:juejin.cn/post/684490…

点击:http://192.168.2.92:3333/permission ,进入到swagger页面。

调用接口,开始测试:

可以看到,我测试Feign跨服务是成功了的,当然,我这次测试是自己跨服务调用自己,当然会有问题,不过这里暂且不提。

接下来,我们开始测试Hystrix的熔断机制。 在接口:login/get里的service层打断点

然后再在swagger上调用接口,我们发现,当打上断点到一点的时间点(其实就是我们设置的Hystrix的熔断时间,仔细看一下我们的配置文件,application.properties),然后再去掉断点,让程序运行,那么调用接口就会失败,返回的内容也是我们自己定义的返回内容:

这个返回内容,不就是我们自己定义的fallBack方法吗?

讲到这里,我们已经将Feign和Hystrix讲了,那么Ribbon在哪里呢?当你开始使用Feign的时候,只要你的依赖里面有Ribbon,那么springCloud会默认使用Ribbon来进行分发Feign的请求,所以我们无须关心这一点。

总结

这个搭建过程,其实还是比较曲折的,原因就是网上的一些技术博客,可能由于springboot和springCloud的依赖版本不同,其实不能给到我帮助,因此许多的配置方面的东西,我都需要自己去看官方文档,这个就比较累了。

最后,希望能够帮助看到这篇文章的你。