一、Nacos
1、基本概念
(1)Nacos 是阿里巴巴推出来的一个新开源项目,是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。
(2)常见的注册中心:
- Eureka(原生,2.0遇到性能瓶颈,停止维护)
- Zookeeper(支持,专业的独立产品。例如:dubbo)
- Consul(原生,GO语言开发)
- Nacos
相对于 Spring Cloud Eureka 来说,Nacos 更强大。Nacos = Spring Cloud Eureka + Spring Cloud Config
Nacos 可以与 Spring, Spring Boot, Spring Cloud 集成,并能代替 Spring Cloud Eureka, Spring Cloud Config
- 通过 Nacos Server 和 spring-cloud-starter-alibaba-nacos-discovery 实现服务的注册与发现。
(3)Nacos是以服务为主要服务对象的中间件,Nacos支持所有主流的服务发现、配置和管理。
Nacos主要提供以下四大功能:
- 服务发现和服务健康监测
- 动态配置服务
- 动态DNS服务
- 服务及其元数据管理
(4)Nacos结构图
- nacos注册中心
- consumer消费者(调用方法的)
- provider生产者(提供方法的)
- 比如edu调用vod里的方法,edu就是消费者,vod就是生产者
(5)Nacos作用之一是实现微服务之间的调用,比如有edu服务和vod服务,要通过edu调用vod里的方法,按照微服务做法就要用到Nacos。
2、Nacos下载和安装
下载地址:https://github.com/alibaba/nacos/releases
下载版本:nacos-server-1.1.4.tar.gz或nacos-server-1.1.4.zip,解压任意目录即可(不要选择beat版本,公测版本,不推荐上线生成)
解压后, Windows里找到bin文件里的startup.cmd双击启动。
(2)启动nacos服务
如果启动失败,失败的原因之一是没有配置jdk环境,参考地址:https://www.cnblogs.com/konglxblog/p/14909489.html进行配置。
Linux/Unix/Mac
- 启动命令(standalone代表着单机模式运行,非集群模式)
- 启动命令:sh startup.sh -m standalone
Windows
- 启动命令:cmd startup.cmd 或者双击startup.cmd运行文件。
启动成功如下图:
访问:http://localhost:8848/nacos(固定地址,在浏览器直接输入即可)
用户名密码:nacos/nacos
二、服务注册
1、在service模块配置pom
<!--服务注册--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
<?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> <modules> <module>service</module> <module>common</module> </modules> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.stu</groupId> <artifactId>guli_parent</artifactId> <packaging>pom</packaging> <version>0.0.1-SNAPSHOT</version> <name>guli_parent</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <guli.version>0.0.1-SNAPSHOT</guli.version> <mybatis-plus.version>3.0.5</mybatis-plus.version> <velocity.version>2.0</velocity.version> <swagger.version>2.7.0</swagger.version> <aliyun.oss.version>2.8.3</aliyun.oss.version> <jodatime.version>2.10.1</jodatime.version> <poi.version>3.17</poi.version> <commons-fileupload.version>1.3.1</commons-fileupload.version> <commons-io.version>2.6</commons-io.version> <httpclient.version>4.5.1</httpclient.version> <jwt.version>0.7.0</jwt.version> <aliyun-java-sdk-core.version>4.3.3</aliyun-java-sdk-core.version> <aliyun-sdk-oss.version>3.1.0</aliyun-sdk-oss.version> <aliyun-java-sdk-vod.version>2.15.5</aliyun-java-sdk-vod.version> <aliyun-java-vod-upload.version>1.4.11</aliyun-java-vod-upload.version> <aliyun-sdk-vod-upload.version>1.4.11</aliyun-sdk-vod-upload.version> <fastjson.version>1.2.28</fastjson.version> <gson.version>2.8.2</gson.version> <json.version>20170516</json.version> <commons-dbutils.version>1.7</commons-dbutils.version> <canal.client.version>1.1.0</canal.client.version> <docker.image.prefix>zx</docker.image.prefix> <cloud-alibaba.version>0.2.2.RELEASE</cloud-alibaba.version> </properties> <dependencyManagement> <dependencies> <!--Spring Cloud--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!--mybatis-plus 持久层--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis-plus.version}</version> </dependency> <!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 --> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>${velocity.version}</version> </dependency> <!--swagger--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>${swagger.version}</version> </dependency> <!--swagger ui--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>${swagger.version}</version> </dependency> <!--aliyunOSS--> <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>${aliyun.oss.version}</version> </dependency> <!--日期时间工具--> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> <version>${jodatime.version}</version> </dependency> <!--xls--> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>${poi.version}</version> </dependency> <!--xlsx--> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>${poi.version}</version> </dependency> <!--文件上传--> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>${commons-fileupload.version}</version> </dependency> <!--commons-io--> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>${commons-io.version}</version> </dependency> <!--httpclient--> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>${httpclient.version}</version> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>${gson.version}</version> </dependency> <!-- JWT --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>${jwt.version}</version> </dependency> <!--aliyun--> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> <version>${aliyun-java-sdk-core.version}</version> </dependency> <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>${aliyun-sdk-oss.version}</version> </dependency> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-vod</artifactId> <version>${aliyun-java-sdk-vod.version}</version> </dependency> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-vod-upload</artifactId> <version>${aliyun-java-vod-upload.version}</version> </dependency> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-sdk-vod-upload</artifactId> <version>${aliyun-sdk-vod-upload.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency> <dependency> <groupId>org.json</groupId> <artifactId>json</artifactId> <version>${json.version}</version> </dependency> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>${commons-dbutils.version}</version> </dependency> <dependency> <groupId>com.alibaba.otter</groupId> <artifactId>canal.client</artifactId> <version>${canal.client.version}</version> </dependency> </dependencies> </dependencyManagement> <!-- <build>--> <!-- <plugins>--> <!-- <plugin>--> <!-- <groupId>org.springframework.boot</groupId>--> <!-- <artifactId>spring-boot-maven-plugin</artifactId>--> <!-- </plugin>--> <!-- </plugins>--> <!-- </build>--> </project>
2、添加服务配置信息
# nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
3、添加Nacos客户端注解
@EnableDiscoveryClient
例子:
package com.stu.eduservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication @EnableDiscoveryClient @ComponentScan(basePackages = {"com.stu"}) public class EduApplication { public static void main(String[] args) { SpringApplication.run(EduApplication.class,args); } }
4、启动客户端微服务

三、服务调用-Feign
基本概念
- Feign是Netflix开发的声明式、模板化的HTTP客户端, Feign可以帮助我们更快捷、优雅地调用HTTP API。
- Feign支持多种注解,例如Feign自带的注解或者JAX-RS注解等。
- Spring Cloud对Feign进行了增强,使Feign支持了Spring MVC注解,并整合了Ribbon和Eureka,从而让Feign的使用更加方便。
- Spring Cloud Feign是基于Netflix feign实现,整合了Spring Cloud Ribbon和Spring Cloud Hystrix,除了提供这两者的强大功能外,还提供了一种声明式的Web服务客户端定义的方式。
- Spring Cloud Feign帮助我们定义和实现依赖服务接口的定义。在Spring Cloud feign的实现下,只需要创建一个接口并用注解方式配置它,即可完成服务提供方的接口绑定,简化了在使用Spring Cloud Ribbon时自行封装服务调用客户端的开发量。
1、需求
2、在service模块添加pom依赖
<!--服务调用--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
3、在调用端的启动类添加注解(@EnableFeignClients)
package com.stu.eduservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication @EnableDiscoveryClient//nacos注册 @EnableFeignClients //服务调用-Feign(调用端加这个注解,就是edu调用vod) @ComponentScan(basePackages = {"com.stu"}) public class EduApplication { public static void main(String[] args) { SpringApplication.run(EduApplication.class,args); } }
4、创建包和接口
注意这个接口是创建在edu层的,也就是调用端(edu调用vod里的方法)
package com.stu.eduservice.client; import com.stu.commonutils.ResultData; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient("service-vod")//这个是vod属性文件里的服务名,即spring.application.name=service-vod @Component public interface VodClient { //根据id删除阿里云视频(映射路径是Vod业务里controller层的类映射+方法上的映射,即 // @RequestMapping("/eduvod/video") + @DeleteMapping("removeVideo/{id}")) /* @FeignClient注解用于指定从哪个服务中调用功能 ,名称与被调用的服务名保持一致。 @GetMapping注解用于对被调用的微服务进行地址映射。 @PathVariable注解一定要指定参数名称,否则出错 @Component注解防止,在其他位置注入VodClient时idea报错*/ @DeleteMapping("/eduvod/video/removeVideo/{id}") public ResultData removeAlyVideo(@PathVariable("id") String id); }
5、调用微服务
package com.stu.eduservice.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.stu.eduservice.client.VodClient; import com.stu.eduservice.entity.EduVideo; import com.stu.eduservice.mapper.EduVideoMapper; import com.stu.eduservice.service.IEduVideoService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * <p> * 课程视频 服务实现类 * </p> * * @author stu * @since 2021-05-29 */ @Service public class EduVideoServiceImpl extends ServiceImpl<EduVideoMapper, EduVideo> implements IEduVideoService { @Autowired private VodClient codClient; @Override public boolean removeVodAndVideoByCourseId(String id) { //根据小节id获取视频id,调用方法实现删除 EduVideo eduVideo = baseMapper.selectById(id); //取得视频id String videoSourceId = eduVideo.getVideoSourceId(); //根据视频id删除阿里云视频 if(!StringUtils.isEmpty(videoSourceId)){ codClient.removeAlyVideo(videoSourceId); } //先删除视频在删除小节,因为删除视频要先根据小节查出视频id在删除视频 baseMapper.deleteById(id); return true; } }
四、Hystrix基本概念
1、Spring Cloud调用接口过程
Spring Cloud 在接口调用上,大致会经过如下几个组件配合:
Feign
----->Hystrix
—>Ribbon
—>Http Client
(apache http components 或者 Okhttp)
具体交互流程上,如下图所示:

@FeignClient
注解修饰的接口时,在框架内部,将请求转换成Feign的请求实例
feign.Request
,交由Feign框架处理(在消费者指定调用的生产者的服务名称和接口地址,即在edu指定要调用的vod的服务名称和接口地址)。
(2)Feign :转化请求Feign是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求,封装了Http调用流程(Feign找到调用的服务名称,根据服务名称找到接口地址)。
(3)Hystrix:熔断处理机制 Feign的调用关系,会被Hystrix代理拦截,对每一个Feign调用请求,Hystrix都会将其包装成HystrixCommand
,参与Hystrix的流控和熔断规则。如果请求判断需要熔断,则Hystrix直接熔断,抛出异常或者使用FallbackFactory
返回熔断Fallback
结果;如果通过,则将调用请求传递给Ribbon
组件(edu服务调用vod服务,假如vod服务突然不好用了,Hystrix就不让他继续调用,断开调用的连接)。
(4)Ribbon:服务地址选择 当请求传递到Ribbon
之后,Ribbon
会根据自身维护的服务列表,根据服务的服务质量,如平均响应时间,Load等,结合特定的规则,从列表中挑选合适的服务实例,选择好机器之后,然后将机器实例的信息请求传递给Http Client
客户端,HttpClient
客户端来执行真正的Http接口调用;
(5)HttpClient :Http客户端,真正执行Http调用根据上层Ribbon
传递过来的请求,已经指定了服务地址,则HttpClient开始执行真正的Http请求
2、Hystrix概念
Hystrix 是一个供分布式系统使用,提供延迟和容错功能,保证复杂的分布系统在面临不可避免的失败时,仍能有其弹性。
比如系统中有很多服务,当某些服务不稳定的时候,使用这些服务的用户线程将会阻塞,如果没有隔离机制,系统随时就有可能会挂掉,从而带来很大的风险。SpringCloud使用Hystrix组件提供断路器、资源隔离与自我修复功能。下图表示服务B触发了断路器,阻止了级联失败

五、feign结合Hystrix使用
改造service-edu模块
1、在service的pom中添加依赖(红色加粗部分)
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>guli_parent</artifactId> <groupId>com.stu</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <groupId>com.stu</groupId> <artifactId>service</artifactId> <packaging>pom</packaging> <version>0.0.1-SNAPSHOT</version> <modules> <module>service-edu</module> <module>service-oss</module> <module>service-vod</module> </modules> <dependencies> <dependency> <groupId>com.stu</groupId> <artifactId>service-base</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> <!--hystrix依赖,主要是用 @HystrixCommand --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <!--服务注册--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--服务调用--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--mybatis-plus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.10</version> </dependency> <!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 --> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> </dependency> <!--swagger--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> </dependency> <!--lombok用来简化实体类:需要安装lombok插件--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--xls--> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> </dependency> <!--httpclient--> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> <!--commons-io--> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> </dependency> <!--gson--> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build> </project>
2、在配置文件中添加hystrix配置(红色加粗部分)
# 服务端口
server.port=8001
# 服务名
spring.application.name=service-edu
# 环境设置:dev、test、prod
spring.profiles.active=dev
# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/zxjy?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
#配置mapper xml文件的路径
mybatis-plus.mapper-locations=classpath:com/stu/eduservice/mapper/xml/*.xml
# nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
#开启熔断机制 feign.hystrix.enabled=true # 设置hystrix超时时间,默认1000ms hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=6000
# 设置日志级别
#logging.level.root=info
#mybatis日志
#mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
3、在service-edu的client包里面修改接口并创建熔断器实现类
package com.stu.eduservice.client; import com.stu.commonutils.ResultData; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; import java.util.List; //这个是vod属性文件里的服务名,即spring.application.name=service-vod, //fallback是实现类的class @FeignClient(name = "service-vod",fallback = VodFileDegradeFeignClient.class) @Component public interface VodClient { //根据id删除阿里云视频(映射路径是Vod业务里controller层的类映射+方法上的映射,即 // @RequestMapping("/eduvod/video") + @DeleteMapping("removeVideo/{id}")) /* @FeignClient注解用于指定从哪个服务中调用功能 ,名称与被调用的服务名保持一致。 @GetMapping注解用于对被调用的微服务进行地址映射。 @PathVariable注解一定要指定参数名称,否则出错 @Component注解防止,在其他位置注入CodClient时idea报错*/ @DeleteMapping("/eduvod/video/removeVideo/{id}") public ResultData removeAlyVideo(@PathVariable("id") String id); //根据id批量删除阿里云视频 @DeleteMapping("eduvod/video/removeVideoBatch") public ResultData removeVideoBatch(@RequestParam("videoIdList") List<String> videoIdList); }
实现类
package com.stu.eduservice.client; import com.stu.commonutils.ResultData; import org.springframework.stereotype.Component; import java.util.List; /*熔断器的实现类*/ @Component public class VodFileDegradeFeignClient implements VodClient { //如果删除接口报错,就会执行这里面的方面,比如删除视频的时候,只启动edu服务,断掉vod服务,就会执行这里的方法 @Override public ResultData removeAlyVideo(String id) { return ResultData.error().message("删除视频出错了"); } @Override public ResultData removeVideoBatch(List<String> videoIdList) { return ResultData.error().message("批量删除视频出错了"); } }