认识微服务

106 阅读26分钟

一、认识微服务

1.服务架构演变

单体架构

将业务的所有功能集中在一个项目中开发,打成一个包部署,适合小型项目,如:学生管理系统

优点:

  • 架构简单
  • 部署成本低

缺点:

  • 耦合度高

  • 拓展性差

分布式架构

根据业务功能对系统进行拆分,每个业务模块作为独立项目开发,成为一个服务。适合大型互联网项目,如:拼多多、京东、淘宝。

优点:

  • 降低服务耦合
  • 有利于服务升级拓展

缺点:

  • 架构复杂

  • 难度大

分布式架构需要考虑的问题

  • 服务拆分粒度?
  • 服务集群地址如何维护?
  • 服务之间如何实现远程调用?
  • 服务健康状态如何感知?

微服务

微服务是一种经过良好架构设计的分布式架构方案。

微服务架构特征:

  • 单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责,避免重复业务开发

  • 面向服务:微服务对外暴露业务接口

  • 自治:团队独立、技术独立、数据独立、部署独立

2.微服务技术对比

DubboSpringCloudSpringCloudAlibaba
注册中心zookeeper、RedisEureka、ConsulNacos、Eureka
服务远程调用Dubbo协议Feign(http协议)Dubbo、Feign
配置中心SpringCloudConfigSpringCloudConfig、Nacos
服务网关SpringCloudGateway、ZuulSpringCloudGateway、Zuul
服务监控和保护dubbo-admin,功能弱HystrixSentinel

企业需求

SpringCloud + Feign

  • 使用SpringCloud技术栈

  • 服务接口采用Restful风格

  • 服务调用采用Feign方式

SpringCloudAlibaba + Feign

  • 使用SpringCloudAlibaba技术栈
  • 服务接口采用Restful风格
  • 服务调用采用Feign方式

SpringCloudAlibaba + Dubbo

  • 使用SpringCloudAlibaba技术栈
  • 服务接口采用Dubbo协议标椎
  • 服务调用采用Dubbo方式

SpringCloudAlibaba + Dubbo

  • 基于Dubbo老旧技术体系

  • 服务接口采用Dubbo协议标椎

  • 服务调用采用Dubbo方式

3.SpringCloud

  • SpringCloud是目前国内使用最广泛的微服务框架。官网地址:spring.io/projects/sp…
  • SpringCloud集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配
--
服务注册发现Eureka、Nacos、Consul
服务远程调用OpenFeign、Dubbo
服务链路监控Zipkin、Sleuth
统一配置管理SpringCloudConfig、Nacos
统一网关路由SpringCloudGateway、Zuul
流控、降级、保护Hystix、Sentinel

二、服务拆分与远程调用

1.服务拆分注意事项

  1. 不同的微服务,不要重复开发相同的业务
  2. 微服务数据独立,不要访问其他微服务的数据库
  3. 微服务可以将自己的业务暴露为接口,供其他微服务调用

总结

  1. 微服务需要根据业务模块拆分,做到单一职责,不要重复开发相同的业务
  2. 微服务可以将业务暴露为接口,供其他微服务使用
  3. 不同微服务都应该有自己的独立的数据库

2.案例:微服务远程调用-查询订单

1.注册 RestTemplate

在 order-service 的 OrderApplication 中注册 RestTemplate

@MapperScan("cn.wow.order.mapper")
@SpringBootApplication
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }

    // 在此进行注册
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

2.服务远程调用 RestTemplate

修改 order-service 中的 OrderService 的 queryOrderById 方法

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    //在此添加
    @Autowired
    private RestTemplate restTemplate;

    public Order queryOrderById(Long orderId) {
        // 1.查询订单
        Order order = orderMapper.findById(orderId);
        // 2.利用 RestTemplate 发起 http 请求,查询用户
        // 2.1 url 路径
        String url = MicroServicesUrl.UserService + "/user/" + order.getUserId();
        // 2.2 发送 http 请求,实现远程调用
        User user = restTemplate.getForObject(url,User.class);
        // 3.封装 user 到 Order
        order.setUser(user);
        // 4.返回
        return order;
    }
}

3.微服务远程调用

