spring提供了一系列工具,帮助人员迅速搭建分布式系统中的公共组件,如:
【配置管理】、【服务发现】、【断路器】、【智能路由】、【微代理】、【控制总线】、【一次性令牌】、【全局锁】、【主节点选举】、【分布式session】、【集群状态】
协调分布式环境中各个系统,为各类服务提佛那个模板性配置。
使用Spring Cloud,开发人员可以搭建实现这些样板的应用,并且在任何分布式环境下都能工作得非常好。
Spring Cloud基于springboot,最适合用于管理springboot创建的各个微服务应用。
要管理,必然有个管理注册中心的服务器,eureka是一个高可用组件,它没有后端缓存,每一个实例注册之后就需要向注册中心发送心跳,在默认情况下erueka server也是一个erueka client,必须制定一个server
微服务就像一个公司,从最初的小公司到大公司拆分出多个子公司,每个公司都有自己独立的业务,员工,各自发展,互不影响。
单体项目中,臃肿的系统,重复的代码,超长的启动时间带给开发人员的只有无限的埋怨。
使用微服务的优势:
1)服务的独立部署
每个服务都是一个独立的项目,可以独立部署,不依赖于其他服务,耦合性第。
2)服务的快速启动
拆分之后服务启动的速度必然比拆分之前快很多,因为依赖的库少了,代码量也少了。
3)更适合敏捷开发
敏捷开发以用户的需求进化为核心,采用迭代、循序渐进的方法进行。服务拆分可以快速发布新版本,修改哪个服务只需要发布对应的服务即可,不用整体重新发布。
4)职责专一,由专门的团队负责专门的服务
业务发展迅速,每个团队可以负责对应的业务线,服务的拆分有利于团队之间的分工。
5)服务可以动态按需扩容
当某个服务的访问量较大时,我们只需要将这个服务扩容即可。
6)代码的复用
每个服务都提供REST API,所有的基础服务都必须抽出来,很多的底层实现都可以以接口方式提供。
使用微服务的劣势:
1)分布式部署,调用的复杂性高、
单体应用的时候,所有的模块之前的调用都是在本地进行的,在微服务中,每个模块都是独立部署的,通过http来进行通信,这当中会产生网络错误,容错问题,调用关系等。
2)独立的数据库,分布式事务的挑战
每个微服务都有自己的数据库,这就是所谓的去中心化的数据管理。这种模式的优点在于不同的服务,可以选择适合自身业务的数据,比如订单服务的可以用Mysql,评论服务的可以用Mongodb等
3)测试难度提升
服务质检通过接口来交互,当接口改变时,对所有的调用方都是有影响的,需要自动化测试。
4)运维难度的提升
常用5大组件
服务发现——Netflix Eureka
客服端负载均衡——Netflix Ribbon
断路器——Netflix Hystrix
服务网关——Netflix Zuul
分布式配置——Spring Cloud Config
Spring Cloud Eureka是Spring Cloud Netflix项目下的服务治理模块。
而Spring Cloud Netflix项目是Spring Cloud的子项目之一,主要内容是对Netflix公司一系列开源产品的包装,它为Spring Boot应用提供了自配置的Netflix OSS整合。通过一些简单的注解,开发者就可以快速的在应用中配置一下常用模块并构建庞大的分布式系统。
它主要提供的模块包括:服务发现(Eureka),断路器(Hystrix),智能路由(Zuul),客户端负载均衡(Ribbon)等。
使用Eureka实现服务治理
服务注册中心:eureka-server,提供服务注册功能
服务提供方:eureka-client,注册服务到服务注册中心
配置:
pom依赖以及配置
启动类注解:@EnableEurekaServer,@EnableEurekaClient
默认情况下改注册中心会将自己作为客户端来尝试注册自己,此处可以禁用
spring.application.name=eureka-server
server.port=1001
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
服务提供方
pring.application.name=eureka-client
server.port=9002
eureka.client.serviceUrl.defaultZone=http://localhost:9001/eureka/
每一个实例注册后要向注册中心发送心跳,注册中心从每个服务提供方接收心跳消息,超市则从注册中心删除。
Ribbon+RestTemplate实现调用服务
在获取RestTemplate的方法上加@LoadBalanced实现默认轮询,要更改成其余均衡策略,则在配置类中声明想要的均衡策略
@Configuration
public class RestConfig {
@Bean //通过RestTemplate来实现调用接口
@LoadBalanced //表示RestTemplate开启了负载均衡
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
//重新创建一个均衡策略,表示不使用默认
@Bean
public IRule getIReule(){ //通过获取一个IRule对象,
return new RandomRule(); //达到的目的,用我们重新选择的随机,替代默认的轮训方式
}
}
消费者Controller
//声明一个接口
// private static final String HTTP_NAME = “http://localhost:8001/product/”;
private static final String HTTP_NAME = “http://MICROSERVICE-PRODUCT”;
@Autowired
private RestTemplate restTemplate;
@Resource
private DiscoveryClient client; //我这个微服务想要被别人去发现
@GetMapping("/consum/find/{id}")
public User findone(@PathVariable(“id”)Integer id){
String url = HTTP_NAME+"/product/findone/"+id;
return (User) restTemplate.getForObject(url,User.class);
}
@GetMapping("/consum/findAll")
public List getAll(){
List users = restTemplate.getForObject(HTTP_NAME + “/product/list”, List.class);
return users;
}
Feign是一个声明式的Web服务客户端,使得编写Web服务客户端变得非常容易,在api层只需要创建一个接口,然后在上面添加注解即可
实际开发中,对服务依赖的调用可能不止一处,往往一个接口会被多处调用,如果使用Ribbon+RestTemplate,就需要在每一个模块都要创建一个配置类,而且,各社区中javaweb也倾向于面向接口编程
通常针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用,所以,Feign在此基础上做了进一步封装,在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),完成对服务提供方的接口绑定
使用Feign是通过接口的方法调用Rest服务,该请求发送给Eureka服务器,通过Feign直接找到服务接口。Feign融合了Ribbon技术,所以也支持负载均衡。
在使用Feign的时候其实就是远程通过调用标注微服务的对应接口(每一个方法上的路径),来获取返回值
Hystrix
在分布式架构中,存在许多的服务单元,一个单元出现故障,容易因依赖关系引发故障的蔓延,所以产生了断路器等服务保护机制;
流程:单元故障——》向调用方返回一个错误响应,释放线程
Spring Cloud Hystrix实现了断路器、线程隔离等一系列服务保护功能;
具备服务降级、服务熔断、线程、信号隔离、请求缓存、请求合并、服务监控等强大功能;
使用:
在消费者启动类上使用@EnableCircuitBreaker注解开启断路器功能;
在service或其他的方法上增加@HystrixCommand(fallbackMethod="")注解指定回调方法;
网关的作用:
为了简化前端的调用逻辑,同时简化内部服务之间调用的复杂度,具体作用就是转发服务,接收并转发所有内部的客户端调用,还拥有权限认证,限流控制等。
api网关,路由,负载均衡等多种作用
Zuul:
使用的是阻塞式的 API,不支持长连接,比如 websockets。
底层是servlet,Zuul处理的是http请求
没有提供异步支持,流控等均由hystrix支持。
依赖包spring-cloud-starter-netflix-zuul。
Gateway:
底层依然是servlet,但使用了webflux,多嵌套了一层框架
依赖spring-boot-starter-webflux和/ spring-cloud-starter-gateway
提供了异步支持,提供了抽象负载均衡,提供了抽象流控,并默认实现了RedisRateLimiter。
相同点:
1、底层都是servlet
2、两者均是web网关,处理的是http请求
不同点:
gateway对比zuul多依赖了spring-webflux,在spring的支持下,功能更强大,内部实现了限流、负载均衡等,扩展性也更强,但同时也限制了仅适合于Spring Cloud套件
zuul则可以扩展至其他微服务框架中,其内部没有实现限流、负载均衡等。
zuul仅支持同步
gateway支持异步。理论上gateway则更适合于提高系统吞吐量(但不一定能有更好的性能),最终性能还需要通过严密的压测来决定
gateway具有更好的扩展性,并且其已经发布了2.0.0的RELESE版本,稳定性也是非常好的
WebFlux 模块的名称是 spring-webflux,名称中的 Flux 来源于 Reactor 中的类 Flux。Spring webflux 有一个全新的非堵塞的函数式 Reactive Web 框架,可以用来构建异步的、非堵塞的、事件驱动的服务,在伸缩性方面表现非常好。使用非阻塞API。 Websockets得到支持,并且由于它与Spring紧密集成,所以将会是一个更好的 开发 体验。
Zuul 1.x,是一个基于阻塞io的API Gateway。Zuul已经发布了Zuul 2.x,基于Netty,也是非阻塞的,支持长连接,但Spring Cloud暂时还没有整合计划。
总结
总的来说,在微服务架构,如果使用了Spring Cloud生态的基础组件,则Spring Cloud Gateway相比而言更加具备优势,单从流式编程+支持异步上就足以让开发者选择它了。
对于小型微服务架构或是复杂架构(不仅包括微服务应用还有其他非Spring Cloud服务节点),zuul也是一个不错的选择。
Zuul是spring cloud中的微服务网关。网关: 是一个网络整体系统中的前置门户入口。请求首先通过网关,进行路径的路由,定位到具体的服务节点上。
Zuul是一个微服务网关,首先是一个微服务。也是会在Eureka注册中心中进行服务的注册和发现。也是一个网关,请求应该通过Zuul来进行路由。
Zuul网关不是必要的。是推荐使用的。
使用Zuul,一般在微服务数量较多(多于10个)的时候推荐使用,对服务的管理有严格要求的时候推荐使用,当微服务权限要求严格的时候推荐使用。
网关有以下几个作用:
统一入口:未全部为服务提供一个唯一的入口,网关起到外部和内部隔离的作用,保障了后台服务的安全性。
鉴权校验:识别每个请求的权限,拒绝不符合要求的请求。
动态路由:动态的将请求路由到不同的后端集群中。
减少客户端与服务端的耦合:服务可以独立发展,通过网关层来做映射。
网关访问方式
通过zuul访问服务的,URL地址默认格式为:http://zuulHostIp:port/要访问的服务名称/服务中的URL
服务名称:properties配置文件中的spring.application.name。
服务的URL:就是对应的服务对外提供的URL路径监听。
网关限流
Zuul网关组件也提供了限流保护。当请求并发达到阀值,自动触发限流保护,返回错误结果。只要提供error错误处理机制即可。
gateWay使用:
入口添加注解@EnableZuulProxy
配置文件指定注册中心地址,服务本身名称,自定义路由映射,统一入口,处理http请求,权限校验(权限校验需要通过实现ZuulFilter进行拦截)
作用:配置管理
简介:SpringCloud Config提供服务器端和客户端。服务器存储后端的默认实现使用git,因此它轻松支持标签版本的配置环境,以及可以访问用于管理内容的各种工具。
这个还是静态的,得配合Spring Cloud Bus实现动态的配置更新,可以轻松的实现分布式项目配置文件统一管理。
服务追踪组件zipkin,Spring Cloud Sleuth集成了zipkin组件。
简介:
Spring Cloud Sleuth 主要功能就是在分布式系统中提供追踪解决方案,并且兼容支持了 zipkin,你只需要在pom文件中引入相应的依赖即可。
分析:
微服务架构上通过业务来划分服务的,通过REST调用,对外暴露一个接口,可能需要很多个服务协同完成这个接口功能。任何一环失败则接口失败。
术语:
Span:基本工作单玉环
Service Provider: 暴露服务的提供方。
Service Consumer:调用远程服务的服务消费方。
EureKa Server: 服务注册中心和服务发现中心。
从核心要素来看:
Spring Cloud 更胜一筹,在开发过程中只要整合Spring Cloud的子项目就可以顺利的完成各种组件的融合,而Dubbo缺需要通过实现各种Filter来做定制,开发成本以及技术难度略高。
Dubbo只是实现了服务治理,而Spring Cloud子项目分别覆盖了微服务架构下的众多部件,而服务治理只是其中的一个方面。Dubbo提供了各种Filter,对于上述中“无”的要素,可以通过扩展Filter来完善。
例如
从协议上看:
Dubbo缺省协议采用单一长连接和NIO异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况
Spring Cloud 使用HTTP协议的REST API
dubbo支持各种通信协议,而且消费方和服务方使用长链接方式交互,通信速度上略胜Spring Cloud,如果对于系统的响应时间有严格要求,长链接更合适。
从服务依赖方式看:
Dubbo服务依赖略重,需要有完善的版本管理机制,但是程序入侵少。
而Spring Cloud通过Json交互,省略了版本管理的问题,但是具体字段含义需要统一管理,自身Rest API方式交互,为跨平台调用奠定了基础。
从组件运行流程看:
每个组件都是需要部署在单独的服务器上,gateway用来接受前端请求、聚合服务,并批量调用后台原子服务。每个service层和单独的DB交互。
所有请求都统一通过 API 网关(Zuul)来访问内部服务。网关接收到请求后,从注册中心(Eureka)获取可用服务。由 Ribbon 进行均衡负载后,分发到后端的具体实例。微服务之间通过 Feign 进行通信处理业务。
业务部署方式相同,都需要前置一个网关来隔绝外部直接调用原子服务的风险。
Dubbo需要自己开发一套API 网关,
而Spring Cloud则可以通过Zuul配置即可完成网关定制。使用方式上Spring Cloud略胜一筹。
springcloud架构说明:
SpringCloud分布式应用微服务系统组件列表:
微服务框架组件:Spring Boot2 + SpringCloud Hoxton.SR8 + SpringCloud Alibaba
Spring Boot Admin: 管理和监控SpringBoot应用程序的微服务健康状态
数据持久化组件:MySql + Druid + MyBatis + MyBatis-Plus
Mycat: 中间件实现数据库读写分离
Seata: 分布式事务管理,跨服务的业务操作保持数据一致性
高性能的key-value缓存数据库:Redis + RedissonClient + RedisTemplate
API接口文档: Swagger2 + knife4j
接口参数校验:spring-boot-starter-validation
Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台
Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性
OpenFeign: 微服务架构下服务之间的调用的解决方案 + Ribbon实现负载均衡/高可用重试机制
Gateway: 微服务路由转发 + 聚合knife4j微服务文档 + 【Gateway+OAuth2+JWT微服务统一认证授权】
Oauth2:SpringSecurity单点登录功能支持多终端认证授权 + RBAC权限框架
验证码:集成滑动验证码【AJ-Captcha】 + 图片验证码【EasyCaptcha】
多租户: 基于Mybatis-Plus【TenantLineInnerInterceptor】插件实现多租户功能
数据权限: 基于Mybatis-Plus【PaginationInnerInterceptor】分页插件实现可配置的数据权限功能
对象存储服务( OSS):MinIO + 阿里云 + 七牛云 + 腾讯云 + 百度云 + 华为云
工作流:Flowable轻量级业务流程引擎
XXL-JOB:分布式任务调度平台,作业调度系统
Ant-design-vue + ElementUI (基础)优秀流行的前端开源框架整合
uni-app: 可发布到iOS、Android、Web(响应式)、以及各种小程序(微信/支付宝/百度/头条/QQ/钉钉/淘宝)、快应用等多个平台 (本框架中主要用于H5、小程序)
Flutter: 给开发者提供简单、高效的方式来构建和部署跨平台、高性能移动应用 (本框架中主要用于移动应用)
EKL: Elasticsearch + Logstash + Kibana分布式日志监控平台
代码生成器: 基于Mybatis-Plus代码生成插件开发的,便捷可配置的代码生成器
Keepalived + Nginx: 高可用 + 高性能的HTTP和反向代理web服务器
DevOps : kubernetes + docker + jenkins 实现持续集成(CI)和持续交付(CD)
数据报表:基于Ant-design-vue + Echarts实现的自定义数据可视化报表
以下是具备eureka注册中心,client服务提供方,gateway网关统一管理接口,feign调用服务的多模块父子工程的springcloud工程详细搭建过程
结构图:
建一个springboot父项目
new Module,
此子项目就是各个服务(eureka,client,gateway…)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
因为是父子工程,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.myself</groupId>
<artifactId>eureka</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka</name>
<description>Demo project for Spring Boot</description>
<packaging>jar</packaging>
<parent>
<groupId>com.myself</groupId>
<artifactId>cloudparent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.5.RELEASE</spring-boot.version>
<spring-cloud.version>Hoxton.SR9</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${
spring-cloud.version}</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>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
${spring-cloud.version},继承父工程的依赖设置
server:
port: 8761
spring:
application:
name: eureka
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
注册中心配置,下面两句是设置是否向注册中心注册自己本身。
如果是单个注册中心,就加上此两句配置
register-with-eureka: false #标明是否向注册中心注册自己
fetch-registry: false #不获取服务的注册信息
@EnableEurekaServer
此时注册中心配置完毕
浏览器输入:localhost:8761即可查看注册中心情况
pom.xml,pom其余配置和注册中心相同,这里服务提供方添加client依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
yml配置文件
application.yml
server:
port: 8081
spring:
application:
name: client #\u670D\u52A1\u5668\u540D\u79F0
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
启动类添加注解
@EnableEurekaClient //开启注册中心客户端的注解
此时一个服务提供方就完成了
pom.xml,pom其余配置和注册中心相同,这里服务提供方添加client依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
application.yml
spring:
application:
name: gateway #\u670D\u52A1\u5668\u540D\u79F0
server:
port: 9000
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
zuul:
prefix: /api
routes:
userserver: /user/**
orderserver: /order/**
goodsserver: /goods/**
启动类添加注解
@EnableEurekaClient // 注册中心客户端
@EnableZuulProxy // 启用网关
pom.xml依赖
<!--客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--SQL依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--使用open feign依赖-->
<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>
application.yml配置
server:
port: 9600
spring:
application:
name: userserver
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/cms?characterEncoding=utf-8&useSSL=false
username: root
password: root
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
启动类上注解:
@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients
OrderServiceFeign用来调用orderserver服务里的方法,注意注解上需要指定被调用服务方的服务名
@FeignClient(value = "orderserver")
public interface OrdersServiceFeign {
/**
* @Author chenpengyu
* @Description 调用订单中的查询订单方法
* @Date 2021/2/4
* @Param
**/
@GetMapping(value = "/orders/selectList",produces = MediaType.APPLICATION_JSON_VALUE)
ReturnMsg findList(@RequestParam("userId")Integer userId);
}
@RestController
@RequestMapping("/orders")
public class OrderController {
@Resource
private OrderService orderService;
@GetMapping(value = "selectList", produces = MediaType.APPLICATION_JSON_VALUE)
public string findList(Integer userId) {
ReturnMsg<Object> returnMsg = ReturnMsg.builder().build();
List<Order> orderList = null;
orderList = orderService.findAllOrderByUserId(userId);
returnMsg.setData(orderList);
return returnMsg;
}
}
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
OrdersServiceFeign ordersServiceFeign;
@GetMapping("findOrder")
public Integer findOrderByUserId(){
ordersServiceFeign.findList(1);
return 1;
}
}
测试时:
1、端口号因为添加了网关,所以此处需要添加前缀/api/user,同时端口号统一使用网关的端口号调用服务。
2、通过userserver中controller的方法直接测试