微服务技术栈
微服务
- 单体架构:将业务的所有功能集中在一起开发,打成一个包部署。
- 优点:架构简单、部署成本低
- 缺点:耦合度高
- 分布式架构:根据业务功能对系统进行拆分,每个业务模块作为独立项目开发,称为一个服务。
- 优点:耦合度低、升级拓展方便
- 问题:拆分粒度、服务之间的远程调用
微服务是一种经过良好架构设计的分布式架构方案,微服务架构特征:
- 单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责,避免重复业务开发
- 面向服务
- 自治
- 隔离性强
服务调用关系:
- 服务提供者:暴露接口给其他微服务调用
- 服务消费者:调用其他微服务提供的接口
- 提供者与消费者角色是相对的
Eureka注册中心
在Eureka架构中,微服务角色有两类:
- EurekaServer:服务端,注册中心
- 记录服务信息
- 心跳监控
- EurekaClient:客户端
- Provider:服务提供者,例如案例中的user-service
- 注册自己的信息到EurekaServer
- 每隔30秒向EurekaServer发送心跳
- consumer:服务消费者,例如案例中的order-service
- 根据服务名称从EurekaServer中拉取服务列表
- 基于服务列表做负载均衡,选中一个微服务后发送远程调用
- Provider:服务提供者,例如案例中的user-service
EurekaServer使用:
-
引依赖
<dependencies> <!-- eureka服务端--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies>
-
加注解
@EnableEurekaServer // @SpringBootApplication public class EurekaApplication { public static void main(String[] args) { SpringApplication.run(EurekaApplication.class, args); System.out.println("Eureka-server 运行成功!"); } }
-
写配置信息
server: port: 8082 spring: application: name: eurekaserver eureka: client: service-url: defaultZone: http://localhost:8082/eureka
EurekaClient使用:
-
引入依赖
<dependency> <!--是这个带starter的依赖--> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
-
修改配置信息
server: port: 8081 spring: application: name: userservice eureka: client: service-url: defaultZone: http://localhost:8082/eureka
三个服务都注册了。
服务发现:
-
引入Eureka-client依赖
-
在application.yml中配置eureka的地址
-
给RestTemplate添加@LoadBalanced注解
@MapperScan("cn.itcast.order.mapper") @SpringBootApplication public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } @Bean @LoadBalanced // 负载均衡的注解 public RestTemplate restTemplate () { return new RestTemplate(); } }
-
用服务提供者的服务名称远程调用。
public Order queryOrderById(Long orderId) { // 1.查询订单 Order order = orderMapper.findById(orderId); String url = "http://userservice/user/" + order.getUserId(); // 请求地址 User user = restTemplate.getForObject(url, User.class); order.setUser(user); // 4.返回 return order; }
Ribbon负载均衡
负载均衡的策略(两种方式):
-
代码方式:在order-service中的OrderApplication类中,定义一个新的IRule:
@Bean public IRule randomRule() { return new RandomRule(); }
全局配置方式,即这个order服务 请求的所有服务 都使用 随机方式,负载均衡。
-
配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改规则:
userservice: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
指定某些服务,使用什么样的负载均衡规则。
饥饿加载
-
开启饥饿加载
-
指定饥饿加载的微服务名称
ribbon:
eager-load:
enabled: true # 开启饥饿加载,即在启动时加载这些类。不是等调用时,再加载(懒汉式)
clients: userservice # 指定对userservice这个服务饥饿加载。这个是List,赋多个值时,如下
- userservice
- userservice
Nacos
1 配置Nacos
-
下载,Nacos
-
在bin目录,打开cmd,使用
startup.cmd -m standalone
启动。注意,路径中不能有中文 -
引入依赖,配置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>
客户端:
<!-- nacos客户端依赖包 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
服务的yml文件
spring: cloud: nacos: server-addr: localhost:8848
此时,启动服务,会在http://127.0.0.1:8848/中发现注册的服务。账号和密码都为nacos
2 服务多级存储模型
Nacos服务分级存储模型:
- 一级是服务,UserService
- 二级是集群,例如杭州或上海
- 三级是实例,例如杭州机房的某台部署了UserService的服务器
如何设置实例的集群属性:
- 修改application.yml文件的
spring.cloud.nacos.discovery.cluster-name
属性
3 服务实例的权重设置
通过设置服务实例的权重,可以调节服务的请求量,可以用在服务升级时(用户无感知)。值为0~1
4 环境隔离
- namespace用来做环境隔离
- 每个namespace都有唯一id
- 不同namespace下的服务不可见
5 Nacos与Eureka的异同
- 相同点
- 都支持服务注册和服务拉取
- 都支持服务提供者心跳方式做健康检测
- 区别
- Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
- 临时实例心跳不正常会被删除,非临时实例则不会被剔除
- Nacos支持服务列表的消息推送模式,服务列表更新更及时
- Nacos集群默认采用AP方式,当急群众存在非临时实例时,采用CP模式;Eureka采用AP方式
6 Nacos配置管理
将配置交给Nacos管理的步骤:
- 在Nacos中添加配置文件
- 在微服务中引入nacos的config依赖
- 在微服务中添加bootstrap.yml,配置nacos地址、当前环境、服务名称、文件后缀名。这些决定了程序启动时去nacos读取哪个文件。
7 配置热更新
热更新:不需要重启服务,就能更改配置文件
两种方式:
- 通过@Value注解注入,结合 在类上面使用@RefreshScope
- 通过@ConfigurationProperties注入,自动刷新
注意事项:
- 不是所有的配置都适合放到配置中心,维护起来比较麻烦。只放经常变动的
- 建议将一些关键参数,需要运行时调整的参数放到nacos配置中心,一般都是自定义配置
Feign
为了解决RestTemplate的发送HTTP请求不优雅的问题,使用Feign优雅的发送HTTP请求。
1 使用步骤
-
引入依赖
<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); }
-
使用
@Service public class OrderService { @Autowired private OrderMapper orderMapper; @Autowired private RestTemplate restTemplate; @Autowired private UserClient userClient; public Order queryOrderById(Long orderId) { // 1.查询订单 Order order = orderMapper.findById(orderId); // String url = "http://userservice/user/" + order.getUserId(); // User user = restTemplate.getForObject(url, User.class); User user = userClient.findById(order.getUserId()); order.setUser(user); // 4.返回 return order; } }
-
启动服务,访问 http://localhost:8080/order/102,测试成功。自动负载均衡,集成了Ribbon
2 日志配置
两种方式
- 配置文件,feign.client.config.xxx.loggerLevel
- 如果xxx是default则代表全局
- 如果xxx是服务名称,例如UserService则代表某服务
- java代码配置Logger.Level这个Bean
- 如果在@EnableFeignClients注解声明则代表全局
- 如果在@FeignClient注解中声明则代表某服务
网关
作用:
- 对用户请求做身份认证、权限校检
- 将用户请求路由到微服务,并实现负载均衡
- 对用户请求做限流
用户发送请求,网关和自己的配置文件做对比,看符合哪一个路由规则。找到对应的微服务,从注册中心拉取服务列表,做负载均衡,发送请求给对应的服务。
1 网关搭建的步骤
-
创建项目,引入依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud-demo</artifactId> <groupId>cn.itcast.demo</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>gateway</artifactId> <dependencies> <!-- nacos 服务注册返现依赖--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> </dependencies> </project>
-
配置application.yml,包括服务基本信息、nacos地址、路由
server: port: 8085 spring: application: name: gateway cloud: nacos: server-addr: localhost:8848 gateway: routes: - id: user-service # 路由id 路由的唯一标识,不能重复 uri: lb://userservice # 路由uri,路由的目标地址,http代表固定地址,lb表示根据服务名负载均衡 predicates: # 路由断言,判断路由规则, - Path=/user/** - id: order-service uri: lb://orderservice predicates: - Path=/order/**
2 网关过滤器
过滤器的作用?
-
对路由的请求或响应做加工处理,比如添加请求头
-
配置在路由下的过滤器只对当前路由的请求生效
-
配置
server: port: 8085 spring: application: name: gateway cloud: nacos: server-addr: localhost:8848 gateway: routes: - id: user-service uri: lb://userservice predicates: - Path=/user/** filters: - # xxxxxx 添加相应的过滤器,并且只对当前的路由起作用 - id: order-service uri: lb://orderservice predicates: - Path=/order/** default-filters: - #xxxx,对全局路由起作用
defaultFilters的作用是什么?
- 对所有路由都生效的过滤器
3 全局过滤器
GlobalFilter
自定义类,实现GlobalFilter接口,添加@Order注解:
package cn.itcast.gateway;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @author yongheng98
* @date 2022/12/29
* @time 15:03
*/
@Component
@Order(-1)
public class Filter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取请求参数
MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
String authorization = params.getFirst("authorization");
// 校检请求参数
if ("admin".equals(authorization)) {
return chain.filter(exchange);
}
// 设置状态码
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
// 拦截
return exchange.getResponse().setComplete();
}
}
作用:
- 对所有路由都生效的过滤器,并且可以自定义处理逻辑(区别于defaultfilters)。
实现全局过滤器的步骤:
- 实现GlobalFilter接口
- 添加@Order注解或实现Ordered接口编写处理逻辑
4 过滤器的执行顺序
- order值越小,优先级越高
- 当order值相同时,顺序时defaultFilter最先,然后是局部的路由器过滤器,最后是全局的过滤器。
5 跨域问题
Docker
【待补充】
MQ
同步调用优点:
- 时效性强,可以立即得到结果
同步调用的问题:
- 耦合度高
- 性能和吞吐能力下降
- 有额外的资源消耗
- 有级联失败问题
异步通信的优点:
- 耦合度低
- 吞吐量提升
- 故障隔离
- 流量削峰
异步通信的缺点:
- 依赖于Broker的可靠性、安全性、吞吐能力
- 架构复杂了,业务没有明显的流程线,不好追踪
1 RabbitMQ
docker 运行rabbitmq
docker run -d --hostname my-rabbit --name rabbit -p 15672:15672 -p 5673:5672 rabbitmq
进入容器内部执行
rabbitmq-plugins enable rabbitmq_management
访问 http://linuxip:15672
用户名和密码默认都是guest
输入命令:exit退出容器目录.
几个基础概念:
- channel:操作MQ的工具
- exchange:路由消息到队列中
- queue:缓存消息
- virtual host:虚拟主机,是对queue、exchange等资源的逻辑分组
端口:通信用的是5672,控制台用的是15672
2 SpringAMQP
发布者端设置
-
引入SpringAMQP的依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
-
在publisher服务中编写application.yml,添加mq连接信息
spring: rabbitmq: host: localhost # 主机名 port: 5672 # 端口 virtual-host: / # 虚拟主机 username: guest # 用户名 password: guest # 密码
-
在publisher服务中新建一个测试类,编写测试方法
消费者端设置
- 引入amqp的starter依赖
- 配置RabbitMQ地址
- 定义类,添加@Component注解
- 类中声明方法,添加@RabbitListener注解,方法参数就是消息。
注意:消息一旦消费就会从队列中删除,RabbitMQ没有消息回溯功能。
通过调整application的prefetch来控制消费者预取的消息数量。
3 交换机Exchange
作用:
- 接收publisher发送的消息
- 将消息按照规则路由到与之绑定的队列
- 不能缓存消息,路由失败,消息丢失
- FanoutExchange的会将消息路由到每个绑定的队列
声明队列、交换机、绑定关系的Bean是什么?
- Queue
- FanoutExchange
- Binding
DirectExchange:通过队列的key,决定将消息发送给哪个queue。
注解式声明Queue、Binding、DirectExchange
TopicExchange
4 消息转换器
SpringAMQP中的消息的序列化和反序列化是怎么实现的?
-
利用MessageConverter实现的,默认是JDK的序列化
-
注意发送方与接收方必须使用相同的MessageConverter。
-
使用
Spring中默认的MessageConverter是基于JDK序列化,自己指定一个基于json的MessageConverter,覆盖Spring的默认实现。
ElasticSearch
一个开源的分布式搜索引擎,可以用来实现搜索、日志统计、分析、系统监控等功能。
什么是Elastic stack,ELK
- 是以ElasticSearch为核心的技术栈,包括beats、Logstash、kibana、ElasticSearch
什么是Lucene
- 是Apache的开源搜索引擎类库,提供了搜索引擎的核心API
正向索引和倒排索引:
- 正向索引:基于id,查找词条。查询词条时,必须先找到文档,而后判断是否包含词条。
- 倒排索引:基于词条定位id。对文档内容分词,对词条创建索引,并记录词条所在文档信息。查询时先根据词条查询到文档id,而后获取到文档。
基础概念
- 文档:ES是面向文档存储的,可以是数据库中的一条商品数据,一个订单信息。
- 文档数据会被序列化为json格式后存储在ElasticSearch中
- 索引:相同类型的文档的集合。
- 映射:索引中文档的字段约束信息,类似表的结构约束。
与Mysql对比:
- Mysql:更擅长事务类型的操作,可以确保数据的安全性和一致性
- ElasticSearch:擅长海量数据的搜索、分析、计算
安装、使用。。。。跳过
面试篇
1 常见的SpringCloud组件有哪些
- 注册中心组件:Eureka、Nacos等
- 负载均衡组件:Ribbon
- 远程调用组件:OpenFeign
- 网管组件:Zuul、Gateway
- 服务保护组件:Hystrix、Sentinel
- 服务配置管理组件:SpringCloudConfig、Nacos
2 Nacos的服务注册表结构
Nacos采用了数据的分级存储模型,最外层是NameSpace,用来隔离环境。然后是Group,用来对服务分组。接下来就是服务(Service)了,一个服务包含多个实例,但是可能处于不同机房,因此Service下有多个集群(Cluster),Cluster下是不同的实例(Instance)。
对应到Java代码中,Nacos采用了一个多层的Map来表示。结构为Map<String, Map<String, Service>> ,其中最外层的Map的key就是namespaceId,值是一个Map。内层的Map的key是group拼接serviceName,值是Service对象。
Service对象内部又是一个Map,key是集群名称,值是Cluster对象。而Cluster对象内部维护了Instance的集合。
标签:服务,spring,Nacos,技术,cloud,order,路由 From: https://www.cnblogs.com/Sun-yuan/p/17047969.html