springcloud笔记

Eureka——服务注册与发现

搭建EurekaServer

1、创建项目,引入依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

2、编写启动类,添加@EnableEurekaServer注解

3、全局配置:

server:
  port: 7001
spring:
  application:
    name: "eureka-server" #eureka实例名
eureka:
  instance:
    hostname: localhost #eureka实例的主机名
  client:
    register-with-eureka: false #false表示不向注册中心注册自己。
    fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #单机版服务注册中心

服务注册

1、创建项目,引入依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

2、全局配置:

spring:
  application:
    name: "user-service" #eureka实例名
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka #单机版服务注册中心

服务发现

1、修改调用者代码,修改访问的url路径,用服务名代替ip、端口:

String url = "http://user-service/user/" + userId;

2、在RestTemplate添加负载均衡注解:

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

Nacos——服务注册与发现

安装Nacos

去github下载,解压

进入bin目录,启动

startup.cmd -m standalone

打开管理网页localhost:8848,账号密码都是nacos

快速入门

1、在父POM的 dependencyManagement 中指定spring-cloud-alibaba的版本

<dependencyManagement>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    <version>2.2.9.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>
</dependencyManagement>

2、在要注册的服务POM中,引入依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

3、全局配置

spring:
  application:
    name: user-service #服务实例名
  cloud:
    nacos:
      server-addr: localhost:8848 #nacos服务地址

4、启动nacos-server,启动要注册的服务(不用写注解)

Nacos服务分级存储模型

服务分级存储模型:

服务——集群——实例

如何设置实例的集群属性:

spring:
  application:
    name: user-service #服务实例名
  cloud:
    nacos:
      server-addr: localhost:8848 #nacos服务地址
      discovery:
        cluster-name: HZ #集群名

NacosRule负载均衡

order-service配置:

spring:
  application:
    name: order-service #服务实例名
  cloud:
    nacos:
      server-addr: localhost:8848 #nacos服务地址
      discovery:
        cluster-name: HZ
user-service: #要调用的服务名
  ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule #指定负载均衡策略规则类

user-service配置:

spring:
  application:
    name: user-service #服务实例名
  cloud:
    nacos:
      server-addr: localhost:8848 #nacos服务地址
      discovery:
        cluster-name: SH

负载均衡策略:

(1)优先选择同集群服务实例列表

(2)本地集群找不到,才去其他集群找

(3)确定了可用实例列表后,再采用随机负载均衡挑选实例

设置服务实例的权重

在Nacos控制台修改

环境隔离

用namespace来做环境隔离;

不同namespace下的服务不可见。

新建namespace:在Nacos控制台

实例加入到namespace:

spring:
  application:
    name: user-service #服务实例名
  cloud:
    nacos:
      server-addr: localhost:8848 #nacos服务地址
      discovery:
        cluster-name: SH
        namespace: sdsdd-wee-22 #命名空间id

Nacos——配置管理

Nacos实现配置管理

在Nacos控制台上操作,假设我们创建了一个配置文件叫userservice-dev.yaml,后面会用到。

微服务配置拉取

1、引入Nacos的配置管理客户端依赖:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

2、在resources下创建bootstrap.yml文件,这个文件是引导文件,优先级高于application.yml

spring:
  application:
    name: userservice #服务名称
  profiles:
    active: dev #开发环境
  cloud:
    nacos:
      server-addr: localhost:8848
      config:
        file-extension: yaml #文件名后缀

2.1、SpringBoot2.4之后不会默认加载bootstrap.yaml,如果需要加载bootstrap.yaml文件需要手动添加依赖

<!--SpringBoot2.4.x之后默认不加载bootstrap.yml文件,需要在pom里加上依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

3、在微服务中,像使用全局配置文件一样使用配置:

@Value("${data.name}")
private String name;

4、启动程序时,就会读到Nacos上的配置了。

配置热更新

