目录
一、微服务、分布式概念
分布式思想与基本概念
二、SpringCloud 概述
三、SpringCloud 微服务环境搭建 (RestTemplate应用)
1、服务提供者 (8001) 复制粘贴即可。
2、服务消费者(80)
RestTemplate
Hutool工具包
postman 模拟发起请求
热部署Devtools
四、Eureka:服务注册与发现
1、IDEA生成eurekaServer端服务注册中心 (7001)
2、服务提供者8001 注册到7001
3、服务消费者注册到7001
五、Ribbon:负载均衡服务调用★
如何替换策略?
六、OpenFeign:服务接口远程调用★
Ribbon 超时设置
OpenFeign 日志打印功能
微服务、分布式概念、微服务架构注册中心:Eureka负载均衡:Ribbon声明式调用远程方法:OpenFeign熔断、降级、监控:Hystrix网关:Gateway链路跟踪:Sleuth服务注册和配置中心:Spring Cloud Alibaba Nacos熔断、降级、限流:Spring Cloud Alibaba Sentinel
一、微服务、分布式概念
Day85.Dubbo分布式RPC框架、Zookeeper、provider | consumer 开发、管理控制台、事物问题_焰火青年·的博客-CSDN博客
/articles/microservices.html微服务microservices
微服务|YYGCui's blog
什么是微服务?
简单来说,微服务架构风格[1]是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级(restful风格)通信机制(通常用HTTP资源API)。这些服务围绕业务能力构建并且可通过全自动部署机制独立部署。这些服务共用一个最小型的集中式的管理,服务可用不同的语言开发,使用不同的数据存储技术。
总结:
1. 微服务需要根据业务模块拆分,做到单一职责,不要重复开发相同
务。
2. 微服务可以将业务暴露为接口,供其它微服务使用
3. 不同微服务都应该有自己独立的数据库
分布式服务架构
分布式一定是集群的(防止单点故障)。 而集群并不一定就是分布式的。
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。
RPC【Remote Procedure Call】是指远程过程调用,是一种进程间通信方式,他是一种技术的思想,而不是规范。
它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。
即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。
分布式思想与基本概念
高并发
通过设计保证系统可以并行处理很多请求。应对大量流量与请求
Tomcat最多支持并发多少用户?
Tomcat 默认配置的最大请求数是 150,也就是说同时支持 150 个并发,当然了,也可以将其改大。
当某个应用拥有250 个以上并发的时候,应考虑应用服务器的集群。
具体能承载多少并发,需要看硬件的配置,CPU越多性能越高,分配给JVM 的内存越多性能也就越高,但也会加重 GC的负担。
操作系统对于进程中的线程数有一定的限制:
Windows每个进程中的线程数不允许超过 2000
Linux每个进程中的线程数不允许超过 1000
另外,在 Java 中每开启一个线程需要耗用1MB的 JVM 内存空间用于作为线程栈之用。
Tomcat 默认的 HTTP 实现是采用阻塞式的 Socket 通信,每个请求都需要创建一个线程处理。这种模式下的并发量受到线程数的限制,但对于 Tomcat 来说几乎没有 BUG 存在了。
Tomcat 还可以配置 NIO方式的 Socket 通信,在性能上高于阻塞式的,每个请求也不需要创建一个线程进行处理,并发能力比前者高。但没有阻塞式的成熟。
这个并发能力还与应用的逻辑密切相关,如果逻辑很复杂需要大量的计算,那并发能力势必会下降。如果每个请求都含有很多的数据库操作,那么对于数据库的性能也是非常高的。
对于单台数据库服务器来说,允许客户端的连接数量是有限制的。
并发能力问题涉及整个系统架构和业务逻辑。
系统环境不同,Tomcat版本不同、JDK版本不同、以及修改的设定参数不同。并发量的差异还是满大的。
maxThreads="1000" 最大并发数 ,默认值为200minSpareThreads="100"//初始化时创建的线程数,默认值为10acceptCount="700"// 指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理,默认值为100
Apache Tomcat 8 Configuration Reference (8.0.53) - The HTTP Connector
高并发衡量指标
响应时间(RT) 请求做出响应的时间,即一个http请求返回所用的时间吞吐量 系统在单位时间内处理请求的数量QPS(Query/Request Per Second)、 TPS(Transaction Per Second)每秒查询(请求)数、每秒事务数 专业的测试工具:Load RunnerApache abApache JMeter并发用户数 承载的正常使用系统功能的用户的数量
高可用
服务集群部署
数据库主从+双机热备
主-备方式(Active-Standby方式)
主-备方式即指的是一台服务器处于某种业务的激活状态(即Active状态),另一台服务器处于该业务的备用状态(即Standby状态)。
双主机方式(Active-Active方式)
双主机方式即指两种不同业务分别在两台服务器上互为主备状态(即Active-Standby和Standby-Active状态)
注册中心
保存某个服务所在地址等信息,方便调用者实时获取其他服务信息
提供者和消费者是相对而言的
服务注册:服务提供者服务发现:服务消费者
负载均衡
动态将请求派发给比较闲的服务器
策略:
轮询(Round Robin)加权轮询(Weighted Round Robin)随机Random哈希Hash最小连接数LC最短响应时间LRT
服务雪崩
服务之间复杂调用,一个服务不可用,导致整个系统受影响不可用
限流
限制某个服务每秒的调用本服务的频率
API网关
API网关要做很多工作,它作为一个系统的后端总入口,承载着所有服务的组合路由转换等工作,除此之外,我们一般也会把安全,限流,缓存,日志,监控,重试,熔断等放到 API 网关来做。
服务跟踪
追踪服务的调用链,记录整个系统执行请求过程。如:请求响应时间,判断链中的哪些服务属于慢服务(可能存在问题,需要改善)。
弹性云
Elastic Compute Service(ECS)弹性计算服务
动态扩容,压榨服务器闲时能力
例如:双11,618,高峰时多配置些服务器,平时减少多余的服务器配置(用于其他服务应用),避免资源浪费
二、SpringCloud 概述
SpringCloud 是微服务一站式服务解决方案,微服务全家桶。它是微服务开发的主流技术栈。它采用了名称,而非数字版本号。
springCloud 和 springCloud Alibaba 目前是最主流的微服务框架组合。
SpringCloud版本选择
选用 springboot 和 springCloud 版本有约束,不按照它的约束会有冲突。
官方版本的对应信息:https://start.spring.io/actuator/info
SpringBoot2.3.6版和SpringCloud Hoxton.SR9版
SpringCloud Alibaba 2.2.6
三、SpringCloud 微服务环境搭建 (RestTemplate应用)
与springboot搭建基本相同服务提供者(cloud-provider-payment8001)、服务消费者(cloud-consumer-order80)
建module改POM 添加依赖写YML主启动业务类 (orm - dao - service - controller)
父工程xml
<groupId>com.atguigu.springcloud</groupId><artifactId>springcloud</artifactId><version>1.0-SNAPSHOT</version><packaging>pom</packaging><!-- 统一管理jar包版本 --><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><piler.source>1.8</piler.source><piler.target>1.8</piler.target><junit.version>4.12</junit.version><log4j.version>1.2.17</log4j.version><lombok.version>1.16.18</lombok.version><mysql.version>8.0.26</mysql.version><druid.version>1.1.16</druid.version><mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version></properties><!-- 子模块继承之后,提供作用:锁定版本+子modlue不用写groupId和version --><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.3.6.RELEASE</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Hoxton.SR9</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.2.6.RELEASE</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.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.spring.boot.version}</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>${junit.version}</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>${log4j.version}</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version><optional>true</optional></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><fork>true</fork><addResources>true</addResources></configuration></plugin></plugins></build>
1、服务提供者 (8001) 复制粘贴即可。
1. 创建项目cloud-provider-payment8001,引入依赖
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--通用代码提取 提取实体类(省略)--><!--<dependency><groupId>com.atguigu.springcloud</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency>--><!--当前8001作为Eureka的客户端,向7001进行注册--><!--<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>--></dependencies>
2.YML配置文件
server:port: 8001spring:application:name: cloud-payment-servicedatasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/cloud?characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=trueusername: rootpassword: *******mybatis:mapperLocations: classpath:/mapper/*.xmltype-aliases-package: com.atguigu.springcloud.entities
3.主启动类
package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@MapperScan(basePackages = "com.atguigu.springcloud.dao") //扫描dao包,创建代理对象@SpringBootApplicationpublic class PaymentMain8001 {public static void main(String[] args) {SpringApplication.run(PaymentMain8001.class,args);}}
4. 业务类
数据库表
主实体Payment
@Data@AllArgsConstructor@NoArgsConstructorpublic class Payment implements Serializable {private Long id;private String serial;}
Json封装体CommonResult
@Data@AllArgsConstructor@NoArgsConstructorpublic class CommonResult <T> implements Serializable{private Integer code;private String message;private T data;public CommonResult(Integer code,String message){this(code,message,null);//如果这行报错,请安装lombok插件}}
接口PaymentDao
@Component //代替@Repository声明bean@Mapper//mybatis提供的,等价:@MapperScan("com.atguigu.springcloud.dao")//@Repository//spring提供的。在此,只是为了声明bean对象public interface PaymentDao {public int create(Payment payment); public Payment getPaymentById(@Param("id") Long id);}
映射
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-////DTD Mapper 3.0//EN" "/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.atguigu.springcloud.dao.PaymentDao"><insert id="create" useGeneratedKeys="true" keyProperty="id">insert into payment(serial) values(#{serial});</insert><resultMap id="BaseResultMap" type="com.atguigu.springcloud.entities.Payment"><id column="id" property="id" jdbcType="BIGINT"></id><result column="serial" property="serial" jdbcType="VARCHAR"></result></resultMap><select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">select * from payment where id=#{id}</select></mapper>
Service 接口与实现
public interface PaymentService {public int create(Payment payment); //写public Payment getPaymentById(Long id); //读取}@Service@Transactionalpublic class PaymentServiceImpl implements PaymentService {@AutowiredPaymentDao paymentDao;@Overridepublic int create(Payment payment) {return paymentDao.create(payment);}@Overridepublic Payment getPaymentById(Long id) {return paymentDao.getPaymentById(id);}}
Controller
@RestController@Slf4jpublic class PaymentController {@Resourceprivate PaymentService paymentService;@PostMapping(value = "/payment/create")public CommonResult<Payment> create(Payment payment){ //埋雷int result = paymentService.create(payment);log.info("*****插入结果:"+result);if (result>0){ //成功return new CommonResult(200,"插入数据库成功",result);}else {return new CommonResult(444,"插入数据库失败",null);}}@GetMapping(value = "/payment/get/{id}")public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){Payment payment = paymentService.getPaymentById(id);log.info("*****查询结果:"+payment);if (payment!=null){ //说明有数据,能查询成功return new CommonResult(200,"查询成功",payment);}else {return new CommonResult(444,"没有对应记录,查询ID:"+id,null);}}}
2、服务消费者(80)
1. 创建项目cloud-consumer-order80,引入依赖
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!--热部署--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--通用代码提取 提取实体类(省略)--><!--<dependency><groupId>com.atguigu.springcloud</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency>--><!--当前8001作为Eureka的客户端,向7001进行注册--><!--<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>--></dependencies>
2. YML文件
server:port: 80spring:application:name: cloud-consumer-order80
3. 主启动
@SpringBootApplication//@EnableEurekaClient//声明当前应用80为Eureka(7001)的客户端//在哪个请求上,采用哪种规则//@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class)public class OrderMain80 {public static void main(String[] args) {SpringApplication.run(OrderMain80.class,args);}}
4. 业务类
创建entities (将cloud-provider-payment8001工程下的entities包下的两个实体类复制过来)
RestTemplate
由于没有引入OpenFeign,所以暂时使用RestTemplate 类远程调用
RestTemplate提供了多种便捷访问远程Http服务的方法,是一种简单便捷的访问Restful服务模板类,是Spring 提供的用于访问Rest服务的客户端模板工具集
官网地址: RestTemplate (Spring Framework 5.2.2.RELEASE API)
使用RestTemplate访问Restful接口非常的简单粗暴无脑。(url,requestMap,ResponseBean.class)这三个参数分别代表REST请求地址、请求参数、Http响应转换被转换成的对象类型。
缺点:面向模板类编程,不是面向接口编程
config配置类
@SpringBootConfiguration//声明一个配置类public class ApplicationContextConfig {//重点 模板类//@RestTemplate 只能进行远程调用@Bean//@LoadBalanced //RestTemplate + Ribbon 实现远程调用和负载均衡(轮询)public RestTemplate restTemplate(){return new RestTemplate();}}
创建controller
@RestControllerpublic class OrderController {//基于 RestTemplate 模板类进行远程调用@AutowiredRestTemplate restTemplate;public static final String PATH = "http://localhost:8001";@RequestMapping("/consumer/payment/get/{id}")public CommonResult get(@PathVariable("id") Long id){CommonResult<Payment> commonResult = restTemplate.getForObject(PATH + "/payment/get/" + id, CommonResult.class);System.out.println("commonResult = " + commonResult);return commonResult;}@RequestMapping("/consumer/payment/create")public CommonResult create(@RequestBody Payment payment) {System.out.println("payment = " + payment);CommonResult commonResult = restTemplate.postForObject(PATH + "/payment/create", payment, CommonResult.class);return commonResult;}/* //面向接口编程:推荐 使用OpenFeign组件@AutowiredPaymentService paymentService; //注入代理对象@RequestMapping("/consumer/payment/get/{id}")public CommonResult get(@PathVariable("id") Long id){Payment payment = paymentService.getPaymentById(id);System.out.println("payment = " + payment);return new CommonResult(200,"操作成功",payment);}@RequestMapping("/consumer/payment/create")public CommonResult create(@RequestBody Payment payment){System.out.println("payment = " + payment);paymentService.create(payment);return new CommonResult(200,"操作成功");}*/}
测试
先启动cloud-provider-payment8001再启动cloud-consumer-order80http://localhost/consumer/payment/get/32不要忘记@RequestBody注解服务提供者接口方法需要增加@RequestBody注解(踩雷or破雷);否则,接收不到数据。
Hutool工具包
封装实体类代码 (在弄一个工具项目,通过Maven继承传递。略)
糊涂工具包:Hutool — 🍬A set of tools that keep Java sweet.
<!--糊涂工具包:几个热爱编程的人开发的一些工具类 打包开源免费试用--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.1.0</version></dependency>
postman 模拟发起请求
热部署Devtools
1. 子工程添加依赖、插件
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency>
2. 父工程添加插件
下一段配置黏贴到父工程当中的pom里<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><fork>true</fork><addResources>true</addResources></configuration></plugin></plugins></build>
3.Enabling automatic build
4. Ctrl+Shift+Alt+/选择Registry…,重启idea
compiler.automake.allow.when.app.running -> 自动编译
compile.document.save.trigger.delay -> 自动更新文件;它主要是针对静态文件如JS CSS的更新,将延迟时间减少后,直接按F5刷新页面就能看到效果!
热部署:idea找不到 Registry中没有找到compiler.automake.allow.when.app.running选项?
四、Eureka:服务注册与发现
SpringCloud封装了Netflix公司开发的Eureka模块来实现服务治理。
在传统的RPC远程调用框架中,管理每个服务与服务之间依赖关系比较复杂、所以需要进行服务治理,管理服务与服务之间依赖关联,以实现服务调用,负载均衡、容错等,实现服务发现与注册。
Eureka Client通过注册中心进行访问
是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、默认使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会在Eureka Server发送心跳(默认周期30秒,zookeeper为10秒)。如果Eureka Server在多个心跳周期内没有收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移出(默认90秒)
Eureka两组件
Eureka Server提供服务注册服务
各个微服务节点通过配置启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
Eureka Client通过注册中心进行访问
是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会在Eureka Server发送心跳(默认周期30秒)。如果Eureka Server在多个心跳周期内没有收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移出(默认90秒)
1、IDEA生成eurekaServer端服务注册中心 (7001)
1.新建项目 cloud-eureka-server7001,导入依赖
<dependencies><!--Eureka服务注册中心--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency><dependency><groupId>com.atguigu.springcloud</groupId><artifactId>cloud-api-commons</artifactId><version>${project.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId></dependency></dependencies>
2. 配置application.yml 文件
server:port: 7001eureka:instance:hostname: localhostclient:register-with-eureka: falsefetchRegistry: falseservice-url:defaultZone: http://localhost:7001/eureka
3. 启动类,添加@EnableEurekaServer注解
@SpringBootApplication@EnableEurekaServer //声明当前应用为Eureka服务器端public class App {public static void main(String[] args) {SpringApplication.run(App.class,args);}
4. 测试访问http://localhost:7001/
2、服务提供者8001 注册到7001
1. 修改项目 cloud-provider-payment8001,导入依赖
<!--当前8001作为Eureka的客户端,向7001进行注册--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>
2. 修改 YML配置文件
#8001 向 7001进行注册eureka:client:register-with-eureka: truefetchRegistry: trueservice-url:defaultZone: http://localhost:7001/eureka
3.主启动 加入@EnableEurekaClient注解
@SpringBootApplication@MapperScan(basePackages = "com.atguigu.springcloud.dao") //扫描dao包,创建代理对象@EnableEurekaClient //声明当前应用8001为Eureka(7001)的客户端public class PaymentMain8001 {public static void main(String[] args) {SpringApplication.run(PaymentMain8001.class,args);}}
3、服务消费者注册到7001
1. 新建Module:cloud-consumer-order80,导入依赖
<!-- !!!!向7001进行注册--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>
2. 添加YML配置文件
eureka:client:register-with-eureka: truefetchRegistry: trueservice-url:defaultZone: http://localhost:7001/eureka
3.主启动类 加入@EnableEurekaClient注解
@SpringBootApplication@EnableEurekaClient//声明当前应用80为Eureka(7001)的客户端//在哪个请求上,采用哪种规则(ribbon)//@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class)public class OrderMain80 {public static void main(String[] args) {SpringApplication.run(OrderMain80.class,args);}}
测试
http://localhost/consumer/payment/get/31
测试8001服务和80服务效果一样
五、Ribbon:负载均衡服务调用★
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。
Ribbon客户端组件提供一系列完善的配置项,如:连接超时,重试等。
简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。
Ribbon目前也进入维护模式,未来替换方案:Spring Cloud LoadBalancer
注:Feign、Nacos 默认集成了 Ribbon
Ribbon的本地负载均衡客户端VSNginx服务端负载均衡区别: Nginx是服务器负载均衡,客户端所有请求都会交给Nginx,然后,由nginx实现转发请求。即负载均衡是由服务器端完成的。Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用。集中式LB 即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5,也可以是软件,如Nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方;进程内LB 将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。Ribbon=负载均衡+RestTemplate调用
1. 引入依赖
<!--ribbon包无需手动导入,因为eureka-client中已经间接引入了--><!--<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-ribbon</artifactId></dependency>-->
2. 服务消费者80 配置类中 模板对象加入注解@LoadBalanced
@SpringBootConfiguration//声明一个配置类public class ApplicationContextConfig {//重点 模板类//RestTemplate 只能进行远程调用@Bean@LoadBalanced //RestTemplate + Ribbon 实现远程调用和负载均衡(轮询)public RestTemplate restTemplate(){return new RestTemplate();}}
3. 修改地址
//public static final String PATH = "http://localhost:8001";//修改接收请求地址: 从7001订阅服务,获取三个实例 public static final String PATH = "http://CLOUD-PAYMENT-SERVICE";@RequestMapping("/consumer/payment/get/{id}")public CommonResult get(@PathVariable("id") Long id){CommonResult<Payment> commonResult = restTemplate.getForObject(PATH + "/payment/get/" + id, CommonResult.class);System.out.println("commonResult = " + commonResult);return commonResult;}
4. 构建支付服务提供者集群环境
启动三个服务,设置不同端口。测试轮询效果
如何替换策略?
Ribbon 底层使用了 Irule 接口,实现各种负载均衡策略
IRule:根据特定算法从服务列表中选取一个要访问的服务
flix.loadbalancer.RoundRobinRule轮询,默认策略。flix.loadbalancer.RandomRule随机flix.loadbalancer.RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务WeightedResponseTimeRule 对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例ZoneAvoidanceRule 默认规则,复合判断server所在区域的性能和server的可用性选择服务器
自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化订制的目的了。
1. 在启动类上一级新建MySelfRule规则类
@Configurationpublic class MySelfRule {@Beanpublic IRule myRule(){return new RandomRule();//定义为随机}}
2. 主启动类添加@RibbonClient
@SpringBootApplication@EnableEurekaClient//声明当前应用80为Eureka(7001)的客户端//在哪个请求上,采用哪种规则@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class)public class OrderMain80 {public static void main(String[] args) {SpringApplication.run(OrderMain80.class,args);}}
六、OpenFeign:服务接口远程调用★
Feign是一个声明式的web服务客户端,让编写web服务客户端变得非常容易,只需创建一个接口并在接口上添加注解即可。(上面的例子使用了RestTemplate)
SpringCloud 对Feign 进行了封装,使其支持了SpringMVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。
接口定义:狭义:interface声明的类 广义:对外暴露的类、方法也可称为接口
Feign 集成了 Ribbon
利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用。
Feign和OpenFeign两者区别
使用方式:微服务调用接口+@FeignClient
1.新建Module:cloud-consumer-feign-order80,导入依赖
<dependencies><!--openfeign--><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-client</artifactId></dependency><dependency><groupId>com.atguigu.springcloud</groupId><artifactId>cloud-api-commons</artifactId><version>${project.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>
2. YML文件
server:port: 80spring:application:name: cloud-consumer-feign-order80eureka:client:register-with-eureka: truefetch-registry: trueservice-url:defaultZone: http://localhost:7001/eureka
3. 主启动类 添加注解@EnableFeignClients
@SpringBootApplication@EnableEurekaClient //声明当前应用80为Eureka(7001)的客户端@EnableFeignClients //启用OpenFeign组件远程调用功能public class App {public static void main(String[] args) {SpringApplication.run(App.class);}}
4. 业务层 创建接口并新增注解 @FeignClient
//远程调用接口:@Component //声明bean对象,创建代理对象,进行远程调用@FeignClient("CLOUD-PAYMENT-SERVICE") //指定调用微服务的名称public interface PaymentFeignService {//接口方法声明要求:需要与被调用的服务的controller方法声明保持一致@GetMapping(value = "/payment/get/{id}")public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);@PostMapping(value = "/payment/create")public CommonResult<Payment> create(@RequestBody Payment payment);}
4. 控制层Controller
@RestControllerpublic class OrderFeignController {//远程调用微服务接口(默认集成Ribbon 轮询),实现原原理: 代理@AutowiredPaymentFeignService paymentFeignService; @RequestMapping("/consumer/payment/get/{id}")public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){CommonResult<Payment> commonResult = paymentFeignService.getPaymentById(id);return commonResult;}}
5. 测试
Ribbon 超时设置
OpenFeign远程调用 默认一秒超时,有时候我们需要设置Feign客户端的超时控制,即Ribbon的超时时间,因为Feign集成了Ribbon进行负载均衡。
如果不配置ribbon的重试次数,默认会重试一次。
注意:
默认情况下,GET方式请求无论是连接异常还是读取异常,都会进行重试
非GET方式请求,只有连接异常时,才会进行重试
如果对增删改进行重试,应通过乐观锁机制,进行幂等处理,防止重复提交
1、修改yml配置文件
ribbon:ReadTimeout: 3000ConnectTimeout: 3000MaxAutoRetries: 1 #同一台实例最大重试次数,不包括首次调用MaxAutoRetriesNextServer: 1 #重试负载均衡其他的实例最大重试次数,不包括首次调用OkToRetryOnAllOperations: false #是否所有操作都重试(false时只对get重试)#hystrix(降级熔断)的超时时间hystrix:command:default:execution:timeout:enabled: trueisolation:thread:timeoutInMilliseconds: 9000
一般情况下 都是 ribbon 的超时时间(<)hystrix的超时时间(因为涉及到ribbon的重试机制)
OpenFeign 日志打印功能
日志级别
NONE:默认的,不显示任何日志
BASIC:仅记录请求方法、RUL、响应状态码及执行时间
HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息
FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据
1. 配置类,配置日志bean,指定日志级别
@Configuration //用哪个都可以,一个SpringBoot提供,一个spring提供//@SpringBootConfigurationpublic class FeignConfig {@Beanpublic Logger.Level feignLoggerLevel(){return Logger.Level.FULL;}}
级别:
2. YML配置需要开启日志的Feign客户端
logging:level:com.atguigu.springcloud.service.PaymentFeignService: debug
如果觉得《Day97.SpringCloud:概述 Eureka(注册发现) Ribbon(负载均衡与调用) OpenFeign(远程调用)》对你有帮助,请点赞、收藏,并留下你的观点哦!