微服务学习笔记(一)
1、初识微服务
1.1、分布式架构:
-
根据业务功能对系统进行拆分,每个业务模块作为独立项目进行开发,称为一个服务。
-
优点:
-
降低服务耦合
-
有利于服务拓展升级
-
1.2、微服务:
微服务是一种经过良好架构设计的分布式架构方案,微服务架构的特征:
-
单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责,避免重复业务开发。
-
面向服务:微服务对外暴露业务接口
-
自治:团队独立、技术独立、数据独立、部署独立
-
隔离性强:服务调用做好隔离、容错、降级,避免出现级联问题
2、服务的拆分和远程调用
2.1、服务拆分
-
微服务拆分注意事项:
-
不同的微服务,不要重复开发相同的业务
-
微服务数据独立,不要访问其他微服务的数据库
-
微服务可以将自己的业务暴露为接口,供其他微服务使用
-
2.2、远程调用
2.2.1、RestTemplate实现远程调用(发送一次跨服务的http请求)
-
使用 RestTemplate 发送 http 请求来实现远程调用:
-
注册 RestTemplate
// 在启动类中注册RestTemplate -
服务远程调用 RestTemplate
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.利用RestTemplate发起http请求,查询用户
// 2.1 url路径
String url = "http://localhost:8081/user/" + order.getUserId();
// 2.2 发送http请求,实现远程调用
// get请求用getForObject,post请求用postForObject
User user = restTemplate.getForObject(url, User.class);
// 3. 封装user到order
order.setUser(user);
// 4.返回
return order;
} -
总结:
-
微服务调用方式:
-
基于restTemplate发起的http请求实现远程调用
-
HTTP请求做远程调用是与语言无关的调用,只要知道对方的ip、端口、接口路径、请求参数即可。
-
-
-
2.2.2、消费者与提供者
-
服务提供者:一次业务中,被其他微服务调用的服务(提供接口给其他服务)
-
服务消费者:一次业务中,调用其他微服务的服务(调用其他微服务提供的接口)
-
一个服务既可以是服务者也可以是消费者,角色是相对的
3、Eureka注册中心
3.1、eureka的作用
-
消费者该如何获取服务提供者具体信息?
-
服务提供者启动时会向eureka注册自己的信息
-
eureka会保存这些信息
-
消费者根据服务名称向eureka拉取提供者信息
-
-
如果有多个服务提供者,消费者应该如何选择呢?
-
服务消费者利用负载均衡算法,从服务列表中挑选一个
-
-
消费者如何感知服务提供者的健康状态?
-
服务提供者会每隔30秒向eurekaServer发送心跳请求,报告健康状态
-
eureka会更新记录服务列表,心跳不正常会被剔除
-
消费者就可以拉取到最新的信息
-
-
总结:
-
在eureka架构中,微服务角色有两类:
-
eurekaServer:服务端,注册中心,记录服务信息
-
心跳监控
-
-
eurekaClient:客户端
-
Provider:服务提供者
-
注册自己的信息到eurekaService
-
每隔30秒向eurekaService发送心跳
-
-
consumer:服务消费者
-
根据服务名称从eurekaServer拉取服务列表
-
基于服务列表做负载均衡,选中一个微服务后发起远程调用
-
-
-
3.2、搭建eureka
3.2.1、搭建eurekaServer
-
创建项目,引入依赖
<!--eureka服务端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency> -
编写启动类,添加@EnabledEurekaServer注解
-
添加application.yml文件,编写如下配置:
server
3.2.2、搭建eureka-client
3.2.2.1、服务注册
-
在项目中引入依赖
<!--eureka客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency> -
在项目的application.yml文件中,编写如下配置
spring
3.2.2.2、服务发现
服务拉取是基于服务名称获取服务列表,然后再对服务列表做负载均衡
-
修改项目的代码,修改访问的URL路径,用服务名代替IP、端口:
// 2.1 url路径
String url = "http://userService/user/" + order.getUserId(); -
在项目中的启动类的RestTemplate添加负载均衡的注解:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
4、Ribbon负载均衡
4.1、负载均衡流程
4.2、负载均衡策略
4.2.1、默认负载均衡策略
-
ZoneAvoidanceRule: 以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。
4.2.2、IRule更改策略
-
通过定义IRule实现可以修改负载均衡规则,有两种方式:
-
代码方式: 在项目(消费者)的启动类中,定义一个新的IRule:(针对全局的配置,修改时需要重新打包发布)
// 将默认的轮询负载均衡方式更改为随机的负载均衡方式
@Bean
public IRule randomRule(){
return new RandomRule();
} -
配置文件方式:在项目(消费者)的application.yml文件中,添加新的配置也可以修改规则:(针对某个服务的,修改后无需重新打包发布)
userService: # 服务名称
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
-
4.2.3、饥饿加载
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBanlanceClient,请求时间会很长。而饥饿加载则会在项目启动时创建,降低第一次访问的耗时。可以通过下面的配置开启饥饿加载:
ribbon:
eager-load:
enabled: true # 开启饥饿加载
clients: # 指定饥饿加载的服务名称,这里的clients是一个列表
- userService
5、Nacos
5.1、Nacos安装
-
下载安装包
-
解压
-
在bin目录下运行指令:startup.cmd -m standalone(单机运行),默认端口为8848
5.2、Nacos服务注册
-
在cloud-demo父工程中添加spring-cloud-alibaba 的管理依赖
<!--nacos的管理依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency> -
注释掉项目中原有的eureka依赖
-
添加nacos的客户端依赖
<!--nacos客户端依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency> -
修改项目中的application.yml文件,注释eureka地址,添加nacos地址:
spring:
application:
name: userService #user服务的服务名称
cloud:
nacos:
server-addr: localhost:8848 # nacos服务地址,将服务注册到nacos中去 -
启动并测试。
5.3、Nacos服务分级存储模型
-
存储模型:防止跨服务调用问题出现,不到万不得已不跨服调用
-
一级是服务
-
二级是集群
-
三级是实例
-
-
设置服务集群属性:
-
修改application.yml,添加如下内容:
spring:
cloud:
nacos:
server-addr: localhost:8848
discovery:
cluster-name: HZ # 集群名称,HZ代指杭州
-
5.4、NacosRule负载均衡
-
修改order-service中的application.yml,设置集群为HZ:
spring -
在order-service中设置负载均衡的IRule为NacosRule,这个规则优先会寻找与自己同集群的服务:(若不配置,则依旧会采用轮询的机制,不会考虑同集群的问题),优先选择本地集群,在本地集群的多个实例中进行随机访问。
userService -
总结:使用了NacosRule的负载均衡策略时:
-
优先使用同集群服务实例列表
-
本地集群找不到提供者时,会去寻找其他集群,同时,控制台会报警告
-
确定了可用实例列表后,再采用随机负载均衡挑选实例
-
5.5、根据权重负载均衡
-
在Nacos控制台可以设置实例的权重值,首先选中实例后面的编辑按钮
-
可以设置权重(0~1)
-
总结:实例的权重控制
-
Nacos控制台可以设置实例的权重值,0~1之间
-
同集群内的多个实例,权重越高被访问的频率越高
-
权重设置为0则完全不会被访问
-
5.6、Nacos环境隔离
Nacos中服务存储和数据存储的最外层都是一个名为namespace的东西,用来做最外层隔离。
-
在Nacos控制台可以创建namespace,用来隔离不同环境
-
然后填写一个新的命名空间信息:
-
保存后会在控制台看到这个命名空间的id:(这个id很重要)
-
修改order-service的application.yml,添加namespace:
spring:
datasource:
url: jdbc:mysql://localhost:3306/cloud_order?useSSL=false
username: root
password: 1234
driver-class-name: com.mysql.jdbc.Driver
application:
name: orderService # order的服务名称
cloud:
nacos:
server-addr: localhost:8848 # 将服务注册到nacos中去,nacos的地址配置
discovery:
cluster-name: HZ # 集群名称
namespace: b61fc8f8-8b0f-4437-b05d-dc6d87f48567 # dev环境,命名空间,填ID -
总结:
-
namespace用来做环境隔离
-
每个namespace都有唯一的id
-
不同的namespace下的服务不可见
-
5.7、Nacos和Eureka的比较
5.7.1、nacos注册中心细节分析
5.7.2、临时实例和非临时实例
服务注册到Nacos时,可以选择注册为临时或非临时实例,通过下面的配置来设置:
spring:
cloud:
nacos:
discovery:
ephemeral: false # 表示是否为临时实例
临时实例宕机时,会从nacos的服务列表中剔除,而非临时实例则不会
5.7.3、Nacos和Eureka的异同点
-
共同点:
-
都支持服务注册和服务拉取
-
都支持服务提供者心跳方式做健康监测
-
-
区别:
-
Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
-
临时实例心跳不正常会被剔除,非临时实例则不会
-
Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
-
Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP模式
-
6、Nacos配置管理
6.1、统一配置管理
-
在Nacos中添加配置信息:
-
在弹出的表单中填写配置信息:
注意:Nacos中的配置不是把application.yaml文件中的所有配置都拿来,建议将一些关键参数,需要运行时调整的参数放到nacos配置中心,一般都是自定义的配置
-
引入Nacos的配置管理客户端依赖:
<!--添加nacos的配置管理依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
-
在userService中的resource目录添加一个bootstrap.yml文件,这个文件是引导文件,优先级高于application.yml文件。
目的是在nacos的客户端寻找到对应的Data ID的配置文件
spring
注意:在进行配置读取时一定要注意命名空间的问题,要么配置bootstrap.yml的命名空间;要么将nacos配置文件放在public下
在微服务中添加bootstrap.yml,配置nacos地址、当前环境、服务名称、文件后缀名。这些决定了程序启动时去nacos的哪个环境读取哪个文件
在配置namespace时使用id而不是名字
6.2、配置自动更新
Nacos中的配置文件变更后,微服务无需重启就可以感知(热更新)。不过需要下面两种配置实现:
-
方式一:在@Value注入的变量所在类上添加注解@RefreshScope
-
方式二:使用@ConfigurationProperties注解
6.3、多环境配置共享
微服务启动时会从nacos读取多个配置文件:
-
[spring.application.name]-[spring.profiles.active].yaml,例如:userService-dev.yaml
-
[spring.application.name].yaml,例如:userService.yaml
无论profile如何变化,[spring.application.name].yaml这个文件一定会加载,因此,多环境共享配置可以写入这个文件
多种配置的优先级
-
服务名-profile.yaml > 服务名称.yaml > 本地配置
6.4、Nacos集群搭建
-
集群搭建步骤:
-
搭建MySQL集群并初始化数据库表
-
下载nacos
-
修改集群配置(节点信息)、数据库配置
-
分别启动多个nacos
-
Nginx反向代理
-
7、http客户端Feign
7.1、基于Feign的远程调用
7.1.1、ReatTemplate方式存在的问题
-
代码可读性差,编程体验不统一
-
参数复杂URL难以维护
7.1.2、定义和使用Feign客户端
使用Feign的步骤如下:
-
引入依赖:
<!--feign客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency> -
在order-service的启动类添加注解开启Feign的功能:
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
@EnableFeignClients // 开启Feign的功能
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
......
} -
编写Feign客户端:
@FeignClient("userService") // 括号里为服务的名称
public interface UserClient {
@GetMapping("/user/{id}")
User findById (@PathVariable("id") long id);
} -
使用FeignClient中定义的方法代替RestTemplate:
7.2、自定义Feign的配置
Feign运行自定义配置来覆盖默认配置,一般我们需要配置的就是日志级别。
配置Feign日志有两种方式:
-
方式一:配置文件的方式:
-
全局生效
feign -
局部生效
feign
-
-
方式二:java代码方式,首先需要先声明一个Bean:
public class DefaultFeignConfiguration {
@Bean
public Logger.Level logLevel(){
return Logger.Level.BASIC;
}
}-
而后如果是全局配置,则把它放到@EnableFeignClients这个注解中:(在启动类中)
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)
-
如果是 局部配置,则把它放到@FeignClient这个注解中:(在具体的Feign客户端里)
@FeignClient(value = "userService",configuration = DefaultFeignConfiguration.class)
-
7.3、Feign的性能优化
-
Feign底层的客户端实现:
-
URLConnection:默认实现,不支持连接池
-
Apache HTTPClient:支持连接池
-
OKHttp:支持连接池
-
-
因此,优化Feign的性能主要包括:
-
使用连接池代替默认的URLConnection
-
日志级别,最好用basic或none
-
-
Feign的性能优化--连接池配置:
-
Feign添加HttpClient的支持:
-
引入依赖:
<!--httpClient的支持-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency> -
配置连接池:
feign
-
-
8、统一网关Gateway
8.1、网关的功能
-
对用户请求做身份认证和权限校验
-
将用户请求路由到微服务、并实现负载均衡
-
对用户请求做限流
8.2、搭建网关服务
搭建服务网关的步骤:
-
创建新的module,引入SpringCloudGateway的依赖和Nacos的服务发现依赖:
<!--nacos服务注册发现依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--网关gateway的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency> -
编写路由配置及nacos地址
server -
总结:
网关路由可以配置的内容包括:
-
路由id:路由唯一标识
-
uri:路由目的地,支持lb和http两种
-
predicates:路由断言,判断请求是否符合要求,符合则转发到路由目的地
-
filter:路由过滤器,处理请求或者响应
-
8.3、路由断言工厂Route Predicates Factory
我们在配置文件中写的断言规则只是字符串,这些字符串会被 Predicates Factory 读取并处理,转变成为路由判断的条件
spring提供了11种基本的Predicate工厂。(使用时前往官网查找即可)
8.4、路由过滤器GatewayFilter
GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理,有31种。
-
配置局部过滤器:
gateway
-
配置全局过滤器:
gateway
-
过滤器的作用:
-
对路由的请求或者响应做加工处理,比如添加请求头
-
配置在路由下的过滤器只对当前路由的请求生效
-
-
default-filters的作用:
-
全局过滤器的配置
-
8.5、全局过滤器 GlobalFilter
全局过滤器的作用也是处理一切进入网关的请求和微服务的响应,与GatewayFilter的作用一样。
区别在于,GatewayFilter通过配置定义,处理逻辑是固定的。而GlobalFilter的逻辑需要自己写代码实现。
定义方式是实现GlobalFilter接口。
案例:定义全局过滤器,拦截并判断用户身份
需求:定义全局过滤器,拦截请求,判断请求的参数是否满足下面的条件:
-
参数中是否有authorization
-
authorization参数值是否为admin
如果同时满足则放行,否则拦截。
8.6、过滤器的执行顺序
请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter。
请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilte,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器。
-
每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前。
-
GlobalFilte通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定。
-
路由的过滤器和DefaultFilter的order由Spring指定,默认顺序是按照声明顺序从1递增,两者分开计数。
-
当过滤器的order值一样时,会按照 DefaultFilter > 当前路由的过滤器 > GlobalFilte 的顺序执行。
-

浙公网安备 33010602011771号