1.微服务调用方式

  • 基于 RestTemplate 发起的 http 请求实现远程调用
  • http 请求做远程调用是与语言请求无关的调用,只要知道对方的 IP、端口、接口路径、请求参数即可

2.提供者与消费者

  • 服务提供者:一次业务中,被其他微服务调用的服务。(提供接口给其他服务)
  • 服务消费者:一次业务中,调用其他微服务的服务。(调用其他微服务提供的接口)
  • 提供者与消费者角色其实是**相对**的
  • 一个服务可以同时是服务提供者与服务消费者

三、Eureka注册中心

1.远程调用的问题

  • 服务消费者该如何获取服务提供者的地址信息?

    • 服务提供者启动时向eureka注册自己的信息
    • eureka保存这些信息
    • 消费者根据服务名称向eureka拉取提供者信息
  • 如果有多个服务提供者,消费者该如何选择?

    • 服务消费者利用负载均衡算法,从服务列表中挑选一个
  • 消费者如何得知服务提供者的健康状态?

    • 服务提供者会每个30秒向 EurekaServer 发送心跳请求,报告健康状态

    • eureka会更新记录服务列表信息,心跳不正常会被剔除

    • 消费者就可以拉取到最新的信息

2.Eureka原理

Eureka注册中心

Eureka的作用

  1. 注册服务信息

  2. 客户端 eureka-client: # 服务提供者 user-service: # 心跳续约——> eureka-service ,每30秒一次 localhost:8081 localhost:8082 localhost:8083 # 服务消费者 order-service: localhost:8080 # 注册中心 eureka-server:

  3. 拉取服务 user-service 的信息

  4. 负载均衡

  5. 远程调用

3.搭建 EurekaServer

  1. 创建项目,引入 spring-cloud-starter-netflix-eureka-server的依赖
  2. org.springframework.cloud spring-cloud-starter-netflix-eureka-server 2.2.3.RELEASE
  3. 编写启动类,添加 @EnableEurekaServer 注解
  4. 添加application.yml文件,编写下面的配置:
  5. server: port: 10086 # 服务端口 spring: application: name: eureka-server # eureka 的服务名称 eureka: client: service-url: # eureka 的地址信息 defaultZone: http://127.0.0.1:10086/eureka/

4. 服务注册

注册user-service

  1. 将user-service项目引入 spring-cloud-starter-netflix-eureka-client的依赖
  2. org.springframework.cloud spring-cloud-starter-netflix-eureka-client 2.2.3.RELEASE
  3. 在 application.yml 文件,编写下面的配置
  4. spring: application: name: user-service eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka/

5. 服务发现

在order-service完成服务拉取

服务拉取是基于服务名称获取服务列表,然后在对服务列表做负载均衡

  1. 修改 OrderService 的代码,修改访问的 url 路径,用服务名称代替 ip

  2. String url = "http://userservice/user" + order.getUserId();

  3. 在 order-service 项目的启动类 OrderApplication 中的RestTemplate添加**负载均衡**注解:

  4. @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); }

四、Ribbon负载均衡

1.负载均衡流程

2.负载均衡策略

Ribbon的负载均衡规则是一个叫做 IRule 的接口来定义的,每一个子接口都是一种规则。

内置负载均衡规则类规则描述
RoundRobinRule简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡。
AvailabilityFliteringRule对以下服务器进行忽略: 1. 在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为短路状态。短路状态将持续30秒,如果在此连接失败,短路的持续时间就会以几何级地增加。 2. 并发数过高的服务器,如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的..ActiveConnectionsLimit属性配置。
WeightedResponseTimeRule为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重会影响服务器的选择。
ZoneAvoidanceRule以区域可用的服务器为基础进行服务器的选择,使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的服务做轮询。
BestAvailableRule忽略哪些短路的服务器,并选择并发数较低的服务器。
RandomRule随机选择一个可用的服务器。
RetryRule重试机制的选择逻辑。

通过定义IRule实现可以修改负载均衡规则,有两种方式:

  1. 代码方式,在order-service的OrderApplication类中,定义一个新的IRule
  2. @Bean public IRule randomRule(){ return new RandomRule(); }
  3. 配置文件方式,在 order-service 的 application.yml 文件中,添加新的配置也可以修改规则:
  4. user-service: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则

