认识微服务
- 微服务是系统架构的一种设计风格,将一个原本独立的服务拆分成多个小型服务,每个服务独立运行在在各自的进程中,服务之间通过 HTTP RESTful API 进行通信.每个小型的服务都围绕着系统中的某个耦合度较高的业务进行构建。
- 微服务是一种经过良好设计的分布式架构方案,而全球的互联网公司都在积极尝试自己的微服务落地方案。其中在java领域最引人注目的是SpringCloud提供的方案。
- 单一职责:微服务拆分粒度更小,每个服务都应对唯一的业务能力,做到单一职责
- 自治:团队独立、技术独立、数据独立,独立部署和交付
- 面向服务:服务提供统一标准的接口,与语言无关、与技术无关
- 隔离性强:服务调用做好隔离、容错、降级,避免出现级联问题
SpringCloud
- SpringCloud是目前国内使用最广泛的微服务技术栈。官网地址:https://spring.io/projects/spring-cloud。
- SpringCloud集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配,从而提供了良好的开箱即用体验
- SpringCloud:SpringCloud是微服务架构的一站式解决方案,集成了各种优秀微服务功能组件
服务注册发现
- Eureka
- Nacos
- Consul
服务远程调用
- OpenFeign
- Dubbo
服务链路监控
- Zipkin
- Sleuth
统一配置管理
- SpringCloudConfig
- Nacos
统一网关路由
- SpringCloudGateway
- Zuul
流控、降级、保护
- Hystix
- Sentinel
服务提供者、服务消费者
- 服务提供者:一次业务中,被其它微服务调用的服务。(提供接口给其它微服务)
- 服务消费者:一次业务中,调用其它微服务的服务。(调用其它微服务提供的接口)
注册中心-Eureka
搭建注册中心
- pom.xml引入依赖
1
2
3
4
5
6
7<dependencies>
<!--EurekaServer包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies> - 编写启动类,添加@EnableEurekaServer注解
- 添加application.yml文件,编写配置
1
2
3
4
5
6
7
8
9
10
11server:
port: 8001 #端口号
spring:
application:
name: eureka-server # 应用名称,会在Eureka中作为服务的id标识(serviceId)
eureka:
client:
register-with-eureka: false #是否将自己注册到Eureka中
fetch-registry: false #是否从eureka中获取服务信息
service-url:
defaultZone: http://localhost:8001/eureka
服务注册
- 服务提供者注册
- pom.xml
1
2
3
4
5<!--EurekaClient包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency> - 修改application.yml
1
2
3
4
5
6
7
8
9eureka:
client:
service-url:
# EurekaServer的地址
defaultZone: http://localhost:8001/eureka
instance:
#以IP地址注册到服务中心
prefer-ip-address: true
instance-id: ${spring.application.name}:${server.port}:@project.version@
- 服务消费者注册
与提供者一致
总结
搭建EurekaServer
- 引入eureka-server依赖
- 启动类上添加@EnableEurekaServer注解
- 在application.yml中配置eureka地址
服务注册
- 引入eureka-client依赖
- 在application.yml中配置eureka地址
服务发现
- 引入eureka-client依赖
- 在application.yml中配置eureka地址
- 给RestTemplate添加@LoadBalanced注解
- 用服务提供者的服务名称远程调用(由原来的ip:port改服务名(spring.application.name))
负载均衡Ribbon
Ribbon是什么?
Ribbon是Netflix发布的负载均衡器,有助于控制HTTP客户端行为。为Ribbon配置服务提供者地址列表后,Ribbon就可基于负载均衡算法,自动帮助服务消费者请求。
概念:Ribbon是基于Http协议请求的客户端负载均衡器,能实现很丰富的负载均衡算法。
负载均衡流程图
1 | 1:用户发起请求,会先到达xxxxx-order服务 |
负载均衡算法
内置负载均衡规则类 | 规则描述 |
---|---|
RoundRobinRule | 简单轮询服务列表来选择服务器。 |
AvailabilityFilteringRule | 对以下两种服务器进行忽略: (1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。 (2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的 <clientName>.<clientConfigNameSpace>.ActiveConnectionsLimit 属性进行配置。 |
WeightedResponseTimeRule | 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。 |
ZoneAvoidanceRule【默认】 | 以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。它是Ribbon默认的负载均衡规则。 |
BestAvailableRule | 忽略哪些短路的服务器,并选择并发数较低的服务器。 |
RandomRule | 随机选择一个可用的服务器。 |
RetryRule | 重试机制的选择逻辑 |
Ribbon负载均衡算法使用
Ribbon负载均衡算法的使用有2种方式
代码方式: 全局,所有的服务提供提供者采用相同的负载均衡策略
- 注册IRule接口的实现类(负载均衡算法):在
xxxxx-order
的启动类中添加如下负载均衡注册代码:
- 注册IRule接口的实现类(负载均衡算法):在
1 | /** |
- 配置方式 局部配置,为每个服务提供者制定不同的负载均衡策略
- 为指定服务配置负载均衡算法:在
xxxxx-order
的核心配置文件中添加如下配置:1
2
3
4
5
6#注意配置到跟节点
#指定服务使用指定负载均衡算法
xxxxx-user:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #负载均衡规则
从懒加载 变为 饥饿加载
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。
而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,在xxxxx-order
的核心配置文件中,添加如下配置开启饥饿加载:
1 | #注意配置到根节点 |
Ribbon总结
Ribbon负载均衡规则
- 规则接口是IRule
- 默认实现是ZoneAvoidanceRule,根据zone选择服务列表,然后轮询
负载均衡自定义方式
- 代码方式:配置灵活,但修改时需要重新打包发布,全局配置
- 配置文件方式:直观,方便,无需重新打包发布,但是无法做全局配置,指定某个提供者的负载均衡策略【推荐】
饥饿加载, 拉取服务提供者的方式
- 开启饥饿加载
- 指定饥饿加载的微服务名称
Ribbin负载均衡原理与源码调试
http客户端Feign
- Feign是一个声明式的http客户端,官方地址:https://github.com/OpenFeign/feign 其作用就是帮助我们优雅的实现http请求的发送
Feign入门案例
定义和使用Feign客户端的步骤如下:
1 | 1:引入依赖包 spring-cloud-starter-openfeign |
引入依赖
1 | <!--openfeign--> |
开启Feign功能
在启动类添加@EnableFeignClients
注解开启Feign功能
1 |
|
定义远程调用接口
创建接口xxxClient
1 | import org.springframework.cloud.openfeign.FeignClient; |
主要是基于SpringMVC的注解来声明远程调用的信息,比如:
- 服务名称:user
- 请求方式:GET
- 请求路径:/user/{username}
- 请求参数:String username
- 返回值类型:User
远程调用
修改order
的OrderServiceImpl.one()
方法,执行远程调用
1 |
|
Feign其他功能
Feign运行自定义配置来覆盖默认配置,可以修改的配置如下:
类型 | 作用 | 说明 |
---|---|---|
feign.Logger.Level | 修改日志级别 | 包含四种不同的级别:NONE、BASIC、HEADERS、FULL |
feign.codec.Decoder | 响应结果的解析器 | http远程调用的结果做解析,例如解析json字符串为java对象 |
feign.codec.Encoder | 请求参数编码 | 将请求参数编码,便于通过http请求发送 |
feign. Contract | 支持的注解格式 | 默认是SpringMVC的注解 |
feign. Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试 |
1 | NONE:默认的,不显示任何日志 |
Feign日志配置,结合SpringBoot日志
1 | logging: |
配置Feign日志有两种方式:
- 配置文件方式
- 全局生效
1 | feign: |
- 局部生效
1 | feign: |
- 代码方式
- 注册日志级别
1 | /** |
- 全局生效
1 | #如果是全局配置,则把它放到@EnableFeignClients这个注解中 |
- 局部生效
1 | #如果是局部配置,则把它放到@FeignClient这个注解中 |
Feign性能优化
Feign底层的客户端实现:
- URLConnection:默认实现,不支持连接池
- Apache HttpClient :支持连接池
- OKHttp:支持连接池
因此优化Feign的性能主要包括:
- 使用连接池代替默认的URLConnection
- 日志级别,最好用basic或none
Feign切换Apache HttpClient步骤如下:
1 | 1:引入依赖 |
1)引入依赖
在xxxxx-order
中引入如下依赖:
1 | <!--httpClient依赖--> |
2)配置连接池
在xxxxx-order
的核心配置文件application.yml
中添加如下配置:
1 | feign: |
Feign最佳实践
方式一(继承):给消费者的FeignClient和提供者的controller定义统一的父接口作为标准。
- 服务紧耦合
- 父接口参数列表中的映射不会被继承
方式二(抽取)(推荐):将FeignClient抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用
Feign最佳实现流程如上图所示:
1 | 实现最佳实践方式二的步骤如下: |
1)引入依赖
创建xxxxx-api,然后引入feign的starter依赖 xxxxx-user依赖
1 | <dependencies> |
2)编写的UserClient
将xxxxx-order中编写的UserClient复制到xxxxx-api项目中
1 | package com.xxxxx.client; |
3)在xxxxx-order中引入xxxxx-api的依赖
1 | <!--引入feign-api--> |
- 【注意】当定义的FeignClient不在SpringBootApplication的扫描包范围时,这些FeignClient无法使用。
有两种方式解决:
方式一:指定FeignClient所在包(推荐-范围大)@EnableFeignClients(basePackages = "com.xxxxx.user.feign")
方式二:指定FeignClient字节码
@EnableFeignClients(clients = {UserClient.class})
Feign总结
Feign的使用步骤
- 引入依赖
- 启动类添加@EnableFeignClients注解,如果feignclient接口不在启动类包下,则需要添加扫包(basePackages )
- 编写FeignClient接口
- 使用FeignClient中定义的方法代替RestTemplate
Feign的日志配置:
- 方式一是配置文件,feign.client.config.xxx.loggerLevel
- 如果xxx是default则代表全局
- 如果xxx是服务名称,例如userservice则代表某服务
方式二是java代码配置Logger.Level这个Bean
- 如果在@EnableFeignClients注解声明则代表全局
- 如果在@FeignClient注解中声明则代表某服务
Feign的优化
- 日志级别尽量用basic
- 使用HttpClient或OKHttp代替URLConnection
- 引入feign-httpClient依赖
- 配置文件开启httpClient功能,设置连接池参数
Feign的最佳实践:
- 让controller和FeignClient继承同一接口
- 将FeignClient、POJO、Feign的默认配置都定义到一个项目中,供所有消费者使用