学习笔记-微服务基础(黑马程序员)
框架
- spring cloud
- spring cloud alibaba
Eureka
- eureka-server
- 注册中心
- eureka-client
- 客户端
- 每30s发送心跳
- 服务
- 服务消费者
- 服务提供者
server
依赖
org.springframework.cloud spring-cloud-starter-netflix-eureka-server
启动类
- 添加注解
- @EnableEurekaServer
配置文件
- application.yml
server: port: 10086 # 端口号 spring: application: name: eurekaserver # eureka服务名称 eureka: client: service-url: # eureka的地址,需要将自己注册到eureka中 defaultZone: http://127.0.0.1:10086/eureka
client
依赖
org.springframework.cloud spring-cloud-starter-netflix-eureka-client
配置文件
- application.yml
spring: application: name: userservice # 需要注册的服务名称 eureka: client: service-url: # 本服务地址,需要注册到eureka中 defaultZone: http://127.0.0.1:10086/eureka
服务拉取和负载均衡
- 添加注解
- @LoadBlanced
@Bean @LoadBlanced // 负载均衡 public RestTemplate restTemplate(){ return new RestTemplate(); }
- 修改url
// restTemplate请求 String url = "http://userservice/xxx";
Ribbon负载均衡
自定义负载均衡策略
1、定义新的IRule,将轮询策略(默认)变成随机策略
(图片来源网络,侵删)@Bean public IRule randomRule(){ return new RandomRule(); }
2、配置文件方式
userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务 ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
饥饿加载
-
默认懒加载
- 第一次访问时才会去创建LoadBalanceClient
-
开启饥饿加载
ribbon: eager-load: enabled: true clients: userservice
Nacos
依赖
- 父工程
com.alibaba.cloud spring-cloud-alibaba-dependencies 2.2.6.RELEASE pom import
- 客户端
com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery
spring: cloud: nacos: server-addr: localhost:8848
配置集群
优先选择本地集群
- 添加集群
spring: cloud: nacos: server-addr: localhost:8848 discovery: cluster-name: xx # 集群名称
- 修改负载均衡规则
userservice: ribbon: NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则
配置权重
- nacos控制台->实例列表->编辑->修改权重
环境隔离
- Namespace
- Group
- service/data
配置namespace
- 需要新建namespace
- 修改配置
spring: cloud: nacos: server-addr: localhost:8848 discovery: cluster-name: xx namespace: xxxx # 命名空间,填ID
永久实例
- 临时实例
- 果实例宕机超过一定时间,会从服务列表剔除
- 非临时实例/永久实例
- 实例宕机,不会从服务列表剔除
spring: cloud: nacos: discovery: ephemeral: false # 设置为非临时实例
配置管理
- data id
- xxx-dev.yaml
- group
- default_group
- 配置内容
- 需要热更新的配置有必要放到nacos管理
- 基本不会变更的一些配置保存在微服务本地
依赖
com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config
bootstrao.yaml
spring: application: name: userservice # 服务名称 profiles: active: dev #开发环境 cloud: nacos: server-addr: localhost:8848 # Nacos地址 config: file-extension: yaml # 文件后缀名
配置热更新
-
方式一
(图片来源网络,侵删)- 添加注解
- @RefreshScope
-
方式二
- @ConfigurationProperties代替@Value
@Component @Data @ConfigurationProperties(prefix = "xx") public class Xxxx { private String xx; }
配置共享
-
创建[servename].yaml
- 多环境共享
-
配置文件组成
- [spring.application.name]-[spring.profiles.active].yaml
- 运行环境
- [spring.application.name].yaml
- 公共
配置优先级
- [spring.application.name]-[spring.profiles.active].yaml
- [spring.application.name].yaml
- 本地配置
feign
使用
依赖
org.springframework.cloud spring-cloud-starter-openfeign
Application添加注解
- @EnableFeignClients
客户端
@FeignClient("xxxservice") public interface XxxClient { }
自定义配置
- 区分全局和单个服务
- feign.client.config.xxservice.loggerLevel
- xxservice服务
- feign.client.config.default.loggerLevel
- 全局
- feign.Logger.Level
- 修改日志级别
- NONE
- BASIC
- HEADERS
- FULL
- feign.codec.Decoder
- 响应结果的解析器
- http远程调用的结果做解析
- 解析json字符串为java对象
- feign.codec.Encoder
- 请求参数编码
- 将请求参数编码
- 便于通过http请求发送
- feign.Contract
- 支持的注解格式
- 默认是SpringMVC的注解
- feign.Retryer
- 失败重试机制
使用优化
- 日志级别尽量用basic
- HttpClient或OKHttp代替URLConnection
- Feign底层发起http请求
- URLConnection
- 默认实现,不支持连接池
- Apache HttpClient
- 支持连接池
- OKHttp
- 支持连接池
替换为httpclient
- 依赖
io.github.openfeign feign-httpclient
- 配置文件
feign: client: config: default: # default全局的配置 loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息 httpclient: enabled: true # 开启feign对HttpClient的支持 max-connections: 200 # 最大的连接数 max-connections-per-route: 50 # 每个路径的最大连接数
最佳实践
- 将Feign的Client抽取为独立模块
- 并且把接口有关的POJO、默认的Feign配置都放到这个模块中
- 在服务生产者、消费者中引入该模块
- 指定扫描接口
- @EnableFeignClients(clients = {XXXClient.class})
- @EnableFeignClients(basePackages = “xxx.clients”)
gateway
- 功能
- 身份认证和权限校验
- 服务路由、负载均衡
- 请求限流
搭建
- 依赖
org.springframework.cloud spring-cloud-starter-gateway com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery
- 启动类
@SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } }
- 路由规则
server: port: 10010 # 网关端口 spring: application: name: gateway # 服务名称 cloud: nacos: server-addr: localhost:8848 # nacos地址 gateway: routes: # 网关路由配置 - id: user-service # 路由id,自定义,只要唯一即可 # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址 uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称 predicates: # 路由断言,也就是判断请求是否符合路由规则的条件 - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
断言工厂
- 断言Path=/xx/**
- org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory处理
- 其他
- 时间断言
- After
- Before
- Between
- 请求
- Cookie
- Header
- Host
- Method
- Path
- Query
- RemoteAddr
- 权重
- Weight
过滤工厂
-
种类
- AddRequestHeader
- 给当前请求添加一个请求头
- RemoveRequestHeader
- 移除请求中的一个请求头
- AddResponseHeader
- 给响应结果中添加一个响应头
- RemoveResponseHeader
- 从响应结果中移除有一个响应头
- RequestRateLimiter
- 限制请求的流量
-
局部添加
spring: cloud: gateway: routes: - id: user-service uri: lb://userservice predicates: - Path=/user/** filters: # 过滤器 - AddRequestHeader=xx, xxxxx # 添加请求头
- 全局添加
spring: cloud: gateway: routes: - id: user-service uri: lb://userservice predicates: - Path=/user/** default-filters: # 默认过滤项 - AddRequestHeader=xx, xxxxx
自定义全局过滤器
@Order(-1) @Component public class AuthorizeFilter implements GlobalFilter { @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 1.获取请求参数 MultiValueMap params = exchange.getRequest().getQueryParams(); // 2.获取authorization参数 String auth = params.getFirst("authorization"); // 3.校验 if ("admin".equals(auth)) { // 放行 return chain.filter(exchange); } // 4.拦截 // 4.1.禁止访问,设置状态码 exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN); // 4.2.结束处理 return exchange.getResponse().setComplete(); } }
过滤器顺序
- 过滤器必须指定一个order值
- order值越小,优先级越高,执行顺序越靠前
- order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行
跨域
spring: cloud: gateway: # 。。。 globalcors: # 全局的跨域处理 add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题 corsConfigurations: '[/**]': allowedOrigins: # 允许哪些网站的跨域请求 - "http://localhost:8090" allowedMethods: # 允许的跨域ajax的请求方式 - "GET" - "POST" - "DELETE" - "PUT" - "OPTIONS" allowedHeaders: "*" # 允许在请求中携带的头信息 allowCredentials: true # 是否允许携带cookie maxAge: 360000 # 这次跨域检测的有效期
docker
-
架构
- 镜像
- image
- 容器
- container
-
dockerhub
- 镜像托管平台
-
docker
- client
- 向Docker服务端发送指令
- server
- Docker守护进程
-
镜像名称
-
命令
- systemctl start docker
- systemctl stop docker
- systemctl restart docker
- docker -v
- docker --help
-
镜像命令
- docker pull 镜像
- docker push 镜像
- docker rmi 镜像
- docker images
- docker save -o xxx 镜像
- docker load -i xxx
-
容器命令
- docker run --name 容器名 -p 宿主机port:容器内port -d 镜像
- d:后运行
- docker pause
- docker unpause
- docker start
- docker stop
- docker logs -f 容器名
- f:持续
- docker ps
- docker exec -it 容器名 bash
- it:进去当前容器创建标准输入输出终端
- bash:进入后执行命令
- docker rm -f 容器名
数据卷
-
volume
- 虚拟目录,指向宿主机文件系统中的某个目录
-
命令
- docker volume create
- docker volume inspect
- docker volume ls
- docker volume prune
- docker volume rm
-
挂载
- docker run -name mn -v xxxx:/xx/xx -p xx:xx xxx
- 宿主机目录可直接挂载
dockerfile
- 镜像结构
- 入口:镜像应用启动命令
- 层layer
- 基础镜像
- dockerfile
- 指令
- FROM
- ENV
- COPY
- RUN
- EXPOSE
- ENTRYPOINT
dockercompose
- 功能
- 快速的部署分布式应用,无需手动一个个创建和运行
MQ
- MQ
- RabbitMQ
- ActiveMQ
- RocketMQ
- Kafka
RabbitMQ
- publisher:生产者
- consumer:消费者
- exchange个:交换机,负责消息路由
- queue:队列,存储消息
- virtualHost:虚拟主机,隔离不同租户的exchange、queue、消息的隔离
消息模型
- 基本消息队列
- 工作消息队列
- 发布订阅
- 广播
- 路由
- 主题
demo
ConnectionFactory factory = new ConnectionFactory(); // 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码 factory.setHost("192.168.150.101"); factory.setPort(5672); factory.setVirtualHost("/"); factory.setUsername("itcast"); factory.setPassword("123321"); // 1.2.建立连接 Connection connection = factory.newConnection(); // 2.创建通道Channel Channel channel = connection.createChannel(); // 3.创建队列 String queueName = "simple.queue"; channel.queueDeclare(queueName, false, false, false, null); // 4.发送消息 String message = "hello, rabbitmq!"; channel.basicPublish("", queueName, null, message.getBytes()); System.out.println("发送消息成功:【" + message + "】"); // 5.关闭通道和连接 channel.close(); connection.close();
// 1.建立连接 ConnectionFactory factory = new ConnectionFactory(); // 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码 factory.setHost("192.168.150.101"); factory.setPort(5672); factory.setVirtualHost("/"); factory.setUsername("itcast"); factory.setPassword("123321"); // 1.2.建立连接 Connection connection = factory.newConnection(); // 2.创建通道Channel Channel channel = connection.createChannel(); // 3.创建队列 String queueName = "simple.queue"; channel.queueDeclare(queueName, false, false, false, null); // 4.订阅消息 channel.basicConsume(queueName, true, new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { // 5.处理消息 String message = new String(body); System.out.println("接收到消息:【" + message + "】"); } }); System.out.println("等待接收消息。。。。");
SpringAMQP
- 父工程依赖
org.springframework.boot spring-boot-starter-amqp
- 消息发送
spring: rabbitmq: host: localhost # 主机名 port: 5672 # 端口 virtual-host: / # 虚拟主机 username: xxx # 用户名 password: xxx # 密码
@Autowired private RabbitTemplate rabbitTemplate; @Test public void testSimpleQueue() { // 队列名称 String queueName = "simple.queue"; // 消息 String message = "hello, spring amqp!"; // 发送消息 rabbitTemplate.convertAndSend(queueName, message); }
- 消息接收
spring: rabbitmq: host: localhost # 主机名 port: 5672 # 端口 virtual-host: / # 虚拟主机 username: xxx # 用户名 password: xxx # 密码
@RabbitListener(queues = "simple.queue") public void listenSimpleQueueMessage(String msg) throws InterruptedException { System.out.println("spring 消费者接收到消息:【" + msg + "】"); }
WorkQueue
让多个消费者绑定到一个队列,共同消费队列中的消息
- 消息发送
public void testWorkQueue() throws InterruptedException { // 队列名称 String queueName = "simple.queue"; // 消息 String message = "hello, message_"; for (int i = 0; i
- 消息接收
@RabbitListener(queues = "simple.queue") public void listenWorkQueue1(String msg) throws InterruptedException { System.out.println("消费者1接收到消息:【" + msg + "】" + LocalTime.now()); Thread.sleep(20); } @RabbitListener(queues = "simple.queue") public void listenWorkQueue2(String msg) throws InterruptedException { System.err.println("消费者2........接收到消息:【" + msg + "】" + LocalTime.now()); Thread.sleep(200); }
- 取消消息预取
spring: rabbitmq: listener: simple: prefetch: 1 # 每次只能获取一条消息,处理完成才能获取下一个消息
发布订阅
fanout exchange
广播,将消息交给所有绑定到交换机的队列
- 声明队列和交换机
@Configuration public class FanoutConfig { /** * 声明交换机 * @return Fanout类型交换机 */ @Bean public FanoutExchange fanoutExchange(){ return new FanoutExchange("itcast.fanout"); } /** * 第1个队列 */ @Bean public Queue fanoutQueue1(){ return new Queue("fanout.queue1"); } /** * 绑定队列和交换机 */ @Bean public Binding bindingQueue1(Queue fanoutQueue1, FanoutExchange fanoutExchange){ return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange); } /** * 第2个队列 */ @Bean public Queue fanoutQueue2(){ return new Queue("fanout.queue2"); } /** * 绑定队列和交换机 */ @Bean public Binding bindingQueue2(Queue fanoutQueue2, FanoutExchange fanoutExchange){ return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange); } }
- 发布
// 队列名称 String exchangeName = "itcast.fanout"; // 消息 String message = "hello, everyone!"; rabbitTemplate.convertAndSend(exchangeName, "", message);
- 订阅
@RabbitListener(queues = "fanout.queue1") public void listenFanoutQueue1(String msg) { System.out.println("消费者1接收到Fanout消息:【" + msg + "】"); } @RabbitListener(queues = "fanout.queue2") public void listenFanoutQueue2(String msg) { System.out.println("消费者2接收到Fanout消息:【" + msg + "】"); }
Direct
定向,把消息交给符合指定routing key 的队列
- 订阅
- 发布
- 声明队列和交换机
- 取消消息预取
- 消息接收
- 消息发送
- 消息接收
- 消息发送
- 父工程依赖
- MQ
- 快速的部署分布式应用,无需手动一个个创建和运行
- 功能
- 指令
- 镜像结构
-
- docker run --name 容器名 -p 宿主机port:容器内port -d 镜像
-
- client
- 镜像
-
- 全局添加
- AddRequestHeader
-
- Weight
- 时间断言
- 断言Path=/xx/**
- 路由规则
- 启动类
- 依赖
- 功能
- 配置文件
- 依赖
- 支持连接池
- URLConnection
- Feign底层发起http请求
- 失败重试机制
- 支持的注解格式
- 请求参数编码
- 响应结果的解析器
- 修改日志级别
- feign.client.config.xxservice.loggerLevel
- 区分全局和单个服务
- @EnableFeignClients
- 公共
- [spring.application.name]-[spring.profiles.active].yaml
-
- @ConfigurationProperties代替@Value
- 添加注解
-
- data id
- 实例宕机,不会从服务列表剔除
- 临时实例
- service/data
- Group
- Namespace
- nacos控制台->实例列表->编辑->修改权重
- 修改负载均衡规则
- 添加集群
- 客户端
- 父工程
-
- 修改url
- @LoadBlanced
- 添加注解
- application.yml
- application.yml
- @EnableEurekaServer
- 添加注解
- eureka-server