3.饥饿加载

Ribbon默认采用**懒加载**,即第一次访问时才回去创建 LoadBalanceClient ,请求时间会很长。

而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开始饥饿加载:

ribbon: eager-load: enabled: true # 开始饥饿加载 clients: user-service # 指定对 user-service 这个服务饥饿加载

4.总结

  1. Ribbon负载均衡规则

    1. 规则接口是IRule
    2. 默认实现是ZoneAvoidanceRule,根据zone选择服务列表,然后轮询
  2. 负载均衡自定义方式

    1. 代码发布:配置灵活,但修改时需要重新打包发布
    2. 配置方式:直观、方便、无需重新打包发布,但是无法做全局配置
  3. 饥饿加载

    1. 开启饥饿加载

    2. 指定饥饿加载的微服务名称

五、Nacos注册中心

1.认识和安装Nacos

Nacos是阿里巴巴的产品,现在是SpringCloud中的一个组件。相比Eureka功能更加丰富,在国内受欢迎程度较高。

2.Nacos快速入门

启动

单机启动(非集群)

startup.cmd -m standalone

服务注册到 Nacos

  1. 在cloud-demo父工程中添加spring-cloud-alibaba的管理依赖
  2. com.alibaba.cloud spring-cloud-alibaba-dependencies 2.2.5.RELEASE import
  3. 注释掉order-service 与 user-service中原有的eureka依赖
  4. 添加 nacos 的客户端依赖
  5. com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery 2.2.5.RELEASE
  6. 修改user-service&order-service中的application文件,注释eureka地址,添加nacos地址:
  7. spring: cloud: nacos: server-addr: localhost:8848 # nacos 服务端地址
  8. 启动并测试

3.Nacos服务分级存储模型

服务调用尽可能选择本地集群的服务,跨集群调用延迟较高

本地集群不可访问时,再去访问其它集群

服务集群属性

  1. 修改 application.yml ,添加如下内容
  2. spring: cloud: nacos: server-addr: localhost:8848 discovery: cluster-name: SD #集群配置名称,也就是机房位置,例如:SD,山东
  3. 在 Nacos 控制台可以看到集群变化

临时实例与非临时实例

临时实例采用心跳检测

非临时实例 nacos 主动询问

spring: cloud: nacos: discovery: ephemeral: false # 设置为非临时实例

总结

  1. Nacos服务分级存储模型

    1. 一级是服务,例如 user-service
    2. 二级是集群,例如 ShanDong 或者 ShangHai
    3. 三级是实例,例如杭州机房的某台部署了 user-service 的服务器
  2. 如何设置实例的集群属性

  3. 修改 application.yml 文件,添加 spring.cloud.nacos.discovery.cluster-name 属性即可

  4. Nacos与Eureka的区别

    1. Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式。
    2. 临时实例心跳不正常会被剔除,非临时实例则不会被剔除
    3. Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
    4. Nacos集群默认采用AP模式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP模式

根据权重负载均衡

user-service: ribbon: # NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # eureka 负载均衡规则 NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # nacos 负载均衡规则

实际部署中会出现这样的场景:

  • 服务器设备性能有差异,部分实例所在机器性能较好,另一些较差,我们希望性能好的机器承担更多的用户请求

Nacos 提供了权重配置来控制访问频率,权重越大访问频率越高,取值范围 [0,1],为0时不会被访问

4.Nacos环境隔离

环境隔离 - namespace

Nacos 中服务存储和数据存储的最外层都是一个名为 namespace 的东西,用来做最外层隔离

设置完成后,配置 id

spring: cloud: nacos: server-addr: localhost:8848 discovery: cluster-name: ShanDong namespace: 542ac362-6b65-4ca8-9276-164bfe5fb505

不同 namespace 下的服务不可见

六、Nacos配置管理

1.统一配置管理

  • 配置更改热更新

配置步骤