Nacos中修改配置后,微服务无需重启就可以感知,但是需要通过下面配置实现:

方式一:在@Value注入的变量所在类上添加注解@RefreshScope

方式二:使用@ConfigurationProperties注解

@Data
@Component
@ConfigurationProperties(prefix = "data")
public class DataProperties {
    private String name;
}

多环境配置共享

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

[spring.application.name]-[spring.profiles.active].yaml,可以存放特定环境的配置

[spring.application.name].yaml,可以存放不同环境共同的配置

多种配置的优先级:

[服务名]-[环境].yaml > [服务名].yaml > 本地配置

Feign——远程调用

入门

1、引入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

2、添加@EnableFeignClients注解

@EnableFeignClients
@SpringBootApplication
public class OrderServiceApplication {}

3、编写FeignClient接口

@FeignClient("userservice")
public interface UserClient {
    @GetMapping("/user/{id}")
    String findById(@PathVariable("id") Integer userId);
}

4、使用FeignClient中定义的方法代替RestTemplate

@Autowired
private UserClient userClient;

@GetMapping("{userId}")
public String getOrderByUserId(@PathVariable("userId") Integer userId) {
    String user = userClient.findById(userId);//这里
    return String.format("【%s】的订单", user);
}

自定义配置

配置Feign日志:

方式一:配置文件方式

feign:
  client:
    config:
      default: #default表示对所有service的调用生效,也可以写具体的服务名
        loggerLevel: BASIC

方式二:java代码

需要先声明一个bean:

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

如果是全局配置,则把它放到启动类的注解中:

@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)

如果是局部配置,则把它放到FeignClient接口的注解中:

@FeignClient(value = "userservice", configuration = FeignClientConfiguration.class)

Gateway——网关

入门

1、引入依赖

<!--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>

2、配置

server:
  port: 10010
spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: localhost:8848
    gateway:
      routes:
        - id: user-service #路由标识
          uri: lb://userservice #路由的目标地址
          predicates: #路由断言,判断请求是否符合规则
            - Path=/user/**
        - id: order-service
          uri: lb://orderservice
          predicates:
            - Path=/order/**

路由断言工厂

配置中的predicates就是路由断言,Spring提供了11种基本的Predicate工厂:

Spring Cloud Gateway

网关过滤器

对进入网关的请求和微服务返回的响应做处理,例如:

AddRequestHeader,给请求添加请求头

RemoveRequestHeader,移除请求中的请求头

AddResponseHeader,给响应结果中添加一个响应头

RemoveResponseHeader,从响应结果中移除一个响应头

RequestRateLimiter,限制请求的流量

例子:针对某个路由

spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: localhost:8848
    gateway:
      routes:
        - id: user-service #路由标识
          uri: lb://userservice #路由的目标地址
          predicates: #路由断言,判断请求是否符合规则
            - Path=/user/**
          filters:
            - AddRequestHeader=YourName,ZhangCan #这里

例子:针对所有路由

spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: localhost:8848
    gateway:
      routes:
        - id: user-service #路由标识
          uri: lb://userservice #路由的目标地址
          predicates: #路由断言,判断请求是否符合规则
            - Path=/user/**
          filters:
            - AddRequestHeader=YourName,ZhangCan
        - id: order-service
          uri: lb://orderservice
          predicates:
            - Path=/order/**
      default-filters:
        - AddRequestHeader=YourName,ZhangCan #这里

全局过滤器GlobalFilter

写代码实现过滤逻辑

例子:实现接口+提供组件

@Order(-1)//指定过滤器顺序,值越小越优先
@Component
public class AuthorizeFilter implements GlobalFilter {
    @Override
    public Mono<Void> 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();
    }
}

过滤器链执行顺序

  • 每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高
  • GlobalFilter通过@Order注解指定order值
  • 路由过滤器和defaultFilter的order由Spring指定,默认按照声明顺序从1递增
  • 当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter 的顺序执行

网关CORS跨域配置

只需配置即可:

spring:
  cloud:
    gateway:
      globalcors: # 全局的跨域处理
        add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
        cors-configurations:
          '[/**]':
            allowedOrigins: # 允许哪些网站的跨域请求
              - "http://localhost:8090"
              - "http://www.abc.com"
            allowedMethods: # 允许的请求方法
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*" # 允许在请求中携带的头信息
            allowedCredentials: true # 是否允许携带cookie
            maxAge: 360000 # 这次跨域检测的有效期

Docker

数据卷命令

docker volume [command]

create:创建

inspect:显示某个volume的信息

ls:列出所有volume

prune:删除未使用的volume

rm:删除volume

自定义镜像

Dockerfile文件:

FROM java:8-alpine

COPY ./docker-demo.jar /tmp/app.jar

EXPOSE 8080

ENTRYPOINT java -jar /tmp/app.jar

创建镜像:

docker build -t javaweb:1.0 .

创建容器:

docker run --name mj -d -p 8080:8080 javaweb:1.0

RabbitMQ

入门

拉取镜像:

docker pull rabbitmq:3-management

运行容器:

docker run \
 -e RABBITMQ_DEFAULT_USER=zc \
 -e RABBITMQ_DEFAULT_PASS=123321 \
 --name mq \
 --hostname mq1 \
 -p 15672:15672 \
 -p 5672:5672 \
 -d \
 rabbitmq:3-management

打开管理网页:

http://localhost:15672

消息模型

  • Basic Queue:基本消息队列
  • Work Queue:工作消息队列
  • 发布订阅,根据交换机类型不同分为三种:
    • Fanout Exchange:广播
    • Direct Exchange:路由
    • Topic Exchange:主题

Basic Queue

引入AMQP依赖:

<!--AMQP依赖,包含rabbitmq-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

在publisher服务中配置mq:

spring:
  rabbitmq:
    host: localhost
    port: 5672
    virtual-host: /
    username: zc
    password: 123321

编写publisher代码:

@SpringBootTest
public class SpringAMQPTest {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void sendMessage2SimpleQueue() {
        String queueName = "simple.queue";
        String message = "hello, spring amqp!";
        rabbitTemplate.convertAndSend(queueName, message);
    }
}

在consumer服务中配置mq:

同publisher

在consumer中新建一个类,编写消费逻辑:

@Component
public class SpringRabbitListener {
    @RabbitListener(queues = "simple.queue")
    public void listenSimpleQueue(String msg) {
        System.out.println("消费者接收到消息:【" + msg + "】");
    }
}

运行consumer主程序即可接收到消息

Work Queue

本质是多个消费者绑定到一个queue,同一个消息只会被一个消费者处理。

消费者默认预取的消息数量是无限,可以配置它。

Fanout Exchange 广播

会把消息广播到所有绑定的queue中。

在consumer中声明交换机和队列,并绑定它们:

@Configuration
public class FanoutConfig {
    //交换机
    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange("itcast.fanout");
    }

    //队列1
    @Bean
    public Queue fanoutQueue1() {
        return new Queue("fanout.queue1");
    }

    //绑定队列1到交换机
    @Bean
    public Binding fanoutBinding1(Queue fanoutQueue1, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
    }

    //队列2
    @Bean
    public Queue fanoutQueue2() {
        return new Queue("fanout.queue2");
    }

    //绑定队列2到交换机
    @Bean
    public Binding fanoutBinding2(Queue fanoutQueue2, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
    }
}

在consumer中分别监听2个队列:

@Component
public class SpringRabbitListener {
    @RabbitListener(queues = "fanout.queue1")
    public void listenFanoutQueue1(String msg) {
        System.out.println("消费者接收到fanout.queue1的消息:【" + msg + "】");
    }

    @RabbitListener(queues = "fanout.queue2")
    public void listenFanoutQueue2(String msg) {
        System.out.println("消费者接收到fanout.queue2的消息:【" + msg + "】");
    }
}

在publisher中发送消息给交换机:

    @Test
    public void sendMessage2FanoutExchange() {
        String exchangeName = "itcast.fanout";
        String message = "hello, every one";
        rabbitTemplate.convertAndSend(exchangeName, "", message);
    }

Direct Exchange 路由

会把消息根据规则路由到指定的queue,因此被称为路由模式。

  • 每个queue都与exchange设置一个BindingKey
  • 发送消息时,会指定消息的RoutingKey
  • exchange只会把消息转发到绑定key相同的queue

在consumer中利用@RabbitListener声明Exchange, Queue, RoutingKey:

@Component
public class SpringRabbitListener {
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue("direct.queue1"),
            exchange = @Exchange(value = "itcast.direct", type = ExchangeTypes.DIRECT),
            key = {"red", "blue"}
    ))
    public void listenDirectQueue1(String msg) {
        System.out.println("消费者接收到direct.queue1的消息:【" + msg + "】");
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue("direct.queue2"),
            exchange = @Exchange(value = "itcast.direct", type = ExchangeTypes.DIRECT),
            key = {"red", "yellow"}
    ))
    public void listenDirectQueue2(String msg) {
        System.out.println("消费者接收到direct.queue2的消息:【" + msg + "】");
    }
}

在publisher中发送消息:

@Test
    public void sendMessage2DirectExchange() {
        String exchangeName = "itcast.direct";
        String message = "hello, blue";
        rabbitTemplate.convertAndSend(exchangeName, "blue", message);
    }

Topic Exchange 主题

TopicExchange与DirectExchange类似,区别在于routingKey必须是多个单词的列表,并且以.分割。

通配符:

  • #:代指0个或多个单词
  • *:代指一个单词

在consumer中利用@RabbitListener声明Exchange, Queue, RoutingKey:

@Component
public class SpringRabbitListener {
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue("topic.queue1"),
            exchange = @Exchange(value = "itcast.topic", type = ExchangeTypes.TOPIC),
            key = "china.#"
    ))
    public void listenTopicQueue1(String msg) {
        System.out.println("消费者接收到topic.queue1的消息:【" + msg + "】");
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue("topic.queue2"),
            exchange = @Exchange(value = "itcast.topic", type = ExchangeTypes.TOPIC),
            key = "#.news"
    ))
    public void listenTopicQueue2(String msg) {
        System.out.println("消费者接收到topic.queue2的消息:【" + msg + "】");
    }
}

在publisher中发送消息:

@Test
    public void sendMessage2TopicExchange() {
        String exchangeName = "itcast.topic";
        String message = "一条中文消息";
        rabbitTemplate.convertAndSend(exchangeName, "china.news", message);
    }

消息转换器

spring的默认消息转化器会将消息对象进行序列化,然后发送。

如果要修改只需要定义一个MessageConverter类型的bean即可。推荐用JSON方式序列化。

引入依赖:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

声明MessageConverter:

@Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }

接收消息:

@Component
public class SpringRabbitListener {
    @RabbitListener(queues = "object.queue")
    public void listenObjectQueue(Map<String, Object> msg) {
        System.out.println("消费者接收到object.queue的消息:" + msg);
    }
}

发送消息:

@Test
    public void sendMessage2ObjectQueue() {
        Map<String, Object> message = new HashMap<>();
        message.put("name", "小明");
        message.put("age", 21);
        rabbitTemplate.convertAndSend("object.queue", message);
    }

Sentinel——流量治理

入门

1、去github release下载网页控制台的jar包,运行即可:

java -jar sentinel-dashboard-1.8.6.jar

打开localhost:8080

2、微服务整合sentinel

引入依赖:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

配置:

spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080

访问微服务的任意端点,触发sentinel监控

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值