这篇文章是总结SpringCloud NetFlix的,其中使用的Eureka、Hystrix、zuul、feign等技术已经停更,目前是不推荐使用的,但是为了弄清楚微服务的架构演进,以及不同组件之间的差异性,笔者感觉还是需要学习这些已经过时的技术的,循序渐进式的学习才是最好的学习方法。
一、微服务架构
有基础的话,建议直接跳到Eureka开始看,前面都是老生常谈。
1.微服务架构图
服务从最初的单体,到后来的前后端分离,再到分布式,再到现在的微服务架构,经历的变化不可谓不大,到目前为止,微服务已经是市场主流了。那微服务具体包含了哪些部分呢?springcloud的官网给出了下面的一张图:多种渠道对服务进行访问先经过网关系统,网关系统是所有访问的必经之路,然后才会到达微服务,同时微服务集群被配置中心、熔断、服务注册等模块支撑着,最后处理到达消息和数据库模块。
2.微服务架构技术一览
SpringCloud中各种服务的替代产品一览:
1、服务注册中心
1、Eureka停止更新
2、Zookeeper 替代(保守)
3、Consul go语言
4、nacos(推荐重点)替换
2、服务调用
1、Ribbon停更/ˈrɪbən/
2、LoadBalanced
3、服务调用
1、Feigin维护停更
2、OpenFeigin(推荐使用重点)
4、服务降级
1、Hystrix停更(国内大规模使用)
2、resilience4j(国外推荐)
3、Sentinel(国内推荐)
5、服务网关
1、Zuul(分裂停更)
2、GateWay(推荐重点)
6、服务配置
1、SpringConfig(停更)
2、Nacos(推荐)
7、服务总线
1、Bus(停更)
2、Nacos(推荐)
二、搭建服务
1.创建一个普通的maven父工程,导入jar包
这里导入的是2021.8.28最新的版本。而且目前springcloud不仅支持城市名的版本号,也支持了时间的版本号,这样让人看的更清晰,这点可能是和阿里学的,毕竟还是以时间为版本号更加醒目一些。此处需要注意的是父工程使用的是dependencyManagement来管理的jar包,使用该标签管理jar包时,父工程并不会导入这些jar,只有在被使用的子工程中才会导入这些jar包。
<properties> <spring-cloud.version>2020.0.3</spring-cloud.version> <spring-boot.version>2.4.4</spring-boot.version> <mysql-connecoter.version>5.1.38</mysql-connecoter.version> <druid.version>1.2.4</druid.version> <mybatis-boot.version>2.1.2</mybatis-boot.version> <junit.version>4.13</junit.version> <lombok.version>1.18.18</lombok.version> <log4j.version>1.2.14</log4j.version> <logback.version>1.2.5</logback.version> </properties> <dependencyManagement> <dependencies> <!-- 最新版的spring cloud 依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!--导入最新的spring-boot--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!--数据库相关依赖--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql-connecoter.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis-boot.version}</version> </dependency> <!--单元测试包--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> </dependency> <!--lombok依赖包--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </dependency> <!--日志包log4j--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>${logback.version}</version> </dependency> </dependencies> </dependencyManagement>
2.创建子服务
这个服务提供pojo、api接口、数据库连接等实现,这里将这些都写在了这一个服务里,事实上在真实的微服务架构中,还有可能将各个服务的api接口拆分出来,将数据库连接拆分出来,这些都是有可能的,不过再怎么拆,方式都是一样,学其神莫学其形。
提供必要的数据库配置,笔者这里建立两个数据库,每个数据库建立两个表,下面提供些建表语句:
-- 创建数据库 create database `sunac-mdm`; use `sunac-mdm`; create database `sunac-cockpit`; use `sunac-cockpit`; -- 建表 create table if not EXISTS `sunac-cockpit`.`sys_user`( `id` int(20) not null , `user_name` varchar(100) not null , `user_address` varchar(20) not null , `user_password` varchar(20) not null, primary key(`id`) )engine=INNODB default charset = utf8; create table if not EXISTS `sunac-cockpit`.`sys_role`( `id` int(20) not null , `role_name` varchar(100) not null , primary key(`id`) )engine=INNODB default charset = utf8; create table if not EXISTS `sunac-mdm`.`mdm_org`( `id` int(20) not null , `org_level` varchar(100) not null , `org_name` varchar(100) not null , `org_parent_id` varchar(100) not null , primary key(`id`) )engine=INNODB default charset = utf8; create table if not EXISTS `sunac-mdm`.`mdm_project`( `id` int(20) not null , `project_name` varchar(100) not null , `project_address` varchar(100) not null , `project_area` varchar(100) not null , primary key(`id`) )engine=INNODB default charset = utf8;
事实上笔者下面写的所有服务并没有连接数据库,这块其实对于这篇文章来说其实没啥用。
三、Eureka
Eureka是SpringCloud Netflix中用来做服务注册与发现的组件,类似于我们熟知的zookeeper,SpringCloud集成了Eureka使得它的使用变得简单方便,Eureka是BS架构,需要搭建服务端和客户端,不过搭建起来都很容易。
1.Eureka服务端搭建
SpringCloud中使用的组件比较多,但是各个组件的接入都很便捷基本都是三步就可以搞定,第一步导入相关依赖,第二步配置yml文件,第三步添加相关注解。其余工作SpringCloud都已经帮我们做了。
导入Eureka服务端依赖
SpringCloud的版本使用的2020.0.3,对应的应为字母版本是Hoxton.SR12,注意现在SprngCloud支持这俩种版本的定义。
Eureka的版本使用的是3.0.3。
-- 创建数据库 create database `sunac-mdm`; use `sunac-mdm`; create database `sunac-cockpit`; use `sunac-cockpit`; -- 建表 create table if not EXISTS `sunac-cockpit`.`sys_user`( `id` int(20) not null , `user_name` varchar(100) not null , `user_address` varchar(20) not null , `user_password` varchar(20) not null, primary key(`id`) )engine=INNODB default charset = utf8; create table if not EXISTS `sunac-cockpit`.`sys_role`( `id` int(20) not null , `role_name` varchar(100) not null , primary key(`id`) )engine=INNODB default charset = utf8; create table if not EXISTS `sunac-mdm`.`mdm_org`( `id` int(20) not null , `org_level` varchar(100) not null , `org_name` varchar(100) not null , `org_parent_id` varchar(100) not null , primary key(`id`) )engine=INNODB default charset = utf8; create table if not EXISTS `sunac-mdm`.`mdm_project`( `id` int(20) not null , `project_name` varchar(100) not null , `project_address` varchar(100) not null , `project_area` varchar(100) not null , primary key(`id`) )engine=INNODB default charset = utf8;
配置yml文件
server: port: 7001 # 服务端口号 eureka: instance: hostname: eureka7001 #当前示例名称 client: service-url: defaultZone: http://localhost:7001/eureka/ #Eureka服务端地址,这是单机版配置 register-with-eureka: false # 是否像Eureka服务中注册自己,本身是服务无需注册 fetch-registry: false # 是否从Eureka获取注册信息,单机时不用,集群时开启 server: enable-self-preservation: false # Eureka的自我保护模式,默认开启
添加相关注解
上面两步完成后在启动类上加上@EnableEurekaServer的注解即可。
@SpringBootApplication @EnableEurekaServer public class Eureka7001Application { public static void main(String[] args) { SpringApplication.run(Eureka7001Application.class, args); } }
这样我们就完成了Eureka服务端的搭建,我们启动服务,然后浏览器中输入localhost:7001,就可以查看当前的服务了,如下:
2.Eureka客户端搭建
导入jar包
这里我们需要创建一个服务提供者的微服务,这里不展示细节了,第一步导入Eureka客户端的依赖和SpringBoot的相关启动器
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.4.4</version> </dependency>
配置yml文件
server: port: 8001 eureka: client: service-url: defaultZone: http://localhost:7001/eureka/ # 单机时配置Eureka提供注册的地址 fetch-registry: false # 客户端无需获取注册信息 spring: application: name: cloudname # 配置当前服务名,这个在消费者调用时需要使用
添加注解
Eureka客户端也是一样只需要在启动类上加上一个EnableEurekaClient注解接口,如下:
@SpringBootApplication @EnableEurekaClient public class Provider8001Application { public static void main(String[] args) { SpringApplication.run(Provider8001Application.class, args); } }
然后启动服务,查看Eureka的服务页面,如下:
3.Eureka集群搭建
集群搭建时,对应的依赖无需变动,注解也无需变动,只需要增加一个Eureka服务端结点和Eueka客户端结点即可,更改适量的配置即可实现,下面列出集群的配置文件,其他与单体搭建时没有区别。
Eureka服务端7001配置如下:
server: port: 7001 eureka: instance: hostname: eureka7001 client: service-url: defaultZone: http://localhost:7002/eureka/ # 集群模式时,只需要告诉当前服务,其他结点的注册地址即可,无需加当前结点自己的地址 register-with-eureka: false fetch-registry: true # 集群模式时,需要获取其他结点的信息,这里要开启 server: enable-self-preservation: false
Eureka服务端7002配置如下:
server: port: 7002 eureka: instance: hostname: eureka7002 client: service-url: defaultZone: http://localhost:7001/eureka/ # 集群模式时,只需要告诉当前服务,其他结点的注册地址即可,无需加当前结点自己的地址 register-with-eureka: false fetch-registry: true # 集群模式时,需要获取其他结点的信息,这里要开启 server: enable-self-preservation: false
Eureka客户端8001配置如下:
server: port: 8001 eureka: client: service-url: defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/ # 向所有的Eureka服务中注册自己,使用逗号隔开多个服务 fetch-registry: false register-with-eureka: true spring: application: name: cloudname
Eureka客户端8002配置如下:
server: port: 8002 eureka: client: service-url: defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/ # 向所有的Eureka服务中注册自己,使用逗号隔开多个服务 spring: application: name: cloudname
这样Eureka的集群就搭建完成了,下面看下启动了两个服务端和两个客户端:
这样整个Eureka集群就搭建完成了,在真实的项目中我们也就是只需要提供这么多配置就可以支撑服务的运行了,所以Eureka的掌握并不难,几个简单的配置和注解就可以实现服务的注册与发现了。
4.CAP原则
CAP原则指的是在一个分布式系统中的,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)这三个要素最多只能同时实现两点,不可能三者兼顾。
一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
可用性(A):保证每个请求不管成功或者失败都有响应。
分区容忍性(P):系统中任意信息的丢失或失败不会影响系统的继续运作。
Zookeeper基于CP原则:
ZK设计的原则满足CP原则,ZK的一致性属于弱一致性,数据可能并不会立马更新会有稍许的延迟,当服务被注册到ZK中时,立马去获取该服务可能是找不到的,所以ZK的一致性是弱一致性,再说说ZK不满足的可用性,在分布式环境下的ZK集群会分为master结点与cluster结点,若是master结点挂掉以后,就需要从新选举出master结点(与redis的哨兵模式类似),在选举过程中整个ZK集群不可用,所以ZK不满足可用性,而容错性则是分布式系统必须满足的,即:当一个节点出现问题挂掉以后是不能影响到其他结点工作的。所以ZK是遵循CP原则设计的。
Eureka基于AP原则:
Eureka的设计满足AP原则,Eureka的可用性体现在,在集群环境下所有的Eureka结点都是平等的,不会存在选举master的过程,任何一个节点崩了都不会影响到其他结点的正常工作,所以Eureka的可用性会比较高,此外Eureka的可用性有以下体现,①Eureka会定时发送心跳检测给服务,若是短时间内大量服务没有响应,Eureka并不会立刻将这些服务实例注销,而是会启动自我保护模式,这样就保证了以外情况下服务宕机重启后可以迅速恢复。Eureka不满足一致性要求,因为在自动自我保护模式后,任何的Eureka仍然可以接受注册,但是不会同步到其他结点上,所以数据一致性并不高,只有所有服务恢复了,才会将新注册的服务同步到其他结点。分区容错性任何一个节点的异常不会影响到集群。
四、Ribbon
Ribbon是用来提供负载均衡的组件,所谓负载均衡就是将接收到的请求,以合理的方式分配到不同的服务上,从而保证服务的高可用。他的使用也很简单。
1.导入jar包
这里其实我们已经不用导入新的jar包了,在Eureka3.0.0以后的版本中默认集成了Ribbon,我们导入了Eureka的依赖,也就导入了Ribbon的依赖,所以这里无需再导入Ribbon的相关依赖了,可以直接使用,记住切勿导入Ribbon的包,不然是掉不通服务调用者的,这是笔者深深踩过的坑。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>3.0.3</version> </dependency>
2.配置application.yml
因为Ribbon实现的负载均衡需要依赖于Eureka,所以我们必须配置Eureka,这样Eureka才知道去哪里找到服务的实例,但是Ribbon是不需要什么配置的,Eureka配置如下:
server: port: 9001 eureka: client: register-with-eureka: false # 消费者,也就是实现负载均衡的服务无需往Eureka中注册自己 fetch-registry: true # 需要获取Eureka中的实例,所以需要开启 service-url: defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/ # 从哪些Eureka服务中获取实例
这里需要说明的是负载均衡,是对于服务提供者来说的,说白了Ribbon只是借助于Eureka来实现这个过程,并不会对Eureka服务的调用实现负载均衡,具体使用哪个Eureka服务去寻找实例是随机的,而调用服务提供者的实例才是负载均衡管理的范畴。
3.增加相关注解
这里我们使用SpringCloud提供的RestTemplate来实现接口的调用,这是一种rest的调用方式,我们需要手动注册一个RestTemplate,然后使用它来实现负载均衡调用服务提供者。
手动注入RestTemplate
import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; /** * @author pcc * @version 1.0.0 * @className SysConfiguration * @date 2021-09-04 13:30 */ @Configuration public class SysConfiguration { @Bean @LoadBalanced //该注解实现了负载均衡,默认是轮询策略 public RestTemplate getRestTemplate(){ System.out.println("加载了自定义RestTemplate"); return new RestTemplate(); } }
启动类加入EnableEurekaClient注解
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class CustomerRest9001Application { public static void main(String[] args) { SpringApplication.run(CustomerRest9001Application.class, args); } }
使用RestTemplate
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; /** * @author pcc * @version 1.0.0 * @className CusGetInfoByEureka * @date 2021-09-02 15:23 */ @RestController public class CusGetInfoByEureka { @Autowired RestTemplate restTemplate; private String url = "http://CLOUDNAME"; //CLOUDNAME,是服务提供者的实例名称 @RequestMapping("/getInfoByEu") public String getInfo(){ String forObject = restTemplate.getForObject(url+"/pro/getInfo", String.class); System.out.println(forObject); return forObject.toString(); } }
这样就完成了其实,然后展示下调用结果,可以清晰的看到8001和8002是轮流被调用的,这也是Ribbon的默认负载均衡策略,也就是轮询。
4.流程总结
上面写的不算短,其实只是为了说清楚实现负载均衡的整个流程,讲清楚Ribbon是如何集成的,其实上Ribbon真正参与的就一个地方就是加了一个@LoadBalanced注解。其他的其实都与Ribbon关系不大,现在来梳理下这个流程:因为RestTemplate加了LoadBalanced注解,所以SpringCloud会在RestTemplate根据配置的Eureka的地址信息中拿服务实例时实现负载均衡,默认是轮询策略,也就是这次拿的是提供者的一号实例,下次就会拿二号,这个需要借助Eureka才能实现。
遗留问题:
Ribbon的默认负载均衡策略是轮询,此外还支持随机,权重,重试等不同策略,因为Ribbon已经是属于渐渐被市场淘汰的组件,这里就不持续深究了,不过想要更改现有的负载均衡策略,以前是自己注入IRule的实现类即可,不过在Eureka3.0.0以后默认集成了Ribbon,Eureka里面是没有这个类的,想要使用IRule就必须导入Ribbon自己的包,把Eureka中的Ribbon包排除掉,不过笔者试了,没有成功,可能负载均衡策略的变更方式已经变成了其他方式吧,这里笔者也没有搞明白。
5.集中式LB、进程式LB
负载均衡的实现主要包含两类,一类是集中式,一种是进程式
集中式LB:
所谓集中式就是在服务消费方和提供方之间设立换门的LoadBalance策略,不与消费方和提供方有任何耦合,这种集中式的LoadBalance有Nginx。
进程式LB:
所谓进程式就是在消费方中使用LoadBalance策略,负载均衡与消费方耦合在一起,典型代表有Ribbon。
五、Feign
Feign与Ribbon类似,都是用于支持负载均衡的组件,SpringCloud都支持他们的无缝接入,不过使用Feign时调用服务提供者是通过接口调用,有些类似rpc,这与使用RestTemplate还是有很大区别的,不过在底层Feign使用的还是RestTemplate。所以说Feign其实并不是新技术,只是一种rest接口伪装成的rpc模式,他并不是真正的rpc调用,本质还是rest通讯。
1.导入jar包
需要导入Eureka与Feign的jar包,因为调用服务提供者都需要借助Eureka。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.4.4</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>3.0.3</version> </dependency>
2.配置application.yml
默认情况下只需要配置Eureka的相关配置即可,Feign无需增加多余配置,下面是Eureka客户端的标准配置
server: port: 9002 eureka: client: register-with-eureka: false # 不向Eureka中注册自己 fetch-registry: true # 从Eureka获取注册信息 service-url: defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/ # Eureka的集群服务
3.增加相关注解
增加接口
Feign是通过接口来调用的,首先我们需要提供一个接口来去调用Eureka中注册的服务提供者,然后我们才可以真正使用这个接口,通常所说的Feign是OpenFeign,它支持SpringMVC的注解,可以通过注解直接调用到服务提供者的服务。
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RequestMapping; /** * @author pcc * @version 1.0.0 * @className ProService * @date 2021-09-04 22:18 */ @Component @FeignClient(name = "CLOUDNAME") // 该注解用于告诉Feign去Eureka中找哪个服务实例 public interface ProService { @RequestMapping("/pro/getInfo") // 这是SpringMVC的注解,就是调用对应服务实例中的这个路径的方法 String getInfo(); }
启动类增加注解
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients @EnableEurekaClient public class CustomerFeign9002Application { public static void main(String[] args) { SpringApplication.run(CustomerFeign9002Application.class, args); } }
测试效果
import com.cheng.customerfeign9002.service.ProService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author pcc * @version 1.0.0 * @className CusGetInfoByEureka * @date 2021-09-02 15:23 */ @RestController public class CusGetInfoByEureka { @Autowired ProService proService; @RequestMapping("/getInfoByEu") public String getInfo(){ String result = proService.getInfo(); System.out.println(result); return result; } }
上面是测试代码,启动应用,我们测试下,如下图:
可以看到正常获取到了想要的信息,而且是轮询的方式从8001和8002两台服务提供者上获取的信息。
4.Feign如何实现的负载均衡
上面我们已经看到了我们仅仅使用了Eureka和Feign也实现了负载均衡,那Feign是如何实现负载均衡的呢?事实上Feign是依赖了Ribbon来实现的在均衡,很多人都说Feign集成了Ribbon,事实上并非如此,至少在Feign 3.0.3不是这样,Feign中并未集成Ribbon,Ribbon是在Eureka中集成的,我们导入Rureka也就有了Ribbon,Feign自然是可以使用的。
5.Feign与OpenFeign
Feign与OpenFeign事实上是两个东西,因为Feign不支持SpringMVC注解,为了更方便与开发,SpringCloud将Feign对SpringMVC的注解进行了支持,就是OpenFeign了,所以我们在写接口时,才可以使用RequestMapping、GetMapping、PostMapping等注解来直接映射到服务提供者的目标接口,不过有一点必须要注意,SpringMVC的注解不可以写在@FeignClient(name = “******”)该注解的位置,也就是不支持在接口上声明。
六、Hystrix
Hystrix是SpirngCloud Netflix中用于服务治理的一个组件,他可以用来服务降级、服务熔断,服务监控等作用,使用Hystrix可以在集群服务中某一个提供者出现问题时,返回一个调用方可以处理的返回信息,而不会让调用方一致等待,从而导致服务级联故障这种情况的出现。但是Hystrix已经是SpringCloud不推荐的产品,目前SpringCloud推荐使用的是Resilience4j,它是一个轻量级的断路器,作用与Hystrix类似,此外还有alibaba的sentinel,这个才是目前推荐的主流。
1.服务熔断
导入jar包
上面所有的组件使用的都是3.0.3版本的,但是Hystrix已经是不推荐使用的,所以版本上最新目前是2.2.9。这里导入Eureka、Hystrix、boot-web-stater等jar。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.2.9.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.4.4</version> </dependency>
配置application.yml
Hystrix没有需要配置的内容,只需要提供Eureka的配置即可,如下:
server: port: 8003 spring: application: name: cloudname eureka: client: service-url: defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/ register-with-eureka: true # 向Eureka中注册自己 fetch-registry: false # 无需获取Eureka中的注册信息
增加相关注解
注解需要两处提供注解,一处是需要容错的方法上加@HystrixCommand,一处是启动类上加@EnableCircuitBreaker支持断路器支持。
在需要容错的方法上加@HystrixCommand,如下:
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author pcc * @version 1.0.0 * @className ProController * @date 2021-09-02 09:57 */ @RestController @RequestMapping("/pro") public class ProController { @RequestMapping("/getInfo") @HystrixCommand(fallbackMethod = "fallBack") //声明在发生异常时调用fallBack方法返回调用方。 public String getInfo(){ int i = 1/0; return "8001获取信息:成功"; } public String fallBack(){ return "熔断方法返回"; } }
启动类上加@EnableCircuitBreaker注解开启Hystrix的支持,如下:
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient // 开启Eureka @EnableCircuitBreaker //开启断路器Hystrix支持 public class ProviderHystrix8003Application { public static void main(String[] args) { SpringApplication.run(ProviderHystrix8003Application.class, args); } }
测试熔断是否好用
启动8003服务器,然后看下Eureka服务端页面,如下,可以看到服务正常注册进来了:
然后通过Feign的9002服务来测试下是否好用,正常情况下调用到8003肯定会发生异常(代码里有个1除以0),这样就会进入fallBack方法,一起看下是否如此,如下,可以看到调用到8003时走的都是熔断方法。
2.服务降级
服务熔断是对服务提供者作的处理,当发生服务熔断时其实我们调用服务提供者已经没有了意义,完全可以让服务降级,不再访问服务提供者直到服务恢复正常时再继续访问服务提供者。这时我们就可以使用服务降级的策略,来处理请求。或者在整体资源一定时,在高峰时段牺牲一部分服务,来为高并发的服务让出资源,从而保证服务的整体可用,这也是一种服务降级,在双11时,天猫、京东经常会有这样的处理。
导入jar包
jar包与服务降级的jar没有任何区别
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.2.9.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.4.4</version> </dependency>
配置application.yml
服务降级需要手动开启,默认是关闭的,所以我们需要开启服务降级的支持,如下:
server: port: 9002 eureka: client: register-with-eureka: false fetch-registry: true service-url: defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/ feign: circuitbreaker: enabled: true # 开启服务降级支持,以前是Hystrix,现在配置项得使用circuitbreaker
增加相关注解
1)提供一个服务降级的工厂类,这个工厂类的create的对象应该是Feign的接口类, 注意需要把该类交给IOC容器管理。
import com.cheng.customerfeign9002.service.ProService; import org.springframework.cloud.openfeign.FallbackFactory; import org.springframework.stereotype.Component; /** * @author pcc * @version 1.0.0 * @className CallBackFactory * @date 2021-09-05 14:50 */ @Component public class CallBackFactory implements FallbackFactory { @Override public ProService create(Throwable cause) { return new ProService() { @Override public String getInfo() { return "9002 触发服务降级方法了"; } }; } }
2)将服务降级的工程类交给Feign,在服务端异常时自动启用服务降级
import com.cheng.customerfeign9002.callback.CallBackFactory; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RequestMapping; /** * @author pcc * @version 1.0.0 * @className ProService * @date 2021-09-04 22:18 */ @Component @FeignClient(name = "CLOUDNAME",fallbackFactory = CallBackFactory.class) //这就是我们刚刚写的服务降级的工程类 public interface ProService { @RequestMapping("/pro/getInfo") String getInfo(); }
3)启动类上开启服务降级的支持
这里比较特殊,服务降级时无需配置什么注解在启动类上,因为我们在yml配置文件中已经声明开启服务降级的支持了。
关闭服务熔断,测试服务降级
服务熔断若是开启,就不会触发服务降级,个人理解服务熔断是比服务降级更严格的处理异常的方式,服务降级需要依赖于服务提供者的异常来触发服务降级,就像Spring的声明式事务需要依赖抛出的异常来回滚事务一样,一旦开启了熔断碰到异常就会返回特定信息,则不会触发服务降级,所以需要关闭服务熔断,我们直接取消熔断中的@HystrixCommand注解即可。然后启动9002服务器测试如下:
3.Dashboard监控
Dashboard是仪表盘的意思,也就是用来监控服务调用的情况的,我们可以使用Dashboard来监控任何服务,一起来看下如何使用Dashboard吧。
导入jar包
1)服务端jar包导入,这里需要注意,必须导入Eureka与Ribbon的jar包,Eureka默认集成了Ribbon,就只需要导入Eureka与Hystrix、Hystrix-Dashboard的依赖就行了。这里必须导入Eureka的包,不需要配置Eureka的任何配置,但是包必须导入,不然排错,够排一天的。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.2.9.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> <version>2.2.9.RELEASE</version> </dependency>
2)客户端依赖导入,需要被监控的服务都需要导入下面的依赖,就叫他们客户端吧。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.2.9.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> <version>2.2.9.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>2.5.4</version> </dependency>
配置application.yml
1)服务端yml文件配置,其中只有端口号时必须的,eureka不配置项目启动会报错,hystrix配置该项是为了多环境都是在本地搭建时可以正常显示仪表盘。
server: port: 9003 eureka: client: register-with-eureka: false fetch-registry: true service-url: defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/ hystrix: dashboard: proxy-stream-allow-list: localhost # 被监控的服务域名是localhsot的话,该项必须配,否则仪表盘显示连接拒绝
2)客户端yml文件配置
无需增加多余的控制,保持服务原有的配置即可。
增加相关注解
1)服务端增加注解
只需要在服务端的启动类上加上@EnableHystrixDashboard该注解即可
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; @SpringBootApplication @EnableHystrixDashboard public class HystrixDashboard9003Application { public static void main(String[] args) { SpringApplication.run(HystrixDashboard9003Application.class, args); } }
2)客户端增加注解
客户端只需要导入监控的servlet即可。
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author pcc * @version 1.0.0 * @className SysConfiguration * @date 2021-09-04 13:30 */ @Configuration public class SysConfiguration { //注入HystrixDashboard的servelt用于监控信息 @Bean public ServletRegistrationBean hystrixMetricsStreamServlet(){ ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet()); servletRegistrationBean.addUrlMappings("/actuator/hystrix.stream");//这是官方建议的路径命名,可以不使用该路径 return servletRegistrationBean; } }
测试监控结果
到这里Hystrix-Dashboard就搭建完成了,我们首先看下监控页面,如下:
那我们就来看下访问9002监控的服务吧,笔者在左面的9002不断刷新调用,就可以右侧的监控面板的数据在一直变化,可以看到绿色的数字不断增加,这代表着请求的次数都是成功的。
4.总结Hystrix的作用
这样Hystrix就介绍完了,不过Hystrix的作用相对于其他组件来说还是比较丰富的。所以这里对Hystrix的三个作用做个小小的总结。
服务熔断
在服务端(服务提供者)实现服务熔断,导入依赖后,增加两个注解即可实现:@HystrixCommand、@EnableCircuitBreaker。第一个注解定义熔断方法,第二个注解在启动类上开启Hystrix的支持,可以发现服务熔断的实现还是比较简单的。
服务降级
在客户端(服务调用者)实现服务降级,导入依赖后,在yml配置文件中开启,服务降级的支持:feign.circuitbreaker.enabled: true,然后通过实现FallbackFactory接口来重写create方法,然后返回Feign的接口。最后还需要在FeignClient直接中声明会掉工程为刚刚所实现的类:@FeignClient(name = “CLOUDNAME”,fallbackFactory = CallBackFactory.class) 。这样就完成了服务降级。
服务监控
服务监控相对于服务熔断和服务降级来说略微多一点操作,不过也不难,服务端可以集成在任何的应用上,也可以单开一个服务端,若是使用服务监控,建议还是单开服务端,不要与其他服务进行耦合,服务端导入依赖后,只需要在启动类上加一个@EnableHystrixDashboard注解即可,然后被监控的服务需要注册一个Servlet:HystrixMetricsStreamServlet。该Servlet配置的拦截路径,就是Dashboard的监控路径。
七、Gateway
本来这里是准备总结Zuul的,但是因为Zuul与其他组件存在版本问题,笔者多次尝试未果,就放弃了Zuul,转而使用了简单的Gateway网关,Gateway并不是最早的网飞版的组件,他是SpringCloud 参照Zuul设计的一款高性能的轻量级的网关组件,Gateway的使用同样需要依赖于Eureka注册中心,同时还需要依赖于Ribbon的负载均衡,不过因为Eureka已经集成了Ribbon,所以我们只需要导入Eureka就行了。
1.前置准备
网关通常是作为集群服务的统一入口,将内部服务与外部系统隔离开来,那么我们直接与外界系统交互的应该是消费者,所以我们需要将所有的消费者也注册到Eureka中,
在前面的组件使用中,消费者一直都是从Eureka中获取服务,所有消费者都没有往Eureka中注册自己,在不使用网关时这样是完全没有问题,若是使用网关则必须让所有消费者也要将自己注册到Eureka中。这样Zuul才能拿到消费者的实例,才能实现正确的路由,因此,需要将9001、9002两个服务注册到Eureka中,此外还需要为这两个服务提供统一的应用名,这样在Eureka中才会变成同一个服务的不同实例。
eureka: client: register-with-eureka: true # 向Eureka服务中注册自己 spring: application: name: customer # 服务名称,9001 和 9002 服务都是这个才可以
然后查看Eureka的服务,发现customer有两个结点9001和9002,这样就没问题了。
2.导入jar包
导入Gateway的依赖和Eureka的依赖,虽然也依赖Ribbon,但是Eureka已经集成了Ribbon所以不用导入。
org.springframework.cloud spring-cloud-starter-gateway org.springframework.cloud spring-cloud-starter-netflix-eureka-client
3.配置application.yml
这里的配置主要是三块,第一块是server的配置就一个端口号,第三块是Eureka的配置已经见过太多次了,第二块中的cloud模块中的配置才是这次的核心。
server: port: 6002 spring: application: name: gateway6002 cloud: gateway: discovery: locator: enabled: true # 开启自动映射,开启后会自动根据Eureka的服务名创建路由(同名) lower-case-service-id: true # 支持小写映射 url小写对应到Eureka中的服务名 eureka: client: service-url: defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/ register-with-eureka: false # 网关只需要获取信息,无需向Eureka中注册自己 fetch-registry: true # 从Eureka中获取信息
这里并没有像Zuul那样配置路由和uri,那是怎么实现路由的呢?主要是cloud下面的那两个配置。locator.enabled: true ,这个配置是为了开启自动映射的,开启后我们便不需要为所有服务配置路由,所有服务都会根据Eureka中的服务名默认创建路由,此时可以这么访问,如下:
这里的CUSTOMER就是Eureka中的服务名,此时使用小写的CUSTOMER是访问不了的(验证这个不要使用浏览器);但是都是大写的肯定看着不好看,所以使用这个配置 lower-case-service-id: true,来将默认的路由的uri和path都改成小写,然后我们就可以这么访问了,如下:
4.增加相关注解
事实上只需要完成上面的两步就完成了Gateway的所有开发了,是不是很少,就导入了依赖和配置了几个信息就ok了,连在启动类上加注解都不需要。可以说是很方便了。然后测试下如下图:
可以发现点击两次以后就会变换一个服务器获取信息,初步判断我们实现了正常的路由和负载均衡。那为什么点击两次才会变换一个服务器呢?我们通过网关访问的是9001和9002两个服务器,下面是9001和9002两个服务器的后天输出:
可以看到9001和9002两台服务器上各自调用8001和8002基本都满足负载均衡的默认策略轮询,那为什么使用网关不满足了呢?因为网关的负载均衡管理的是9001和9002,所以才会出现连续两次看到的都是调用8001服务的现象,这其实是第一次调用了9001服务,9001调用了8001,然后轮询到了9002服务,9002服务开始也是调用8001。所以才会连续出现两次8001和连续两次8002.归根结底还是因为网关到消费者的负载均衡与消费者到提供者的负载均衡是没有关系的,他们互不干扰,所以才造成了这个现象。
5.动态路由
上面的例子中使用的默认的路由规则,那么我们要是想要自己定义路由规则该怎么办呢?其实也不难,还是以CUSTOMER为例,只需要将配置文件改成如下形式即可。
server: port: 6002 spring: application: name: gateway6002 cloud: gateway: routes: # 路由标识,可以随便写没有任何要求,不重复就行 - id: customer # ugateway会帮我们去注册中心根据这个customer去获取服务列表 uri: lb://customer predicates: # 断言,当请求路径中有满足下面的路径时就会触发路由规则,然后将uri中的服务的ip和端口代替网关的ip和端口, # 然后继续发起请求,这就是路由规则 - Path=/getInfoByEu/** eureka: client: service-url: defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/ register-with-eureka: false fetch-registry: true
除了该配合文件其他也没有需要变动的了,这里说下uri参数和predicates参数,uri后面可以跟具体的地址,这样就不会有负载均衡了,而是定向路由,这里的配置可以借助Ribbon和Eureka来实现负载均衡的路由规则。predicates这个参数是配置断言,即:当请求地址中满足了path后面的内容才会触发路由,比如访问这个地址:http://localhost:6002/getInfoByEu/,就会触发路由实际访问的是:http://customer/getInfoByEu/这种,这里的customer代表的是不同的服务实例,并不是真正地址。下面展示下测试结果:
6.网关的作用
网关是应用中不可获取的一环,那网关都有哪些作用呢?这里总结几点网关的经典作用:
统一入口
为服务集群统一入口,避免对外直接暴露服务
限流
网关作为服务的第一道入口,完全可以做秒杀场景的限流
鉴权
作为门户,可以对请求方进行统一鉴权
八、Config
Config并不是网飞版的标准组件,它是Springcloud 提供的组件,用以实现配置中心化的一个组件,它也是CS架构的。这里默认看到这里的都是会git的基本使用,能够正常操作git和github的人,下面就开始使用Config了。国内访问github会较慢一些,这里使用gitee码云来演示这个操作,码云不能说和github的使用很像,只能说一模一样了,所以这里就不介绍gitee的使用了,不会的话上网搜个快速入门教程花费个半小时就可以学会。
1.在gitee搭建配置中心
想要将配置文件放在git中(svn其实也行),那肯定需要先搭建一个仓库用以存储配置文件,他的唯一作用就是用来存储配置文件。这里其实就是搭建一个远程仓库,我们使用github或者gitee都是可以的,没有任何区别,建议使用gitee,会比较快一些。
第一步:创建一个仓库
第二步:初始化仓库
第三步:本地拉取仓库信息
这一步需要本地有git bash客户端,如果没有就去装一个吧。找到刚刚创建的仓库,选择https地址(选择ssh需要生成公钥),然后使用克隆就可以将代码当到本地了,然后我们就可以正常的git add --all、git commit -m “”、git push等操作了。
git clone -b master https://gitee.com/studentgo/config.git (克隆这个仓库的主分支) git add -all (新增所有文件) git commit -m “将改变提到本地仓库” (提交改变) git push (将改变从本地仓库提到远程仓库)
2.服务端搭建
config是cs架构,它拥有独立的服务端,所以我们需要搭建一个服务端,这个搭建过程也很简单,也是三步走:加依赖、配yml文件、加启动注解,一起看下吧。
增加依赖
这里没有什么特殊说明的,就是导入config的依赖即可。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.5.4</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> <version>3.0.3</version> </dependency>
配置yml文件
这里只有config中的配置信息是新增的配置信息,uri声明配置仓库的地址,如果仓库不是公开的话,那么username与password则是必须的,事实上没有企业会把自己的项目设成公开的,所以真实项目中这些都是需要的。search-paths,这个配置还是很有用的,在真实项目中不会直接将所有配置文件直接放在根目录下,可能会重新用文件夹包一下,此时就需要使用这个配置了。
server: port: 5001 spring: application: name: config-5001 cloud: config: server: git: uri: https://gitee.com/studentgo/config.git # 这里必须是https的路径 username: studentgo # 个人仓库,不带用户访问不了 password: ************ # 个人仓库,不带密码访问不了 search-paths: /configcenter # 配置‘配置文件’所在位置,configcenter是文件夹
添加启动注解
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication @EnableConfigServer public class Config5001Application { public static void main(String[] args) { SpringApplication.run(Config5001Application.class, args); } }
到这里服务端的搭建就完成了,服务端事实上在真实的项目案例中也就只需要这些东西,若是想让项目都使用config那么还剩下两份工作,一份是将所有的微服务的配置文件放到配置中心,另一份是改造各个微服务的配置文件了。
3.客户端搭建
客户端,就是所有我们准备将配置文件放到配置中心的应用了,这里就先使用消费者来进行操作。我们将服务customer-9002的配置文件放到配置中心来启东,如果正常加载了预期的信息,说明配置中心没有问题。
引入依赖
这里有个依赖需要特别注意:spring-cloud-starter-bootstrap,这个依赖以前是不需要加的,现在我们使用bootstrap作为系统配置文件是需要导入这个依赖的,不然的话并不能正常识别配置信息,这个是和以前版本不同的地方,特别需要注意
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> <version>3.0.3</version> </dependency>
配置bootstrap.yml文件
以前我们都是只配置application.yml即可,这里我们需要配置bootstrap.yml文件,这个文件的所有语法和应用与application.yml并没有什么区别,唯一的区别是bootstrap的配置加载优先级更高,相当于系统配置文件,所以我们可以在这个配置文件里指明使用配置服务中的哪个配置文件作为该项目的配置文件,如下所示:
spring: cloud: config: name: customer-9002 # 表明远程配置文件的名称 profile: dev # 表明使用customer-9002的哪个环境 label: master # 默认就是master uri: http://localhost:5001/ # 配置服务的地址,只需要提供域名和端口即可,其他不用
在仓库中建立customer-9002.yml配置文件
新建配置文件customer-9002.yml,内容如下,然后将新建文件add然后commit再然后push到远程仓库即可。
spring: profiles: active: dev --- # 用于环境分割 spring: profiles: dev application: name: customer server: port: 9002 eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://eureka7001:7001/eureka/,http://eureka7002:7002/eureka/ feign: circuitbreaker: enabled: true --- spring: profiles: test application: name: customer server: port: 9002 eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://eureka7001:7001/eureka/,http://eureka7002:7002/eureka/ feign: circuitbreaker: enabled: true
然后启动客户端、服务端进行测试,发现一切正常如下所示,这样一个单机的配置中心就完成了。这里我们实现配置中心的方式是使用单个文件来实现的,其实我们还可以使用多分支的方式实现多环境的配置,比如建立一个dev分支一个uat分支一个生产分支等,也是可以实现的。
4.集群搭建
使用微服务,就必须用集群,不使用集群的话,在云部署的环境下万一一个节点除了问题,将导致所有服务的不可用,这是致命的。所以即使配置中心并不会有多大并发,我们依然要做成集群,用以支持高可用(HA)。那配置中心的集群该怎么搭建呢?我们还是需要利用Eureka来实现集群的搭建。
服务端引入依赖
单机版的集群我们只需要引入web和conifg-server的依赖即可,但是搭建集群需要依赖Eureka,我们还需要引入Eureka客户端的依赖。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.5.4</version> </dependency>
服务端修改application.yml
这里只需要增加Eureka相应的配置即可,其他不用变化。
server: port: 5001 spring: application: name: config-5001 cloud: config: server: git: uri: https://gitee.com/studentgo/config.git # 这里必须是https的路径,不能是ssh路径 username: studentgo # 个人仓库,不带用户访问不了 password: *********** # 个人仓库,不带密码访问不了 eureka: client: register-with-eureka: true fetch-registry: false # 无需从配置中心获取信息 service-url: defaultZone: http://eureka7001:7001/eureka/,http://eureka7002:7002/eureka/
服务端修改启动类
客户端修改配置文件bootstrap.yml
客户端不能再直接根据uri来获取,需要从Eureka来获取配置服务的地址,所以这里在config下现增了两个配置,如下所示,这里必须要注意的是,若是使用Eureka实现集群化,则Eureka的配置则不能放在远程仓库了,在bootstrap.yml中必须声明Eureka的配置,因为我们配置了从Eureka中加载地址,加载bootstrap时找不到就会出问题,所以必须在bootstrap.yml中声明Eureka的配置信息。
spring: cloud: config: name: customer-9002 profile: dev label: master # 默认就是master # uri: http://localhost:5001/ discovery: enabled: true # 开启从注册中心获取配置服务路径 service-id: config-5001 # 标明获取配置中心哪个服务 eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://eureka7001:7001/eureka/,http://eureka7002:7002/eureka/
这样集群需要做的事情就完成了,9002服务中是一开始讲解feign就建立的,里面必有的feign和Eureka都没有动,这里只是作config客户端时新增了这些配置。使用集群时的效果如下,可以看到整个服务链路都是正常的。
5.config的其他问题
1)配置仓库增加目录层级问题
若是我们不想让配置文件直接放在根目录下,就需要自己在仓库里从新建立文件夹用以存放配置文件,比如下面这样,笔者是在config下面又建立了一个configcenter目录,如果是这样的话,我们就需要额外增加这个配置:
spring: cloud: config: server: git: search-paths: /configcenter # 配置‘配置文件’所在位置,configcenter是文件夹
2)配置仓库增加共享配置文件
其实有很多配置对于所有应用来说都是公用的,这样的配置在每个配置文件都写一遍就会有些多余,这样我们就可以使用一个共享的配置文件,将所有的相同配置放置在这里,其他不同的配置再放在各自的配置文件中,这个共有的配置文件命名应该是application.yml。比如如下这样。这样在加载对应配置文件时,就会先加载这个配置文件。不过这里有一个问题,笔者将这个共有文件移动到根目录时就不生效了,只有在新增的文件夹下才有效,不清楚这个是不是个人操作问题。
九、总结SpringCloud Netflix
认真总结完会发现,所有的组件都需要依赖注册中心,也可以说注册中心是微服务架构的核心,在微服务架构中我们拥有独立服务端的组件有注册中心Eureka、配置中心Config
监控中心Dashboard、服务网关Gateway。这些虽然都是独立的服务端,但是他们也是都需要依赖Eureka的,注册中心就不说了,
1.配置中心的集群化需要依赖Eureka来实现,将配置中心注册进Eureka中,其他服务获取配置信息时是从Eureka中来获取的。
2.监控中心需要依赖Eureka,主要是在jar包的依赖上,不导入Eureka的依赖,启动就会报错。
3.服务网关的话需要从注册中心获取消费者的服务,从而建立路由关系。
所以所有服务端其实都是需要Eureka注册中心来支持的,注册中心若是换成Zookeeper或者Nacos应该也是类似的。
1.业务服务-提供者,需要向Eureka中注册自己
2.业务服务-消费者,需要向Eureka中注册自己并且需要获取注册信息,注册自己是因为,网关服务需要获取消费者信息。
对于其余的Ribbon和Feign。
Ribbon默认集成在Eureka中,所以无需导入,所有的服务都需要导入Eureka。
Feign则是实现远程调用的,他需要依赖Ribbon,所以也是必须导入Eureka的,Ribbon和Feign则更多
应用于消费者,所以这俩属于嵌入式组件。无需提供单独服务。
Springcloud网飞版淘汰只是时间问题,Springcloud阿里巴巴版才是方向,但是作为学习,Springcloud 网飞也是必须要学的,不过学习网飞版只是了解最初Springcloud的整合思想,对各个组建的功能和未来的替代者有个比较。不过即使不用网飞版的组件,使用阿里巴巴的组件,其实思想上都是一致的,新技术都是在老技术上的改良,肯定都会借鉴老技术的优秀的地方,因此作为学习来说是不存在过时一说的,古人云:温故知新。此时再来看下下面这张图就很清晰了,用户从手机、pc等终端访问应用时,先是从网关走,网关通过路由在走到具体的服务消费者,服务消费者通过fein、ribbon、eureka等组件来实现远程调用、负载均衡来调用到服务提供者,同时Hystrix提供的服务熔断、服务降级、服务监控来保证服务的稳定运行。这就是一个基本的流程了。gitee代码路径:https://gitee.com/studentgo/springcloud-netflix.git,可直接当代码下来,不过这部分代码是笔者敲的第二遍,并不是按照上方所示的端口以及服务来写的,但是所使用的组件都是一模一样,只是将服务的范畴划分了一下。