配置获取的步骤如下:

  1. 项目启动

  2. 读取 bootstrap.yml 文件,读取nacos地址

  3. 读取nacos中的配置文件

  4. 读取本地配置文件 application.yml

  5. 创建spring容器

  6. 加载bean

  7. 引入Nacos的配置管理客户端依赖

  8. com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config

  9. 在 user-service中的 resource 目录添加一个 bootstrap.yml文件,这个文件时引导文件,优先级高于application.yml

  10. spring: application: name: user-service profiles: active: dev # 开发环境,这里是 dev cloud: nacos: server-addr: localhost:8848 # Nacos 地址 config: file-extension: yaml # 文件后缀名

2.配置热更新

Nacos 中的配置文件变更后,微服务无需重启就可以感知,不过需要通过下面两种配置实现:

  1. 在 @Value 注入的变量所在类上添加注解 @RefreshScope
  2. @Slf4j @RestController @RequestMapping("/user") @RefreshScope public class UserController { }
  3. 使用@ConfigurationProperties 注解
  4. @Component @Data @ConfigurationProperties(prefix = "pattern") public class PatternProperties { private String dateformat; }

3.多环境配置共享

微服务启动时会从nacos读取多个配置文件:

  • 命名方式:[spring.application.name]-[spring.profiles.active].yaml,例如:user-service-dev.yaml
  • 命名方式:[spring.application.name].yaml,例如 user-service.yaml

无论profile如何变化,[spring.application.name].yaml 文件一定会被加载,因此多环境共享配置可以写入这个文件。

4.搭建Nacos集群

踩坑记录

bootstrap.yml

spring: application: name: user-service profiles: active: dev # 开发环境,这里是 dev cloud: nacos: server-addr: localhost:80 # Nacos 地址,一定要加端口号 config: file-extension: yaml # 文件后缀名 enabled: false # 禁用本地

nacos最好一机启动一个

ngnix 配置最好要用 当前 ip

初始化 nacos sql

github.com/alibaba/nac…

七、http 客户端 Feign

1.引入与使用

org.springframework.cloud spring-cloud-starter-openfeign

添加注解

package cn.itcast.order; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @EnableFeignClients @MapperScan("cn.itcast.order.mapper") @SpringBootApplication public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } /** * 创建 RestTemplate 并注入 Spring 容器,用于请求接口 */ @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } }

UserClient.java

package cn.itcast.order.clients; import cn.itcast.feign.pojo.User; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient("user-service") public interface UserClient { @GetMapping("/user/{id}") User findById(@PathVariable("id") Long id); }

order-service.java

