是的,你的行为实在不会给我什么好印象,你是个不折不扣的混蛋!米彩直言不讳到
微服务与分布式、集群“是吗... 可是你不知道,你眼中那个坏事做尽的混蛋,曾经心里也有过一座干净的城池!”
"我不太懂你在说些什么。"
“你不需要懂... ...因为今天这座城池已经从我的身边脱离,成为了一座我永远也触摸不到的天 空之城!”我低头摸索着口袋,却已经没有一支烟供我燃烧掉惆怅,缝补我碎裂的灵魂。
-------《我的26岁女房客》
1.微服务理论
独立应用变成一套小服务,每一个都运行在自己的进程内(容器) 独立业务能力 独立部署 独立开发语言 独立的数据存储
应该有一个能管理这些服务的中心
2.分布式概念
分布式系统是若干独立计算机的集合(多台服务器),这些计算机对于用户来说就像单个相关系统
集群:集群指的是将几台服务器集中在一起,实现同一业务。
集群就是很多服务器跑同一个项目
分布式与集群
== 分布式中的每一个节点 ==,都可以做集群。而集群并不一定就是分布式的。
服务之间的交互可以用两种方式
-
RPC(Dubbo)
- Socket包+自定义序列化
-
RestAPI (SpringCloud)
- HTTP协议+JSON
RPC是什么
RPC【Remote Procedure Call】是指远程过程调用,是一种进程间通信方式,他是一种技术的思想,而不是规范,dubbo是其实现
网络传输要求二进制,要序列化,打包(socket包)
高并发
工具限制(本身性能):
Tomcat 默认配置的最大请求数是 150,也就是说同时支持 150 个并发。当某个应用拥有 250 个以上并发的时候,应考虑应用服务器的集群。
系统限制
Windows 每个进程中的线程数不允许超过 2000
Linux 每个进程中的线程数不允许超过 1000
高并发衡量指标
- 响应时间(RT) :请求做出响应的时间,即一个http请求返回所用的时间
- 吞吐量: 系统在单位时间内处理请求的数量
- QPS、 TPS:每秒查询(请求)数、每秒事务数
- Apache JMeter: 模拟高并发
高可用
服务集群部署,数据库双主机方式
主-备方式(Active-Standby方式)
主-备方式即指的是一台服务器处于某种业务的激活状态(即Active状态),另一台服务器处于该业务的备用状态(即Standby状态)。
双主机方式(Active-Active方式)
双主机方式即指两种不同业务分别在两台服务器上互为主备状态(即Active-Standby和Standby-Active状态)
版本选择
- SpringBoot 2.3.6.RELEASE
- SpringCloud Hoxton.SR9
- SpringCloud Alibaba 2.2.6.RELEASE
RestTemplate远程调用
创建工程啥的就直接省略了。因为要引入的依赖是在是多呀,大概做法是先创建一个父工程cloud-paraent,再在该父工程下创建其余的工程。
还要安装一些插件,比如lombok,mybatisX以及一些idea中settings中的配置都省略
1、先保证cloud-consumer-order80 可以远程调用 cloud-provider-payment8001成功。
在消费端使用配置类配置bean对象RestTemplate
@SpringBootConfiguration
public class ApplicationContextConfig {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
并在controller层注入使用RestTemplate,使用以下方法实现远程调用
RestTemplate提供了多种便捷访问远程Http服务的方法
- restTemplate.getForObject()完成查询
- restTemplate.postForObject()完成添加
远程调用方法需要的三个参数
REST请求地址、请求参数、Http响应转换被转换成的对象类型。【其实看着都挺麻烦的,一会用OpenFeign替换】
@RestController
@Slf4j
public class OrderController {
public static final String PAYMENT_URL = "http://localhost:8001";
@Resource
private RestTemplate restTemplate;
@PostMapping("/consumer/payment/create")
public CommonResult create(@RequestBody Payment payment){
return restTemplate.postForObject(PAYMENT_URL+"/payment/create",payment, CommonResult.class);
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult getPayment(@PathVariable("id") Long id){
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
}
}
注意一个点,被远程调用的服务cloud-provider-payment8001的添加方法一定要使用@RequestBody,因为RESTAPI的风格就是http+json.
Eureka服务注册与发现(中心)
Eureka采用了CS的设计架构,Eureka Server作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到Eureka Server并维持心跳连接
注册中心(存放服务地址相关信息(接口地址))
Eureka Server 提供服务注册服务
各个微服务节点通过配置启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息。
Eureka Client 通过注册中心进行访问
是一个Java客户端,用于简化Eureka Server的交互。
--------
单机Eureka的构建步骤
IDEA生成eurekaServer端服务注册中心,也就是工程: cloud-eureka-server7001
这个工程特殊的地在于
1、引入以下依赖
org.springframework.cloud spring-cloud-starter-netflix-eureka-server
2、同时在application.yml中做如下配置,一会浏览器访问 http://localhost:7001/
server:
port: 7001 # eureka的服务端
eureka:
instance:
hostname: localhost #注册中心所在的服务器地址
client:
register-with-eureka: false #因为我就是服务端,不需要注册到Eureka中
fetchRegistry: false #我就是服务端不需要从Eureka中获取信息
service-url:
defaultZone: http://localhost:7001/eureka # 注册中心的访问地址
3、在主启动类上加注解 @EnableEurekaServer
@SpringBootApplication
@EnableEurekaServer
public class Application7001 {
public static void main(String[] args) {
SpringApplication.run(Application7001.class,args);
}
}
此时,启动工程 cloud-eureka-server7001,在浏览器端访问 http://localhost:7001/
在服务的提供者cloud-provider-payment8001和服务的消费者cloud-consumer-order80 中
都引入以下依赖,它俩都是Eurake的客户端
org.springframework.cloud spring-cloud-starter-netflix-eureka-client
在配置文件application.yml中加入以下配置
提供者 cloud-provider-payment8001配置如下:
eureka: client: register-with-eureka: true #8001支付微服务 是客户端需要向注册中心注册信息 fetchRegistry: true #8001支付微服务 是客户端需要从注册中心获取信息 service-url: defaultZone: http://localhost:7001/eureka消费者 cloud-consumer-order80配置如下:
eureka: client: register-with-eureka: true fetchRegistry: true service-url: defaultZone: http://localhost:7001/eureka
分别在主启动类加上注解:@EnableEurekaClient
提供者 cloud-provider-payment8001主启动类如下:
@SpringBootApplication @MapperScan(basePackages = "com.atguigu.springcloud.dao") @EnableEurekaClient public class PaymentMain8001 { public static void main(String[] args) { SpringApplication.run(PaymentMain8001.class,args); } }消费者 cloud-consumer-order80主启动类如下:
@SpringBootApplication @EnableEurekaClient public class OrderMain80 { public static void main(String[] args) { SpringApplication.run(OrderMain80.class,args); } }
依次启动工程 7001,8001,80
访问:http://localhost:7001/
Ribbon负载均衡服务调用
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
Ribbon目前也进入维护模式,未来替换方案是Spring Cloud LoadBalancer
LB负载均衡
将用户的请求平均分配到多个服务器上,从而达到系统的HA(高可用)。
Ribbon的本地负载均衡客户端 VS Nginx服务端负载均衡区别:
- Nginx是服务器负载均衡,客户端所有请求都会交给Nginx(http的服务器),然后由nginx实现转发请求到tomcat。即负载均衡是由服务器端完成的。
- Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用。
集中式LB(如Nginx)
- 即在服务的消费方和提供方之间使用独立的LB设施,由该设施负责把访问请求通过某种策略转发至服务的提供方;
进程内LB(如Ribbon)
- 将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。
Ribbon=负载均衡+RestTemplate调用
Ribbon在工作时分成两步:
第一步,先选择EurekaServer,它优先选择在同一个区域内负载较少的server。
第二步,再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。其中Ribbon提供了多种策略。比如:轮询、随机和根据响应时间加权。
总结:Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。
Eureka客户端自带Ribbon的依赖,所以就没必要再引入下面的ribbon依赖
org.springframework.cloud spring-cloud-starter-netflix-ribbon
----------
Ribbon核心组件IRule
IRule 接口及实现类如下:
IRule:根据特定算法从服务列表中选取一个要访问的服务
- com.netflix.loadbalancer.RoundRobinRule 轮询,默认策略。
- com.netflix.loadbalancer.RandomRule 随机
- com.netflix.loadbalancer.RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务
- WeightedResponseTimeRule 对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择(主动处理)
- BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
- AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例
- ZoneAvoidanceRule 默认规则,复合判断server所在区域的性能和server的可用性选择服务器
如何配置负载均衡策略
新建一个配置类,要特别注意该配置类不能放在@ComponentScan所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化订制的目的了。
@SpringBootConfiguration
public class MySelfRule {
@Bean
public IRule myRule(){
return new RoundRobinRule();
}
}
主启动类添加@RibbonClient
在RestTemplate配置类上加注解,@LoadBalanced
在controller层修改代码,url不再是 http://localhost:8001
复刻8001,再生成8002的服务
做到这些还不够,现在只有8001的提供者,我们再给他复刻出来一个8002的服务
8001(右键复制)
想要上面那样的效果,需要快速运行设置
vm options: -Dserver.port=8002
意味着服务名一样,同一个服务多个实例
重启80服务,重新获取服务列表。
此时再测试就达到了负载均衡轮询的效果(很帅)
Ribbon负载均衡算法
rest接口第几次请求数%服务器集群总数量=实际调用服务器位置下标
每次服务重启动后rest接口计数从1开始。
OpenFeign服务接口调用
HttpMessageConverters消息转交器
Feign是一个声明式的web服务客户端,Feign旨在使用编写Java Http客户端变得更容易。
Feign内置了 Ribbon
1、利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡。
2、与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用。
OpenFeign是Spring Cloud 在Feign的基础上支持了SpringMVC的注解,如@RequestMapping。
引入依赖
org.springframework.cloud spring-cloud-starter-openfeign
在配置文件application.yml加入以下配置
server:
port: 80
spring:
application:
name: cloud-consumer-feign-order80
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
在主启动类加一个注解 @EnableFeignClients,不加@EnableEurekaClient也可以吗?也可以的
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class OrderFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain80.class,args);
}
}
之前使用Ribbon+RestTemplate,因为不符合编程习惯。咱还是喜欢三层架构,没有Service层浑身不爽啊,那就使用OpenFeign的方式
在Controller层,调用service层,接着啥也不管。就是这么任性,哼。让我想起了傲娇的央视六公主,在b站的简介,就一个字。哼~
@RestController public class OrderFeignController { @Resource private PaymentFeignService paymentFeignService; //调用远程的微服接口 @GetMapping(value = "/consumer/payment/get/{id}") public CommonResultgetPaymentById(@PathVariable("id") Long id){ //远程调用 return paymentFeignService.getPaymentById(id); } }
实现方式:接口+@FeignClient
新建PaymentFeignService接口并新增注解@FeignClient
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE") //下载注册中心的服务信息
public interface PaymentFeignService {
@PostMapping(value = "/payment/create")
public CommonResult create(@RequestBody Payment payment);
@GetMapping(value = "/payment/get/{id}")
public CommonResult getPaymentById(@PathVariable("id") Long id);
}
启动服务7001,8001,8002,openFeign80
浏览器测试
Feign自带负载均衡(轮询)配置项
OpenFeign超时控制
在服务提供者处故意暂停程序,模拟3秒的延迟
Feign说:我只等一秒(默认等待一秒),等不到就错误渲染
那肯定1秒是太短了呀,就要设置Feign超时时间,在application.yml中加入以下配置(顶格)
ribbon: ReadTimeout: 4000 # 读取 ConnectTimeout: 4000 # 连接