package cn.itcast.order.service; import cn.itcast.order.clients.UserClient; import cn.itcast.order.mapper.OrderMapper; import cn.itcast.order.pojo.Order; import cn.itcast.feign.pojo.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class OrderService { @Autowired private OrderMapper orderMapper; @Autowired private UserClient userClient; public Order queryOrderById(Long orderId) { // 1.查询订单 Order order = orderMapper.findById(orderId); // 2.使用 Feign 进行远程调用 User user = userClient.findById(order.getUserId()); // 3.封装 user 到 Order order.setUser(user); // 4.返回 return order; } }

2.自定义配置

类型作用说明
feign.Logger.Level修改日志级别包含四种不同的级别:NONE,BASIC,HEADERS,FULL
feign.codec.Decoder相应结果的解析器http 远程调用的结果做解析,例如解析json 字符串为 Java 对象
feign.codec.Encoder请求参数编码将请求参数编码,便于通过 http 请求发送
feign.Contract支持的注解格式默认是 SpringMVC 注解
feign.Retryer失败重试机制请求失败的重试机制,默认是没有,不过会使用 Ribbon 重试

配置文件方式

feign: client: config: default: # 这里 default 就是全局配置,服务名称则是局部配置 loggerLevel: FULL 日志级别

Java 代码方式

首先声明一个 Bean

public class FeignClientConfiguration { @Bean public Logger.Level feignLogLevel(){ return Logger.Level.BASIC; } }

全局配置

@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)

局部配置

@EnableFeignClients(value = "user-service",configuration = FeignClientConfiguration.class)

3.性能优化

Feign 底层的客户端实现:

  • URLConnection: 默认实现,不支持连接池

  • Apache HttpClient: 支持连接池

  • OKHttp:支持连接池

因此优化 Feign 的性能主要包括:

  1. 使用连接池代替默认的 URLConnection

  2. 日志级别,最好用basic或 none

引入依赖

io.github.openfeign feign-httpclient

配置连接池

feign: client: config: default: # default 全局的配置 logger-level: BASIC # 日志级别,basic 是基本的请求与响应信息 httpclient: enabled: true # 开启 feign 对 httpClient 的支持 max-connections: 200 # 最大的连接数 max-connections-per-route: 50 # 每个路径的最大连接数

4.最佳实践

方式一 继承 不推荐:

给消费者的 FeignClient 和提供者 controller 定义统一的父接口作为标准。

  • 服务紧耦合

  • 父接口参数列表中的映射不会被继承

方式二 抽取:

将 FeignClient 抽取为独立模块,并且把接口有关的 POJO、默认的 Feign 配置都放到这个模块中,提供给所有消费者使用

  1. 首先创建一个 module,命名为 feign-api,然后引入 feign 的 starter 依赖

  2. 将 order-service 中编写的 UserClient、User、DefaultFeignConfiguration 都复制到 feign-api 项目中

  3. 在 order-service 中引入 feign-api 依赖

  4. 修改 order-service 中的所有与上述三个组件有关的 import 部分,改成导入 feign-api 中的包

  5. 重启测试

当定义的 FeignClient 不在 SpringBootApplication 的扫描包范围时,这些 FeignClient 无法使用,有两种方式解决:

方式一:指定 FeignClient 所在包

@EnableFeignClients(basePackages = "cn.itcast.feign.clients")

方式二:制定 FeignClient 字节码

@EnableFeignClients(clients = {UserClient.class})

八、统一网关 Gateway

1.为什么需要网关

网关功能:

  • 身份认证和权限校验
  • 服务路由、负载均衡
  • 请求限流

2.网关的技术实现

在 SpringCloud 中网关的实现包括两种:

  • gateway
  • zuul

Zuul 是基于 Servlet 的实现,属于阻塞式编程.

SpringCloudGateway 基于 Spring5 中提供的 WebFlux,属于响应式 编程的实现,具备更好的性能。

3.搭建网关

  1. 创建新的 module,引入 SpringCloudGateway 的依赖和 nacos 的服务发现依赖:
  2. com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.cloud spring-cloud-starter-gateway
  3. 编写路由配置及 nacos 地址
  4. server: port: 10010 spring: application: name: gateway # 服务名称 cloud: nacos: discovery: server-addr: localhost:80 # nacos 地址 gateway: routes: # 网关路由配置 - id: user-service # 路由 id,自定义,只要唯一几颗 # uri:http://127.0.0.1:8081 # 路由的目标地址 http 就是固定地址 uri: lb://user-service # 路由的目标地址,lb 后面是负载均衡,后面跟服务名称 predicates: # 路由断言,也就是请求是否符合路由规则的条件 - Path=/user/** # 这个是按照路径匹配,只要以 /user/ 开头就符合要求

4.路由断言工厂 Route Predicate Factory

网关路由可以配置的内容包括:

  • 路由 id:路由唯一标识

  • uri: 路由目的地,支持 lb 和 http 两种

  • predicates:路由断言,判断请求是否符合要求,符合则转发到路由目的地

  • filter:路由过滤器,处理请求或响应

我们在配置文件中写的断言规则只是字符串,这些字符串会被 Predicate Factory 读取并处理,转变为路由判断的条件

例如Path=/user/**是按照路径匹配,这个规则是由 org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory类来处理的

Spring 提供了 11 种基本的 Predicate 工厂

名称说明示例
After是某个时间点后的请求- After=2037-01-20T17:42:47.789-07:00[America/Denver]
Before是某个时间点之前的请求
Between是某个时间点之间的请求
Cookie请求必须包含某些 cookie- Cookie=chocolate, ch.p
Header请求必须包含某些 header- Header=X-Request-ld,\d+
Host请求必须包含某些 host- Host=.somehost.org,.anotherhost.org
Method请求必须要是指定方式- Method=GET,POST
Path请求路径必须符合指定规则- Path=/red/{segment},/blue/**
Query请求参数必须包含指定参数- Query=name,Jack 或者 - Query=name
RemoteAddr请求者的 ip 必须是指定范围- RemoteAddr=192.168.1.1/24
Weight权重处理

5.路由过滤器 GatewayFilter

GatewayFilter 是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理:

GatewayFilterFactories文档,共有 31 种过滤器

gateway

server: port: 10010 spring: application: name: gateway # 服务名称 cloud: nacos: discovery: server-addr: localhost:80 # nacos 地址 gateway: routes: # 网关路由配置 - id: user-service # 路由 id,自定义,只要唯一几颗 # uri:http://127.0.0.1:8081 # 路由的目标地址 http 就是固定地址 uri: lb://user-service # 路由的目标地址,lb 后面是负载均衡,后面跟服务名称 predicates: # 路由断言,也就是请求是否符合路由规则的条件 - Path=/user/** # 这个是按照路径匹配,只要以 /user/ 开头就符合要求 filters: # 过滤器 - AddRequestHeader=Truth,I am freaking awesome! # 添加请求头 - id: order-service uri: lb://order-service predicates: - Path=/order/**

@GetMapping("/{id}") public User queryById(@PathVariable("id") Long id, @RequestHeader(value = "Truth", required = false) String truth) { System.out.println("truth:" + truth); return userService.queryById(id); }

默认过滤器

server: port: 10010 spring: application: name: gateway # 服务名称 cloud: nacos: discovery: server-addr: localhost:80 # nacos 地址 gateway: routes: # 网关路由配置 - id: user-service # 路由 id,自定义,只要唯一几颗 # uri:http://127.0.0.1:8081 # 路由的目标地址 http 就是固定地址 uri: lb://user-service # 路由的目标地址,lb 后面是负载均衡,后面跟服务名称 predicates: # 路由断言,也就是请求是否符合路由规则的条件 - Path=/user/** # 这个是按照路径匹配,只要以 /user/ 开头就符合要求 # filters: # 过滤器 # - AddRequestHeader=Truth,I am freaking awesome! # 添加请求头 - id: order-service uri: lb://order-service predicates: - Path=/order/** default-filters: - AddRequestHeader=Truth,I am freaking awesome!

6.全局过滤器 GlobalFilter

全局过滤器的作用也是处理一切进入网关请求和微服务响应,与 GatewayFilter 的作用一样.

区别在于 GatewayFilter 通过配置定义,处理逻辑是固定的,而 GlobalFilter 的逻辑需要自己写代码实现.

定义方式是实现 GlobalFilter 接口

案例:定义全局过滤器,拦截并判断用户身份

需求:定义全局过滤器,拦截请求,判断请求的参数是否满足下面条件:

  • 参数中是否有 authorization
  • authorization 参数值是否为 admin

如果同时满足则放行,否则拦截

package cn.itcast.gateway; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; //@Order(-1) @Component public class AuthorizeFilter implements GlobalFilter, Ordered { @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 1.获取请求参数 ServerHttpRequest request = exchange.getRequest(); MultiValueMap<String,String> params = request.getQueryParams(); // 2.获取参数中的 authorization 参数 String auth = params.getFirst("authorization"); // 3.判断参数值是否等于 admin if ("admin".equals(auth)){ // 4.是,放行 return chain.filter(exchange); } // 5.否,拦截 // 5.1 设置状态码 exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); // 5.2 拦截请求 return exchange.getResponse().setComplete(); } @Override public int getOrder() { return -1; } }

7.过滤器执行顺序

请求进入网关会碰到三类过滤器:当前路由的过滤器,DefaultFilter,GlobalFilter

请求路由后,会将当前路由过滤器和 DefaultFilter,GlobalFilter,合并到一个过滤器链中,排序后依次执行每个过滤器

  • 每一个过滤器都必须指定一个 int 类型的 order 值,order 值越小,优先级越高,执行顺序越靠前

  • GlobalFilter 通过实现 Ordered 接口,或者添加@Order 注解来指定 order 值,由我们自己指定

  • 路由过滤器和 defaultFilter 的 order 由 Spring 指定,默认是按照声明顺序从 1 递增

  • 当过滤器的 order 值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter 的顺序执行

8.跨域问题处理

网关处理跨域采用的同样是CORS方案,需要进行简单的配置

server: port: 10010 spring: application: name: gateway # 服务名称 cloud: nacos: discovery: server-addr: localhost:80 # nacos 地址 gateway: routes: # 网关路由配置 - id: user-service # 路由 id,自定义,只要唯一几颗 # uri:http://127.0.0.1:8081 # 路由的目标地址 http 就是固定地址 uri: lb://user-service # 路由的目标地址,lb 后面是负载均衡,后面跟服务名称 predicates: # 路由断言,也就是请求是否符合路由规则的条件 - Path=/user/** # 这个是按照路径匹配,只要以 /user/ 开头就符合要求 filters: # 过滤器 - AddRequestHeader=Truth,I am freaking awesome! # 添加请求头 - id: order-service uri: lb://order-service predicates: - Path=/order/** default-filters: - AddRequestHeader=Truth,I am freaking awesome! globalcors: add-to-simple-url-handler-mapping: true # 解决 options 请求被拦截的问题 cors-configurations: '[/**]': allowed-origins: # 允许哪些网站跨域请求 - "http://localhost:8090" - "www.test.com" allowed-methods: - "GET" - "POST" - "DELETE" - "PUT" - "OPTIONS" allowed-headers: "*" # 允许在请求中携带的头信息 allow-credentials: true # 是否允许携带 cookie max-age: 360000 # 这次跨域检测的有效期

九、初始Docker

1.初识Docker

Docker是一个快速交付应用 运行应用的技术

Docker如何解决依赖的兼容问题?

  • 将应用的Libs(函数库)、Deps(依赖)、配置与应用一起打包,形成可移植镜像

  • 将每个应用放到一个隔离容器去运行,使用沙箱机制 , 避免相互干扰

Docker如何解决不同系统环境的问题?

  • Docker将用户程序与所需要调用的系统(比如Ubuntu)函数库一起打包

  • Docker运行到不同操作系统时,直接基于打包的库函数,借助于操作系统的Linux内核来运行

Docker和虚拟机的差异

  • Docker是一个系统进程 ; 虚拟机是在操作系统中的操作系统
  • Docker体积小,启动速度快, 性能好 ; 虚拟机体积大,启动速度慢,性能一般.

镜像和容器

镜像(Image): Docker将应用程序及其所需的依赖,函数库,环境,配置等文件打包再一起,称为镜像

容器(Container):镜像中的应用程序运行后形成的进程就是容器,只用Docker会给容器做隔离,对外不可见

Docker架构

Docker是一个CS架构

  • Server: Docker守护进程,负责处理Docker指令,管理镜像,容器等

  • Client:通过命令或者RestAPI向Docker服务端发送指令.可以在本地或远程向服务器发送指令.

安装Docker

安装yum

yum install -y yum-utils \ device-mapper-persistent-data \ lvm2 --skip-broken

本地镜像源

设置docker镜像源 yum-config-manager \ --add-repo \ mirrors.aliyun.com/docker-ce/l… sed -i 's/download.docker.com/mirrors.aliyun.com/docker-ce/g' /etc/yum.repos.d/docker-ce.repo yum makecache fast

安装

yum install -y docker-ce

2.Docker基本操作

容器相关命令

创建运行一个Nginx容器

去 docker hub 查看Ngnix容器的运行命令

docker run --name containerName -p 80:80 -d nginx

命令解读:

  • docker run: 创建并运行一个容器

  • --name:给容器起一个名字,比如叫做mn

  • -p: 将宿主机端口与容器端口映射,冒号左侧是宿主机端口,右侧是容器端口

  • -d:后台运行容器

  • nginx:镜像名称

查看容器日志

docker logs -f

参数f 可持续查看日志

查看容器状态

docker ps

进入Ngnix容器,修改HTML文件内容

docker exec -it mn bash

命令解读:

  • docker exec:进入容器内部,执行一个命令
  • -it:给当前进入的容器创建一个标准输入、输出终端,允许我们容器交互
  • mn:要进入容器的名称
  • bash:要进入容器后执行的命令。bash是一个linux终端交互命令

练习:创建一个Redis容器,支持数据持久化

3.数据卷

数据卷(volume)是一个虚拟目录,指向宿主文件系统中的某个目录

将容器与数据分离,解耦合,方便操作容器内数据,保证数据安全.

挂载

操作数据卷

数据卷操作的基本语法如下

docker volume [COMMAND]

docker volume命令是数据卷操作,根据命令后跟随的command来确定下一步的操作:

  • create 创建一个volume

  • inspect 显示一个或多个volume的信息

  • ls 列出所有的volume

  • prune 删除未使用的volume

  • rm 删除一个或者多个指定volume

启动mysql

docker run --name mysql -e MYSQL_ROOT_PASSWORD=wuao -p 3306:3306 -v /tmp/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf -v /tmp/mysql/data:/var/lib/mysql -d mysql

docker run --name mn -d -p 80:80 -v /root/docker/nginx/html/:usr/share/nginx/html/ -v /root/docker/nginx/conf/nginx.conf:/etc/nginx/nginx.conf nginx

4.Dockerfile 自定义镜像

镜像结构

镜像是将应用程序及其需要的系统函数库,环境,配置,依赖打包而成

镜像是分层结构,每一层成为一个Layer

  • BaseImage层:包含基本的系统库函数,环境变量,文件系统
  • Entrypoint :入口,是镜像中应用启动的命令
  • 其他:在 BaseImage 基础上添加依赖,安装程序,完成整个应用的安装和配置

Dockerfile

Dockerfile就是一个文本文件,其中包含了一个个的指令(Instruction),用指令来说明执行什么操作来构建镜像,每一个指令都会形成一个Layer,通过指令描述镜像的构建过程.

Dockerfile的第一行必须是FROM,从一个基础镜像来构建

基础镜像可以是基本操作系统,如Ubuntu,也可以是其他人制作好的镜像,例如 java:8-alpine

5.DockerCompose

  • Docker Compose 可以基于 Compose 文件帮我们快速的部署分布式应用,无需手动一个个创建和运行容器

  • Compose 文件是一个文本文件,通过指令定义集群中的每个容器如何运行

十、服务异步通讯

1.初识MQ

MQ(MessageQueue),中文是消息队列,字面上看就是存放消息的队列.也就是事件驱动架构中的Broker

同步调用出现的问题?

  1. 耦合度高,每次加入新的需求,都需要修改原来的代码

  2. 性能下降,调用者需要等待服务提供者相应,如果调用链过长则相应时间等于每次调用的时间之和

  3. 调用链中的每个服务在等待相应过程中,不能释放请求占用的资源,高并发场景下会极度浪费系统资源

  4. 级联失败,如果服务提供者出现问题,所有调用方都会跟着出问题,如同多米诺骨牌一样,迅速导致整个微服务群故障

异步调用方案

异步调用常见就是事件驱动模式

优势:

  1. 服务解耦
  2. 性能提升,吞吐量提高
  3. 服务没有强依赖,不担心级联失败问题
  4. 流浪削峰

缺点:

  1. 依赖于Broker的可靠性,安全性,吞吐能力

  2. 架构复杂了,业务没有明显的流程线,不好追踪管理

RabbitMQ

  • channel:操作MQ的工具

  • exchange:路由消息到队列中

  • queue:缓存消息

  • virtual host: 虚拟主机,是对queue,exchange等资源的逻辑分组

3.SpringAMQP

AMQP

Advanced Message Queuing Protocol,是用于在应用程序或之间传递业务消息的开放标准.该协议与平台无关,更符合微服务中独立性的要求

Spring AMQP

Spring AMQP 是基于AMQP协议定义的一套API规范,提供了模板来发送和接收消息.包含两部分,其中spring-amqp是基础抽象,spring-rabbit是底层默认实现

十一、Elasticsearch

1.Elasticsearch

Elasticsearch结合kibana,Logstash,Beats,也就是 elastic stack (ELK),被广泛应用在日志数据分析,实时监控等领域

Elasticsearch是elastic stack的核心,负责存储,搜索,分析数据

2.正向索引和倒排索引

elasticsearch 采用倒排索引:

  • 文档(document):每条数据就是一个文档

  • 词条(term):文档按照语义分成的词语

3.文档