微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

这篇具有很好参考价值的文章主要介绍了微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

认识微服务

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

SpringCloud和Dubbo是微服务方案的实现
微服务技术对比
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

SpringCloud 和SpringBoot版本兼容需要对应

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

(左侧是SpringCloud的版本,右侧SpringBoot版本。两者版本需要一一对应,否者可能出现兼容性问题)
(此笔记基于SpringCloud Hopxton.SR10和SpringBoot2.3.x进行记录)

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

SpringCloud

SpringCloud快速项目搭建

父工程搭建

父工程负责控制所有微服务的统一版本依赖管理,不负责微服务业务的依赖。

  1. 新建一个空白maven项目(,删除src目录,因为用不上)
    微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

  2. pom.xml

    <?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">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.xz.springcloud</groupId>
        <artifactId>springcloud002</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>pom</packaging>
    
        <!--统一版本管理-->
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
            <spring.version>2.3.9.RELEASE</spring.version>
            <spring-cloud.version>Hoxton.SR10</spring-cloud.version>
            <mysql.version>8.0.29</mysql.version>
            <lombok.version>1.18.24</lombok.version>
        </properties>
    
        <!--所有子项目再次引入此依赖jar包时则无需显式的列出版本号。
        Maven会沿着父子层级向上寻找拥有dependencyManagement 元素的项目,然后使用它指定的版本号。-->
        <dependencyManagement>
            <dependencies>
                <!--spring boot 版本-->
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-dependencies</artifactId>
                    <version>${spring.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
                <!--spring cloud 版本-->
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
    
                <!--mysql版本-->
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>${mysql.version}</version>
                </dependency>
    
                <!--lombok 版本-->
                <dependency>
                    <groupId>org.projectlombok</groupId>
                    <artifactId>lombok</artifactId>
                    <version>${lombok.version}</version>
                </dependency>
            </dependencies>
        </dependencyManagement>
        
        <!--打包用的,这样每个子工程就不用写了-->
        <build>
            <!--app.jar 统一指定生成的名字,方便docker构建镜像-->
            <finalName>app</finalName>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <executions>
                        <execution>
                            <goals>
                                <!--可以把依赖的包都打包到生成的Jar包中-->
                                <!--不写这个生成的jar很小,且无法执行,缺少依赖-->
                                <goal>repackage</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </project>
    

Eureka

Eureka是SpringCloud的一个组件
注册中心

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

  1. Eureka分为Eureka-server服务端和Eureka-client客户端
  2. 每个微服务都是一个eureka-client客户端
  3. 每个服务启动时都会向注册中心进行注册服务,保存自己的信息
  4. 每个eureka客户端都会向注册中心发送心跳,感知提供者健康状况
  5. 消费者根据服务名称向eureka拉取提供者的信息

EurekaServer搭建

EurekaServer——注册中心
注册中心搭建

  1. 在父工程项目中,New——Module——新的maven项目
    微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

  2. 修在pom.xml配置

    <?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>springcloud002</artifactId>
            <groupId>com.xz.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>eureka-server</artifactId>
    
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <!--eureka-服务端-->
                 <!--因为在父工程声明了版本,这里不用声明了-->
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            </dependency>
        </dependencies>
    </project>
    
  3. 编写Eureka服务端启动类(在eureka-server项目下编写)
    微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

    package com.xz.eureka;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
    
    /**
     * @author xzlyf
     * @date 2022/9/12 17:44
     */
    @EnableEurekaServer
    @SpringBootApplication
    public class EurekaApplication {
        public static void main(String[] args) {
            SpringApplication.run(EurekaApplication.class, args);
        }
    }
    
  4. 编写Eureka服务端yml配置文件(在eureka-server项目下编写)
    微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

    server:
      port: 8888 #服务端口
    spring:
      application:
        name: eruekaserver #服务的名字(eureka本身也是一个服务,微服务之间通过名字相互调用)
    eureka:
      client: #服务的注册,指向注册中心地址,eureka本身也是一个服务,在启动时把自己也注册进注册中心
        service-url: #eureka的地址信息
          defaultZone: http://127.0.0.1:8888/eureka
    

EurekaClient搭建

使用EurekaClient把服务注册进注册中心,或者把已存在的服务进行注册

  1. 在父工程下搭建一个新的服务UserServer,以及编写springboot启动类和配置类等等。就是一个普通的springboot web工程。

    这个启动类不需要加@EnableEurekaServer
    微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

  2. pom配置文件

    <?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>springcloud002</artifactId>
            <groupId>com.xz.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>user-server</artifactId>
    
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
        </properties>
    
        <dependencies>
            <!--spring boot web -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            
            <!--...其它业务相关依赖...-->
            
            <!--eureka client 客户端-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
        </dependencies>
    </project>
    
  3. yml配置文件

    server:
      port: 8080
    
    # ... 其它业务相关配置 ...
    
    spring:
      application:
        name: userserver
    eureka:
      client: #服务的注册,指向注册中心地址
        service-url:
          defaultZone: http://127.0.0.1:8888/eureka
    
  4. 重启服务后可以在注册中心看到服务已经注册进来了
    微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

远程调用微服务

eureka注册中心可以根据服务名拉取服务列表,然后在对服务列表做负载均衡。

也就是各个服务之间根据服务名进行调用API

每个微服务通过暴露接口来给消费者调用数据。

各个接口使用Resultful风格进行暴露

微服务之间使用RestTemplate工具进行调用外部接口

示例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kBTqt7bi-1664193921860)(https://gitee.com/xzlyfcc/pic-cloudstack/raw/master/image-20220912194654739.png)]

在userserver业务中,使用RestTemplate工具远程调用order服务中的数据
使用服务名代替对方的ip端口进行请求

其中RestTemplate需要自行注入
@LoadBalanced 负载均衡,ribbon组件实现

@Bean
@LoadBalanced //开启负载均衡,如果该服务存在多台实例中,将会使用负载均衡。
public RestTemplate restTemplate() {
    return new RestTemplate();
}

Ribbon

Ribbon是SpringCloud的一个组件,负责负载均衡

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

使用注解@LoadBalanced开启Ribbon负载均衡

当RestTemplate使用注解@LoadBalanced标记时,表明这个restTemplate发起的请求将被Ribbon拦截和处理。

@Bean
@LoadBalanced //开启负载均衡,如果该服务存在多台实例中,将会使用负载均衡。
public RestTemplate restTemplate() {
    return new RestTemplate();
}

负载均衡策略

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

修改负载均衡策略

  1. 第一种方式:在微服务中注入IRule对象,并修改实现类(对应实现类查看上面负载均衡策略)

    @Bean
    public IRule rule(){
        return new RandomRule();
    }
    

    需要注意的是,这种配置方式是全局(仅这个微服务中),配置之后这个微服务下的RestTemplate负载均衡策略都会生效。

  2. 第二种方式:配置文件方式。指定某个微服务的负载均衡方式

    例如在order-service服务中的yml配置文件,修改了user-service请求负载均衡策略
    order-service.yml:

    # 请求userservice服务ribbon使用RandomRule策略
    userservice:
    	ribbon:
    		NFLoadBalancerRuleClassName: com.netfilx.loadbalancer.RandomRule #负载均衡规则
    # 请求cartService服务ribbon使用RandomRule策略
    cartService:
    	ribbon:
    		NFLoadBalancerRuleClassName: com.netfilx.loadbalancer.RetryRule
    

饥饿加载

Ribbon默认采用懒加载,即第一次访问时才会去创建LoadBalanceClient,导致第一次请求时间过长。
而饥饿加载则会在项目启动时创建,降低第一次访问的消耗。

示例:

在需要开启饥饿加载的微服务中配置yml

ribbon:
	eager-load:
		enabled: true #开启饥饿加载
		clients:
		- userservice #指定对xxxService(对方的服务,不是自己)这个服务饥饿加载

Nacos

SpringCloud一个组件,也是一个注册中心,但比Eureka功能更加丰富。
不仅可以作为注册中心,也可以作为配置中心

Nacos是阿里巴巴的产品

Nacos注册中心

安装指南

Nacos需要安装,并独立启动

1、Windows安装方式
前往Github下载
https://github.com/alibaba/nacos/releases

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

2、解压出来

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

3、启动

进入bin目录,执行命令

standalone 单机启动

startup.cmd -m standalone

也可以直接点击startup.cmd启动

4、启动成功

登录地址:http://192.168.7.1:8848/nacos/index.html#/login

Nacos默认端口为8848,可conf/application.properties中配置新的端口
默认登录密码都是nacos

注册服务

把服务注册进注册中心

示例:

1、在父工程的pom.xml加入依赖
加入Spring Cloud Alibaba依赖,因为Nacos是阿里巴巴的,Nacos的注册发现的依赖需要被它管理

<dependencyManagement>
    <dependencies>
        <!--spring boot 依赖版本管理-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--spring cloud 依赖版本管理-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--spring cloud alibaba 依赖版本管理-->
        <!--引入阿里巴巴的springcloud依赖即可使用nacos,因为nacos是阿里巴巴的-->
        <!--因为阿里巴巴的组件是后来出的,并不在原生spring cloud dependencies依赖中,所以需要单独引入-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${spring-cloud-alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

     	...其它业务代码...
    </dependencies>
</dependencyManagement>

2、在需要注册的微服务pom.xml中配置nacos客户端

<dependencies>
    <!--spring boot 业务-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Nacos客户端-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    ...其它业务依赖...
</dependencies>

3、修改微服务yml配置文件

spring:
  application:
    name: orderservice  #服务名
  cloud:
    nacos:
      server-addr: localhost:8848  #指向nacos注册中心地址

4、前往Nacos注册中心管理面板可以发现服务已近被注册进来

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

5、远程调用与Eureka的一样,使用服务名和RestTemplate进行远程调用。
Eureka切换成Nacos非常简单,只需要在父工程加入springcloudAlibaba的管理依赖和在客户端注释掉Eureka的依赖即可,并且在客户端配置文件配置Nacos注册中心地址。
Nacos注册中心服务端并不需要创建一个子项目来启动,直接下载启动器,在/nacos/bin目录下独立启动Nacos注册中心即可。

集群配置

修改微服务之yml配置

spring:
	cloud:
		nacos:
			service-addr: localhost:8848
			discovery:
				cluster-name: SZ #集群名字,可以自定义

集群名字可以自定义,相同名字表示微服务示例部署在同一个集群。

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

服务调用尽可能选择本地集群服务,跨集群调用延迟高
本地集群不可访问时,再访问其它集群。

负载均衡

配置集群后,不同地域可以通过配置Ribbon的IRule策略,让其优先访问本地集群。当本地集群出现异常时才切换至外域集群。

配置yml,与前面讲到的Ribbon相同,指定服务名并指定IRule策略

userservice:
	ribbon:
		NFLoadBalanceRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule #这里改为NacosRule

NacosRule负载均衡策略

  1. 优先选择同集群服务实例列表
  2. 本地集群找不到提供者,才去其它集群,并且报警告
  3. 确定了可用实例后,再采取随机负载均衡挑选实例

权重配置

Nacos提供了权重配置来控制访问频率,权重越大则访问频率越高

通过控制台修改权重

当权重等于0,流量将不会分配给该实例

环境隔离
  • namespace用来做环境隔离
  • 每个namespace都有唯一ID
  • 不同namespace下的服务不可见

把服务加入命名空间,如果不加入,默认在public这个命名空间下

spring:
	cloud:
		nacos:
			service-addr: lcoalhost:8848
			discovery:
				namespace: d43264f8-3bc9-4030-a280-65dd7ba97df3 #加入到自己创建的命名空间

不同命名空间的服务相互不可见,也就是说,不能相互调用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rbrznZRJ-1664193921862)(https://gitee.com/xzlyfcc/pic-cloudstack/raw/master/image-20220913174739620.png)]

临时实例

服务注册到Nacos时,可以选择注册为临时实例或非临时实例
默认所有服务注册都为临时实例
通过配置修改为非临时实例

spring:
	cloud:
		nacos:
			discoery:
				ephmeral: false #设置为非临时实例,默认为true(临时实例)

临时实例和非临时实例区别:

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

区别在于临时实例采用的心跳机制和eureka一致,由服务提供者向注册中心发出。
非临时实例心跳机制由注册中心发出,心跳间隔更短,但感知服务提供者挂掉了,会主动向服务消费者推送变更消息,更新服务列表缓存。
并且服务提供者挂掉后,并不会在注册中心剔除掉,转为一种等待保护状态,直至服务重启成功。在此期间该服务提供者不会收到任何流量。

Nacos注册中心与Eureka注册中心的主要区别

Nacos配置中心

Nacos不仅可以作为注册中心,还有配置中心的功能

配置发布

向微服务发布配置文件

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

配置内容不是填写一切内容,而是一些支持热更新的内容

读取配置

bootstrap.yml是一个引导文件,这个文件执行优先级高于application.yml
在这个文件里配置nacos地址和配置文件信息,从而在执行Application.yml前获取新的配置文件,然后进行配置文件合并

1、客户端需要配置Nacos配置管理依赖

<!-- Nacos 配置管理依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

2、在服务中的resource目录下添加一个bootstrap.yml文件,用于引导

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

上面三个关键名对应统一配置发布中心的Data ID(即配置中心发布的配置文件名)
[服务名]-[profile].[后缀名]
userservice-dev.yaml

上面配置表示:从localhost:8080的nacos服务器获取名为 userservice-dev.yaml 的配置文件

3、当服务启动时,便会向Nacos读取配置文件,合并配置文件。

配置热更新

Nacos中配置文件变更后,服务无需重启服务器即可实现更新。
两种方式配置:

方式一、

在需要读取配置所在的类上加上注解:@RefreshScope
例如:
在UserController.java类上,通过@Value读取了配置的一个变量,
而此时可以通过在该变量所在的类上加上注解
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

而此时在配置中心发布新的配置信息可以马上更新到变量,无需重启

方式二、常用

使用注解**@ConfigurationProperties**
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

表示会热更新读取配置文件的 pattern.dateformat 属性,dateformat会自动注入数据

多配置优先级

微服务启动时,读取配置的优先级。

三种配置文件,优先级从高到低:
服务名-profile.yaml
服务名.yaml
本地配置.yaml

其中,服务名-profile.yaml和服务名.yaml存在Nacos配置中心,称为远端配置,
本地配置是指服务里面的application.yml配置文件。

当存在相同的配置属性时,本地配置优先级最低,远端配置(服务名-profile.yaml)优先级最高。

profile一般是指开发环境,即自定义标识,一般指dev、test、release等

示例:

当存在两个服务名一致的配置文件时,并且服务指定了profile,那就是userservice-dev.yaml的优先级最高
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

如果服务中的bootstarp.yml没有指定prifile,将会使用userservice.yaml这个配置,并不会读取userservice-dev.yaml

集成搭建

步骤:

  1. 搭建Mysql集群并初始化数据库表
  2. 下载解压nacos
  3. 修改集群配置(节点信息)、数据库配置
  4. 分别启动多个nacos节点
  5. nginx反向代理

Feign

http客户端feign,使用feign代替RestTemplate。

RestTemplate方式远程调用存在的问题:

  • 代码可读性差
  • 参数复杂难以维护

Feign是一个声明式的http客户端,其作用就是帮助我们优雅的实现http请求的发送。

并且Feign集成了负载均衡(Ribbon实现)的功能。

基本使用

使用步骤:

  1. 引入依赖
  2. 添加@EnableFeignClients注解
  3. 编写FeignClient接口
  4. 使用FeignClient中定义的方法代替RestTemplate

使用教程:

1、在服务中引入依赖

<!--Feign客户端-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2、在服务启动类上开启Feign功能

@EnableFeignClients //开启Feign功能
@SpringBootApplication
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class, args);
    }
}

3、声明一个远程调用,创建远程调用客户端

定义一个接口,使用@FeignClient注解表示将调用orderservice服务中的接口,
@GetMapping是SpringMVC的注解,表示该接口使用Get方式请求,对应PostMapping POST请求等等。
Feign会自动封装返回的数据类型

package com.xz.userserver.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.Map;
@FeignClient("orderservice") //调用orderservice服务的接口,
public interface OrderClient {

    //调用orderservice服务中的query接口
    //使用RestFul风格进行传参
    //Feign会自动封装返回数据的类型,数据字段一一对应就好了。
    @GetMapping("/order/query/{id}")
    Map<String,Object> query(@PathVariable("id") String id);
}

4、使用Feign客户端

在需要远程调用的业务上,直接注入OrderClient即可调用

@Service
public class UserService {
    @Autowired
    private OrderClient orderClient;

    public Object query(String id){
        return orderClient.query(id);
    }
}

自定义配置

Feign运行自定义配置来覆盖默认配置

配置日志

日志级别:NONE、BASIC、HEADERS、FULL

方式一、配置文件修改

feign:
	client:
		config:
			default: #default表示全局,指定服务名则是针对某个服务生效
				loggerLevel: FULL #日志级别

方式二、配置类修改

定义一个配置类,但是不需要加@Configurable注解

public class FeignLoggerConfig{
    @Bean
    public Logger.Level feignLogLevel(){
        return Logger.Level.FULL;//日志级别
    }
}

如果全局生效,在@EnableFeignClients注解(启动类)上修改。

@EnableFeignClients(defaultConfiguration = FeignLoggerConfig.class)

如果要局部生效,在需要生效的FeignClient客户端注解上修改

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

性能优化

日志级别最好使用NONE或BASIC,否则影响性能

Feign底层客户端默认是使用JDK自带的URLConnection实现,不支持连接池。
可以通过修改换成Apache HttpClient 或 OkHttp,这两者都支持连接池。

使用HttpClient作为底层请求

1、引入依赖

<!--httpClient依赖-->
<dependency>
	<groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

2、配置文件

feign:
	client:
		config:
			default: #default表示全局,指定服务名则是针对某个服务生效
				loggerLevel: FULL #日志级别
	httpclient:
		enable: true #开启feign对HttpClient的支持
		max-connections: 200 #最大连接数
		max-connections-per-route: 50 #每个路径最大连接数

网关Gateway

网关功能:

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

SpringCloud提供了两种网关实现,SpringCloud Gateway 和 Zuul

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

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

SpringCloud Gateway

搭建网关

1、新建一个module,maven工程。用于搭建网关
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

2、引入网关相关依赖
网关也是作为一个服务,需要向Nacos注册中心进行注册,所以需要引入nacos 客户端

<dependencies>
    <!-- 网关依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <!-- Nacos客户端 用于把网关服务注册-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
</dependencies>

3、编写启动类

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

4、配置yml
需要配置nacos注册中心的地址,以及配置网关路由

在网关路由里配置不同服务的路由
每一个服务都应该有一个id,并且唯一
并且需要配置路由规则predicates(断言)

server:
  port: 10010 #网关端口
spring:
  application:
    name: gateway #服务名称
  cloud:
    nacos:
      server-addr: localhost:8848 #nacos地址,把网关注册进去
    gateway:
      routes: #网关配置
        - id: user-service #路由id,自定义,唯一。指向某个服务
          uri: lb://userservice #服务的地址(注册中心的服务名),使用lb开头表示开启负载均衡,也可以使用http指定服务地址
          predicates: #路由断言,判断请求是否符合规则,符合就放行
            - Path=/user/** #判断路径是以/user开头,符合就放行
        - id: order-service #第二个服务,可以配置多个服务
          uri: lb://orderservice
          predicates:
            - Path=/order/**

5、调用
客户端通过访问网关,不再让客户端直接访问微服务,并通过路径规则来访问不同的微服务

例如:http://localhost:10010/user/test
来访问userservice的服务中的/user/test接口

路由断言

Route Predicate Fatory 路由断言工厂

例如Path = /user/**是按照路径(Path)匹配
而Path就是其中一个断言工厂

断言工厂有很多个,默认就是使用Path断言工厂来判断
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UMC1OiMZ-1664193921864)(https://gitee.com/xzlyfcc/pic-cloudstack/raw/master/image-20220915155850650.png)]

示例:

gateway:
      routes: #网关配置
        - id: order-service #第二个服务
          uri: lb://orderservice
          predicates:
            - Path=/order/**
            - After=2031-01-20T17:42:47.789-07:00[Asia/Shanghai]

这个路由加了两个断言判断规则
一个是Path,表示请求路径,如果符合才能放行
另一个是After,表示是某个时间点之后,如果符合才能放行
要想访问这个服务,必须同时满足上面两个断言。
就算Path路径符合规则,但时间并不符合,也不能访问成功

断言规则可以加很多个

路由过滤器

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

SpringCloud Gateway有31种路由过滤工厂
参看文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories

示例1、

给某个服务的请求添加一个请求头,test=Helloworld

spring:
	cloud:
		gateway:
			routes:
			  - id: order-service #第二个服务
				uri: lb://orderservice
                predicates:
                  - Path=/order/**
                  - After=2018-01-20T17:42:47.789-07:00[Asia/Shanghai]
				filter:
                  - AddRequestHeader=test,Helloworld

其中AddRequestHeader就是一个过滤工厂,用于请求头修改。后面跟了两个参数,用逗号隔开,前面表示key,后面表示value。

示例2
上面的示例1配置是针对某个路由的,下面将演示针对所有路由

spring:
	cloud:
		gateway:
			routes:
			  - id: order-service #第二个服务
				uri: lb://orderservice
                predicates:
                  - Path=/order/**
			default-filters:
				- AddRequestHeader=test,Helloworld

这种配置方式可以对所有路由生效

全局过滤器

上面的过滤例子都是yml写死的,固定的。而通过全局过滤(GlobalFilter接口)来实现动态的逻辑变更

注意这种过滤方式会对所有路由进行过滤

示例

请求者用户身份验证
获取请求者的请求参数:author,判断其值是否等于admin。

1、在网关下新建一个AuthorFilter类,并实现GlobalFilter接口

@Order(1)   //过滤器优先级,越小优先级越高。不写的话优先级非常低
@Component  //把该类进行自动装配
public class AuthorFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.获取请求参数
        MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
        //2.获取 “author”参数
        String key = queryParams.getFirst("author");
        //3.校验
        if (key != null && key.equals("admin")) {
            //4.放行,如果存在下一个过滤器,则跳转到下一个过滤器。根据@Order优先级
            return chain.filter(exchange);
        }
        //5.拦截
        //5.1设置返回状态码,这里UNAUTHORIZED就是401状态码,一般用于身份未验证的提示
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        //5.2结束处理
        return exchange.getResponse().setComplete();
    }
}

2、测试
未加入author参数:http://localhost:10010/order/test
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

加入author参数:http://localhost:10010/order/test?author=admin
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

过滤器执行顺序

局部路由过滤器、defaultFilter、GlobalFilter

  • order值越小,优先级越高
  • 当order值一样时,顺序是 defaultFileter > 局部路由过滤器 > GlobalFilter

Zuul

Spring Cloud 提供了基于Netflix Zuul 实现的API网关组件 Spring Cloud Zuul

搭建网关

1、在父工程引入Netflix依赖管理
因为Zuul输入Netflix的组件,前面并没有引入Netflix依赖管理,现在需要引入

<!--spring cloud Netflix 版本依赖管理-->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-netflix-dependencies</artifactId>
            <version>${spring-cloud-netflix.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

2、新建一个module,作为zuul网关模块
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

3、引入spring cloud zuul依赖

<dependencies>
    <!--zuul 网关-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>
    <!--nacos 客户端 把网关注册进注册中心-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
</dependencies>

4、新建启动类

@EnableZuulProxy
@SpringBootApplication
public class ZuulApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class, args);
    }
}

5、配置yml,配置路由规则和nacos服务注册等

server:
  port: 10011 #网关地址
spring:
  application:
    name: zuul #服务名称
  cloud:
    nacos:
      server-addr: localhost:8848 #注册中心地址,把该服务注册进注册中心
## 配置zuul的路由规则
## 访问路由网关/api-a/**,则将请求分发到对应的serviceId: userservice
## 访问路由网关/api-v/**,则将请求分发到对应的serviceId: orderservice
zuul:
  routes:
    user-service:             #路由名字,自定义
      path: /api-a/**         #路由路径 /api-a是自定义的,后面**是匹配规则
      serviceId: userservice  #指向的服务名
    order-service:
      path: /api-v/**
      serviceId: orderservice

通过访问:http://localhost:10011/api-v/order/test 可以访问到orderservice服务中的/order/test接口

路由配置方式

1、上面已经提到一种配置方式,同时指定未付的serviceId和路径

zuul:
  routes:
    user-service:             #路由名字,自定义
      path: /api-a/**         #路由路径 /api-a是自定义的,后面**是匹配规则
      serviceId: userservice  #指向的服务名

2、指向单实例服务URL和路径
这种方式不用指定服务名,但是指向固定的服务地址
访问url为:http://localhost:10011/api-a/user/test

zuul:
  routes:
    user-service:
      url: http://localhost:8080
      path: /api-a/**

3、简洁配置
这里userservice指向微服务的名称,/user/**表示访问路径
访问url为:
http://localhost:10011/user/user/test
http://localhost:10011/api-b/test

zuul:
  routes:
    userservice: /user/**
    orderservice: /api-b/**

4、路由前缀配置
这种配置会给所有路由加上前缀/api
访问url为:http://localhost:10011/api/api-v/

zuul:
  prefix: /api
  routes:
    userservice: /api-a/**

5、本地跳转
例如在zuul网关项目中存在一个接口/local,通过forward进行跳至zuul网关项目中进行处理
下面例子表示处理路径/api-b/**的路径跳转至本地的local方法
访问url为:http://localhost:10010/api-b/local/test

zuul:
 routes:
 	custom-name:
		path: /api-b/**
 		url:forward: /local
@RestController
public class GateWayTestController {
    @RequestMapping("/local/test")
    public String serverInfo() {
        return "这里是zuul-gateway";
    }
}
忽略服务

在默认不配置任何路由的情况下, zuul都可以通过***(网关:端口/服务名/接口)来访问服务。
这可能会使得服务不那么安全,所有需要通过配置
忽略服务
*
例如:在没有配置userservice服务和没有忽略该服务时,可以通过http://localhost:10011/userservice/user/test来访问到该服务

1、忽略所有服务(一般用这个用得多)
使用ignored-services :*来忽略所有服务,并指定一个路由跳转值orderservice
这样除了orderservice都不能通过网关来访问了
访问orderservice的url为:http://localhost:10011/api-v/order/test

zuul:
  ignored-services: '*' # 使用'*'可忽略所有微服务
  routes:
    orderservice: /api-v/**

2、忽略单个或多个服务

zuul:
  ignored-services: userservice,orderservice,...
过滤器

Zuul允许开发者在API 网关上通过定义过滤器来实现对请求的拦截与过滤,实现方法比较简单,只需要继承ZuulFilter抽象类并实现抽象方法即可

1、编写过滤器

在zuul项目下新建一个类,集成ZuulFilter实现抽象方法
方法介绍:

  • filterType() 过滤器的类型,决定过滤器什么时候执行
  • filterOrder() 相同过滤器类型执行的优先级,越小越优
  • shouldFilter() 该过滤器的开关,决定该过滤器是否执行
  • run() 过滤器业务逻辑处理
package com.xz.zuul.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.http.HttpStatus;

import javax.servlet.http.HttpServletRequest;

/**
 * @author xzlyf
 * @date 2022/9/16 17:07
 */
public class AuthorFilter extends ZuulFilter {
    /**
     * 返回该过滤器的类型,决定了该过滤器什么时候执行
     *
     * @return pre - 前置过滤器,在请求被路由前执行,通常用于处理身份认证,日志记录等;
     * route - 在路由执行后,服务调用前被调用;
     * error - 任意一个filter发生异常的时候执行或远程服务调用没有反馈的时候执行(超时),通常用于处理异常;
     * post - 在route或error执行后被调用,一般用于收集服务信息,统计服务性能指标等,也可以对response结果做特殊处理
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 同类型过滤器执行优先级
     * 返回值越小,执行顺序越优先
     */
    @Override
    public int filterOrder() {
        return 1;
    }

    /**
     * 返回true执行,返回false 不执行。
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 过滤器的具体业务逻辑。
     */
    @Override
    public Object run() throws ZuulException {
        //1、获取请求体
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        //2、获取请求参数author
        String author = request.getParameter("author");
        //3、身份校验
        if (author != null && author.equals("admin")) {
            //4、通过
            return null;
        }
        //5、失败,拦截
        requestContext.setSendZuulResponse(false);
        requestContext.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
        return null;
    }
}

2、把过滤器注入到Spring容器,交给spring管理即可

@EnableZuulProxy
@SpringBootApplication
public class ZuulApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class, args);
    }

    /**
     * 注入过滤器到容器
     */
    @Bean
    public AuthorFilter authorFilter() {
        return new AuthorFilter();
    }
}

3、测试

当开启过滤器后,需要携带参数author=admin才能正常访问到服务,注意该过滤器会对所有服务生效
访问url为:http://localhost:10011/api-v/order/test?author=admin
若不携带身份参数,便会返回http 401错误代码

Docker

Docker学习文档参考:https://blog.csdn.net/weixin_43418331/article/details/125352253

构建JDK环境镜像

基于Centos7系统,构建一个jdk8环境的镜像
准备一个jdk8安装包:https://www.oracle.com/java/technologies/downloads/#java8

1、将jdk8.tar.gz上传至docekr环境服务器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DfQQy7ga-1664193921866)(https://gitee.com/xzlyfcc/pic-cloudstack/raw/master/image-20220917152752604.png)]

2、编写dockerfile文件

# 指定基础镜像
FROM centos:7
# 配置环境变量,jdk安装目录
ENV JAVA_DIR=/usr/local

# 拷贝jdk安装包
COPY ./jdk-8u341-linux-x64.tar.gz $JAVA_DIR

# 安装JDK
RUN cd $JAVA_DIR \
	&& tar -xf ./jdk-8u341-linux-x64.tar.gz \
	&& mv ./jdk1.8.0_341 ./java8
	
# 配置环境变量
ENV JAVA_HOME=$JAVA_DIR/java8
ENV PATH=$PATH:$JAVA_HOME/bin

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

3、构建镜像

docker build -f dockerfile  -t jdk8:1.0 .

# 构建成功
Successfully built 0cdc681f0691
Successfully tagged jdk8:1.0

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

4、启动容器

# 前台启动
docker run -it --name java8 jdk8:1.0 /bin/bash

构建SpringBoot镜像

选择官方的java:8作为基础镜像,当然也可以选择上面自己创建jdk8镜像来作为底包

1、打包一个可以执行的springboot项目包
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

2、编写dockerfile文件

# 指定刚才生成的镜像作为底包
# 使用官方镜像java8作为底包
FROM java:8

# 复制项目jar到/tmp目录下,名为app.jar
COPY ./test-service-1.0-SNAPSHOT.jar /tmp/app.jar

# 暴露项目端口8080
EXPOSE 8080

# 启动容器时同时启动项目
ENTRYPOINT ["java","-jar","/tmp/app.jar"]

3、构建项目

docker build -f dockerfile  -t app:1.0 .
# 构建成功
Successfully built 904ef8269735
Successfully tagged app:1.0

4、启动项目

# 前台启动容器
docker run -it -p 8080:8080 --name app app:1.0 /bin/bash

5、通过访问宿主机8080端口进行访问项目

Docker Compose

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

Compose文件是一个yml格式的文件

安装

1、前往docker github 下载linux 版本的compose文件
https://github.com/docker/compose#linux

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R64FVXH3-1664193921867)(https://gitee.com/xzlyfcc/pic-cloudstack/raw/master/image-20220917164255943.png)]

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

2、将compose文件上传至服务器
存放目录为:/usr/local/bin
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

3、修改文件名为docker-compose

mv docker-compose-linux-x86_64 docker-compose

4、给compose加上可执行权限

chmod +x docker-compose

5、测试是否可用

# 查看docker-compose 版本
[root@localhost bin]# ./docker-compose -v
Docker Compose version v2.10.2

6、加入环境变量,这样不用每次进入该目录执行命令

# 修改环境变量文件
vi ~/.bash_profile 

# 加入
PATH=$PATH:$HOME/bin:$/usr/local/bin

# 是环境变量生效
source ~/.bash_profile

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

配置文件详解

Compose 文件格式有3个版本,分别为1,2.x,3.x,目前主流版本为3.x

docker-compose.yml示例参考

version: "3.2" #docker-compose版本

# 需要创建容器的列表
services:
	nacos: # 创建一个名为nacos的容器
		image: nacos/nacos-server #使用nacos/nacos-server:laster镜像创建
		environment: #环境变量配置
			Mode: standalone #nacos的环境变量,单机启动模式
		ports: #端口的映射
			- "8848:8848"
	mysql: # 创建第二个容器,名为mysql
		image: mysql:5.7.25 #使用mysql:5.7.25这个镜像创建
		environment: #mysql的环境变量配置,root的密码配置
			MYSQL_ROOT_PASSWORD: 123456
		volumes: #数据卷映射,$PWD是当前compose文件执行的路径,当然也可以写死。
			- "$PWD/mysql/data:/var/lib/mysql" #配置mysql数据的映射
			- "$PWD/mysql/conf:/etc/mysql/conf.d/"
	userservice: #创建第三个容器,自己写的微服务
		build: ./user-service #build命令表示当前还没有镜像,需要根据./user-service/dockerfile 构建文件进行构建镜像。构建镜像完成后便自动创建容器
	orderservice: #第四个容器...
		build: ./order-service
	gateway: #第五个容器
		build: ./gateway
		ports: #端口映射
			- "10010:10010"

上面使用compose文件批量构建镜像和创建容器。
除了nacos和gateway容器暴露端口,其余微服务都是以内部网络进行访问,提高安全性

build命令可以根据dockerfile构建文件构建镜像,并自动创建容器启动。
有进行的服务直接通过image命令创建容器

加上dockerfile文件使用

FROM java:8
COPY ./app.jar /tmp/app.jar
ENTRYPOINT ["java","-jar","/tmp/app.jar"]

项目结构
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

每个微服务文件夹都包含了一个dockerfile和app.jar文件

通过父工程批量打包app.jar

在父工程的pom统一修改打包的名字

把项目上传至服务执行
进入到docker-compose文件所在的目录

# 开启编排启动
docker-compose up -d

MQ异步通信

同步调用

优点:

  • 时效性强,可以立即得到结果

缺点:

  • 耦合度高
  • 性能和吞吐能力下降
  • 有额外的资源消耗
  • 有级联失败的问题

异步调用

优点:

  • 耦合度低
  • 吞吐量提升
  • 故障隔离
  • 流量削峰

缺点:

  • 依赖于Broker的可靠性、安全性、吞吐能力
  • 架构复杂了,业务没有明显的流程线,不好追踪管理

什么是MQ

MQ(MessageQueue),即消息队列。也就是事件驱动架构中的Broker。

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

MQ的实现方案
市面上MQ的实现方案有很多,下面列举了几个常用的方案,以及区别

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

RabbitMQ

RabbitMQ是基于Erlang语言开发的开源小兮通信中间件,官网:https://www.rabbitmq.com/

安装

Docker单机部署RabbitMQ

1、使用docker pull拉取rabbitmq官方镜像,以3.9版本为例

docker pull rabbitmq:3.9

2、启动容器

docker run \
	-e RABBITMQ_DEFAULT_USER=root \
	-e RABBITMQ_DEFAULT_PASS=123456 \
	--name mq \
	--hostname mq1 \
	-p 15672:15672 \
	-p 5672:5672 \
	-d \
	rabbitmq:3.9
	
#解释:
#RABBITMQ_DEFAULT_USER 后台登录用户名
#RABBITMQ_DEFAULT_PASS 后台登陆密码
#--name 容器名字
#--hostname mq1 用于集群配置的主机名
#-p 15672 rabbit后台管理页面的端口
#-p 5672  消息通信的端口
#-d 后台启动

3、正常进入15672后台管理页面
访问路径为:http://192.168.7.10:15672
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

常见问题处理

  1. 无法访问15672后台管理页面

    #1、进入mq容器
    docker exec -it mq /bin/bash
    #2、开启web界面管理插件命令
    rabbitmq-plugins enable rabbitmq_management
    
  2. Stats in management UI are disabled on this node

    #1、进入mq容器
    docker exec -it mq /bin/bash
    #2、cd到路径
    cd /etc/rabbitmq/conf.d/
    #3、修改 management_agent.disable_metrics_collector = false
    echo management_agent.disable_metrics_collector = false > management_agent.disable_metrics_collector.conf
    #4、退出容器
    exit 
    #5、重启rabbitmq容器
    docker restart {rabbitmq容器id或容器名称}
    

RabbitMQ结构

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

publisher 消息发布者
consumer 消息消费者
exchange 路由消息到队列
queue 消息队列,接收路由的消息,用于缓存消息,
virtual host 虚拟主机,对exchange、queue等资源进行隔离分组

5种消息模型

官方案例:https://www.rabbitmq.com/getstarted.html

1、简单模式
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)
基本消息模型就是:
一个生产者丶默认交换机丶一个队列丶一个消费者。

2、工作模式
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)
work消息模型就是:
一个生产者丶默认交换机丶一个队列丶多个消费者。

3、发布订阅模式
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

fanout消息模型就是:
多个消费者,每一个消费这都有自己的队列,每个队列都绑定到交换机
生产者发送消息到交换机-交换机发送到哪个队列

4、 路由模式
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

Routing路由模式模型就是:
在某种场景下,我们希望不同的消息被不同的队列消费
这个时候我们就要用到direct类型的exchange
生产者向交换机发送消息—交换机根据路由key发送给队列-队列的消费者接收消息

5、Topics主题模式
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

它可以通过RoutingKey,将交换机和队列机进行模糊匹配

简单模式案例演示

1、项目结构
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)
publisher 消息发布者
consumer 消息消费者

2、父工程引入rabbitMQ client 依赖

<!--dependencyManagement父工程对子工程依赖版本管理,子工程无须指定版本-->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.16.0</version>
        </dependency>
    </dependencies>
</dependencyManagement>

3、publisher.Send 发布消息代码

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.nio.charset.StandardCharsets;

public class Send {

    private final static String QUEUE_NAME = "hello";

    public static void main(String[] argv) throws Exception {
        //1、建立连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2、rabbitmq服务地址
        factory.setHost("192.168.7.10");
        //3、rabbitmq服务端口
        factory.setPort(5672);
        //4、使用的主机(主机隔离消息队列)
        factory.setVirtualHost("testVM");
        //5、操作主机的用户(该用户需要有testVM主机的操作权限)
        factory.setUsername("test");
        factory.setPassword("123456");
        //6、开始连接
        try (Connection connection = factory.newConnection();
             //7、创建消息队列,队列名为hello
             Channel channel = connection.createChannel()) {
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            String message = "Hello World!";
            //8、发送消息至队列,结束业务
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8));
            System.out.println(" [x] Sent '" + message + "'");
        }
    }
}

4、consumer.Recv 接收消息队列代码

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;
import java.nio.charset.StandardCharsets;

public class Recv {

    private final static String QUEUE_NAME = "hello";

    public static void main(String[] argv) throws Exception {
        //1、建立连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2、rabbitmq服务地址
        factory.setHost("192.168.7.10");
        //3、rabbitmq服务端口
        factory.setPort(5672);
        //4、使用的主机(主机隔离消息队列)
        factory.setVirtualHost("testVM");
        //5、操作主机的用户(该用户需要有testVM主机的操作权限)
        factory.setUsername("test");
        factory.setPassword("123456");
        //6、开始连接
        Connection connection = factory.newConnection();
        //7、创建通道(如果通道不存在则执行创建,若存在则跳过
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        System.out.println(" [*] Waiting for messages. To exit press CTRL+C");

        //8、等待消息到达
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
           	//9、接收消息
            String message = new String(delivery.getBody(), StandardCharsets.UTF_8);
            System.out.println(" [x] Received '" + message + "'");
        };
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });
    }
}

Spring AMQP

可以看到,RabbitMQ官方的API操作消息发送和接收非常繁琐。那么通过Spring AMQP 可以大大简化消息发送和接收的API

AMQP——Advanced Message Queueing Protocol。
是一种规范来的。用于在应用程序或之间传递业务消息的开放标准。该协议与语言和平台无关。

Spring AMQP 是基于AMQP协议定义的一套API规范,提供了模板来发送和接收消息。底层则是通过spring-rabbit来实现

简单模式案例演示

对比上面使用官方API发送消息和接收消息,代码简化了不少

1、项目结构
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

2、在父工程引入amqp的依赖,这样子工程就不用引入了

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.7.3</version>
    </dependency>
    <!--AMQP的依赖,springboot 自动装配-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
        <version>2.7.3</version>
    </dependency>
</dependencies>

3、application.yml配置rabbitmq通信地址(两个子项目都要配置)

spring:
  rabbitmq:
    host: 192.168.7.10  # RabbitMQ 服务地址
    port: 5672          # RabbitMQ 服务端口
    virtual-host: testVM # 主机,隔离不同消息队列
    username: test        #主机操作用户
    password: 123456     

4、Publisher.Send代码
rabbitTemplate.convertAndSend(queueName, message);这一行代码是关键,它可以自动把要发送的消息转换成字节码,发送到指定的消息队列。使用前只要注入RabbitTemplate工具即可

package com.xz.publisher.controller;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SendController {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @GetMapping("/")
    public Object index() {
        String queueName = "hello";
        String message = "hello:" + System.currentTimeMillis() + "";
        rabbitTemplate.convertAndSend(queueName, message);
        return "Ok";
    }
}

5、Consumer.Recv代码
通过使用rabbitTemplate.receiveAndConvert(queueName);来接收消息队列的数据,并自动转换

注意:这里演示的手动接收消息,实际开发中一般使用监听,监听到消息自动处理

package com.xz.consumer.controller;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RecvController {
    @Autowired
    RabbitTemplate rabbitTemplate;

    @GetMapping("/")
    public Object recv() {
        String queueName = "hello";
        String recv = (String) rabbitTemplate.receiveAndConvert(queueName);
        return recv;
    }
}

可以看到,通过spring amqp来处理发送消息和接收消息非常的简单。不用手动创建连接工厂,通过配置yml,让spring自动装配。

监听消息队列

实际开发种,一般使用监听消息来自动触发业务。

1、在项目中新建一个类,编写监听器

package com.xz.consumer.listener;

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component //注册为一个组件,归sping管理
public class RabbitQueueListener {
    //queues 指定队列名
    //String msg 发送消息时是什么格式,接收时也是什么格式
    @RabbitListener(queues = "hello")
    public void listenSimpleQueueMessage(String msg) throws InterruptedException {
        System.out.println("接收到[hello]队列消息:" + msg);
    }
}

这样不用手动接收了,当有队列里有消息,将会执行此方法。若队列存在多条消息,将按顺序一条条执行,直至处理完所有消息。

消费预取限制

当一个队列种存在多个消费者,就是5种消息模型种的工作模式。
rabbitMQ默认消费者可以预取很多消息,这会导致性能低的机器拿了很多消息在慢慢处理,性能高的机器处理完了所有消息在干等待。
解决这种问题就是限制预取

在yml种配置预取限制为1,表示每个消费者只能预取1个消息。

spring:
	rabbitmq:
		host: xxx
		port: 5672
		virtual-host: /
		username: xxx
		password: xxx
		listener:
			simple:
				prefetch: 1 #每次只能取一条消息,处理完才能获取下一个消息。

发布、订阅

上面的例子都是发布者直接发送消息到消息队列。
下面的是发布者发送消息到exchange(交换机),由交换机决定消息发送给哪个消息队列。
注意:交换机负责消息路由,而不是存储,路由失败则消息丢失。

常见的exchange类型:

  • Fanout:广播
  • Direct:路由
  • Topic:话题
Fanout Exchange

会将接收到的消息,路由给每一个跟其绑定的queue

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

演示:

1、在consumer消费者的项目种加入一个配置类,用于配置FanoutExchagne交换机
下面演示了声明一个fanout交换机,和两个queue队列。
两个队列绑定同一个交换机。
绑定工作都交给sping管理

这是其中一种声明交换机和队列的方式,还有一种方式是通过@RabbitListener来声明
配置式声明交换机和队列

package com.xz.consumer.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
@Configuration
public class FanoutExConfig {
    /*声明一个fanout exchange 交换机,名为:hello.exchange*/
    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange("hello.exchange");
    }
    /*声明一个队列,名为hello.queue1*/
    @Bean
    public Queue queue1() {
        return new Queue("hello.queue1");
    }
    /*将队列1绑定到交换机,spring会将刚才创建的交换机和队列绑定
    @Bean
    public Binding bindingQueue1(FanoutExchange fanoutExchange, Queue queue1) {
        return BindingBuilder.bind(queue1).to(fanoutExchange);
    }
    /*声明第二个队列,名为hello.queue2*/
    @Bean
    public Queue queue2() {
        return new Queue("hello.queue2");
    }
    /*将队列2绑定到交换机,它们绑定的是同一个交换机*/
    @Bean
    public Binding bindingQueue2(FanoutExchange fanoutExchange, Queue queue2) {
        return BindingBuilder.bind(queue2).to(fanoutExchange);
    }
}

2、在consumer消费者中监听两个队列测试

package com.xz.consumer.listener;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class RabbitQueueListener {
    /*监听第一个队列*/
    @RabbitListener(queues = "hello.queue1")
    public void listenerFanoutQueue1(String msg) throws InterruptedException{
        System.out.println("在queue1队列中收到消息:"+msg);
    }
    /*监听第二个队列*/
    @RabbitListener(queues = "hello.queue2")
    public void listenerFanoutQueue2(String msg) throws InterruptedException{
        System.out.println("在queue2队列中收到消息:"+msg);
    }
}

3、在publisher提供者中发送消息。
更以前的简单模式略有不同,这里是将消息发送至交换机,而不是队列了

@GetMapping("/fanout")
public Object sendFanout() {
    // 交换机名称
    String exchangeName = "hello.exchange";
    // 消息
    String msg = LocalTime.now().toString();
    //发送消息。交换机名称、RoutingKey(暂空)、消息
    rabbitTemplate.convertAndSend(exchangeName, "", msg);
    return "Ok";
}

4、测试

# 结果
在queue2队列中收到消息:19:36:17.371
在queue1队列中收到消息:19:36:17.371

当消息提供者发送了一次消息,两个消息队列都能监听到消息,并接收。
两个消息队列都会读取到消息。

Direct Exchange

会将接收到的消息根据规则路由到指定的Queue,因此称为路由模式

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

  • 每一个Queue都与Exchang设置一个BindingKey
  • 发布者发送消息时,指定消息的RoutingKey
  • Exchang将消息路由到BindingKey与消息RoutingKey一致的队列

演示:

1、直接使用@RabbitListener监听以及声明交换机和队列
下面通过注解直接声明交换机和队列,不再通过配置声明,代码减少了

注解声明交换机和队列

package com.xz.consumer.listener;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.security.Key;
@Component
public class RabbitQueueListener {

    /**
     * 声明监听一个队列
     * bindings:绑定
     *  exchange:交换机,和交换机类型
     *  value:队列
     *  key:路由关键值,可以接收多个,交换机根据此关键字转发到此队列
     */
    @RabbitListener(
            bindings = @QueueBinding(
                    exchange = @Exchange(value = "hello.direct",type = ExchangeTypes.DIRECT),
                    value = @Queue("direct.queue1"),
                    key = {"keyA","keyB"}
            )
    )
    public void listenerDirectQueue1(String msg){
        System.out.println("接收到队列1的消息:"+msg);
    }
    /**
     * 声明监听第二个队列
     */
    @RabbitListener(
            bindings = @QueueBinding(
                    exchange = @Exchange(value = "hello.direct",type = ExchangeTypes.DIRECT),
                    value = @Queue("direct.queue2"),
                    key = {"keyA","keyC"}
            )
    )
    public void listenerDirectQueue2(String msg){
        System.out.println("接收到队列2的消息:"+msg);
    }
}

2、publisher消息提供者
只需要指定路由key即可将消息发送到和绑定key一致的队列

@GetMapping("/direct")
public Object sendDirect(){
    // 交换机名称
    String exchangeName = "hello.direct";
    // 消息
    String msg = "1234567890---=";
    //发送消息
    rabbitTemplate.convertAndSend(exchangeName, "keyC", msg);
    return "Ok";
}

3、测试
当路由key为keyB时,direct.queue1队列可以收到消息
当路由key为keyC时,direct.queue2队列可以收到消息
当路由key为keyA时,direct.queue1、direct.queue2都可以收到消息

Topic Exchange

与DirectExchange类似,区别在于routingKey必须是多个单词的列表,并已 .(点)分割,
例如:
work.hot
work.hot.desc
work.hot.asce

使用通配符来决定单词数量:
每个单词使用 . 来分割

# : 表示0个或多个单词
* : 表示一个单词

示例:
new.#
可以匹配
new.a
new.rabbay.hot
new.rabbay.random

#.new
可以匹配
hot.new
desc.hot.new


new.*
可以匹配
new.a

*.new
可以匹配
hot.new

声明方法和使用和上面DirectExchange类似,只是routingKey值变了,这里就不掩饰了,参考上面例子。

消息序列化

SpringAMQP的消息默认是通过JDk的MessageConverter来实现序列化。也就是说,当消息提供者发送一个bean到消息队列时,它会转换成一串很长的乱码,难以阅读和维护,并且占用大量空间,和安全问题。

通过修改可以直接将bean序列化为json,大大减少空间。通过修改消息转换器为fastjson或其它第三方json序列化工具即可。这样消息消费者也可以直接获取到bean实体类了

示例

1、在父工程引入依赖,这样每个儿子都会拥有依赖

<dependency>
	<groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
	<version>2.9.10</version>
</dependency>

2、在consumer消费者服务中,修改fastjson为转换器

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

3、发送bean实体的消息

public void testSend(){
    //模拟bean实体消息
    Map<String,Object> msg = new HashMap<>();
    msg.put("name","abc");
    msg.put("age",11);
    rabbitTemplate.convertAndSend("queueName",msg);
}

4、接收bean实体消息
直接转换成bean实体

@RabbitListener(queues = "queueName")
public void listenerObjectQueue(Map<String,Object> msg ){
    System.out.printLn(msg)
}

分布式搜索-ES

ES即是elasticsearech
这是一款非常强大的开源搜索引擎,可以帮助我们从海量数据中快速找到需要的内容

Elasticsearch 结合了 kibana、Logstash、Beats。它们结合起来统称elastic stack (ELK 技术栈)

其中,elasticsearch是elastic stack的核心,负责存储、搜索、分析数据
kibana负责数据可视化,logstash和beats负责数据抓取微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

Lucene
elasticsearch是基于Lucene作为底层开发的。
Lucene是Apache的一个开源类库,提供了搜索引擎的核心API。

正向索引
基于文档id创建索引,查询词条时必须先找到文档,而后判断是否包含词条

倒排索引
对文档内容分词,对词条分词创建索引,并记录词条所在文档的信息。查询时先根据词条查询到文档id,而后获取到文档

文档和词条
每一条数据就是一个文档
对稳定中的内容分词(中文按语义分,英文按空格分),得到的词语就是词条

安装ES

1、在docker创建一个网络,让es和kibana容器可以互联
当然也可以使用docker compose集群部署,这样部署内部容器可以互联

docker network create es-net

2、拉取elseticsearch的镜像
镜像非常大,接近1G左右
基于elseticsearch:7.12.1版本讲解

docker pull elasticsearch:7.12.1

3、启动es
单点部署es

docker run -d \
	--name es \
	-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
	-e "discovery.type=single-node" \
	-v es-data:/usr/share/elasticsearch/data \
	-v es-plugins:/usr/share/elasticsearch/plugins \
	--privileged \
	--network es-net \
	-p 9200:9200 \
	-p 9300:9300 \
	elasticsearch:7.12.1
#参数解释:
#-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" :JVM虚拟机的内存,默认是1024m,最低不可低于512m
#-e "discovery.type=single-node" : 单点模式运行
#-v es-data : 数据挂载目录
#-v es-plugins : 插件挂载目录
#--privileged  : 授予逻辑卷访问权限
#--network es-net : 使用自定义的网络,让它们容器之间网络互联
#-p 9200 : http调用端口
#-p 9300 : 各个容器互联的端口

4、测试启动是否成功
访问:http://192.168.7.10:9200/
返回:
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

安装kibana

kibana可以给我们提供一个elasticsearch的可视化界面

1、拉取kibana镜像
注意它们之间的版本要一致

docker pull kibana:7.12.1

2、启动kibana容器

docker run -d \
	--name kibana \
	-e ELASTICSEARCH_HOSTS=http://es:9200 \
	--network=es-net \
	-p 5601:5601 \
	kibana:7.12.1

#参数解释:
#-e "ELASTICSEARCH_HOSTS=http://es:9200" : 指向elasticsearch的服务地址,这里可以直接使用容器名,因为它们在同一个docker网络,直接使用容器名可以得到对方的ip
#--network es-ne : 加入到es-net网络
#-p 5601:5601 : 对外访问接口

3、进入后台页面
http://192.168.7.10:5601/
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

4、DevTools可以快速测试DSL语句
访问:http://192.168.7.10:5601/app/dev_tools#/console
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

分词器

es在创建倒排索引时需要对文档分词;在搜索时,需要对用户输入的内容进行分词。
但默认的分词规则对中文处理并不友好,会对中文文字每个切割,这样就达不到语义的效果。

可以选择使用第三方分词器:IK分词器。
项目地址:https://github.com/medcl/elasticsearch-analysis-ik

这输入一个插件,需要安装。

安装IK分词器演示:

1、离线下载插件,前往项目地址,获取最新版本插件下载
注意,版本必须和es一致
否则es会启动失败
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

2、将下载的插件包复制到服务器,
我们在启动elasticsearch容器时,挂载了一个es-plugins目录,该目录就是存放这种插件的
2.1、通过docker命令,到es-plugins的目录路径

docker volume inspect es-plugins

2.2、把插件包解压复制进去该目录

# 进入到 es-plugins挂载目录
[root@localhost ~]# cd /var/lib/docker/volumes/es-plugins/_data
[root@localhost _data]# ls
elasticsearch-analysis-ik-7.12.1.zip
# 解压压缩包,文件夹文ik
[root@localhost _data]# unzip elasticsearch-analysis-ik-7.12.1.zip -d ik/
# 删除压缩包
[root@localhost _data]# rm -rf elasticsearch-analysis-ik-7.12.1.zip 
# 只剩下一个ik文件夹,这便是插件包了
[root@localhost _data]# ls
ik

3、重启es容器

docker restart es

4、测试分词器
进入kibana的Dev Tools页面,这是用于测试DSL语句的控制器
http://192.168.7.10:5601/app/dev_tools#/console

测试样例:
IK分词器包含两种模式

  • ik_smart:最少切分
  • ik_max_word :最细切分

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

测试结果:
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)
可以看到,ik分词器可以语义的进行词语分割,默认的是每个汉字分割,看不出语义。

拓展分词器

如果想要的词语并没有分词成功,我们可以使用拓展分词器,把需要分词的词语加进去。
比如“奥里给”,默认ik分词器是不能切割出来的。

同样的,如果不想某些词被分出来,也可以使用停用词库的功能,比如“的,吖,呵"的语气词等,还有敏感词。把这写限制词语加入进停用词库,这样ik分词器就不会把这些切割出来。

1、修改ik分词器目录中的config目录中的IKAnalyzer.cfg.xml文件
(插件包)ik/config/IKAnalyzer.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
        <comment>IK Analyzer 扩展配置</comment>
        <!--用户可以在这里配置自己的扩展字典 -->
        <entry key="ext_dict">ext.dic</entry>
         <!--用户可以在这里配置自己的扩展停止词字典-->
        <entry key="ext_stopwords">stopword.dic</entry>
        <!--用户可以在这里配置远程扩展字典 -->
        <!-- <entry key="remote_ext_dict">words_location</entry> -->
        <!--用户可以在这里配置远程扩展停止词字典-->
        <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>          

2、在IKAnalyzer.cfg.xml同级目录下新建ext.dic文件,用于存放拓展词库。当然你也可以指向绝对路径,指向别的文件。

ext.dic

奥里给
干了兄弟们

stopword.dic
可以看到默认stopword.dic以及存在一些英文的无意义单词

太阳
的

3、重启es容器生效

docker restart es

4、测试演示
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

结果:
微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

可以看到,拓展分词器词典生效了。停用词库的”太阳“也生效了,没有显示出来。

DSL语法

创建索引库

ES中通过Restful请求操作索引、文档。

请求内容用DSL语句来表示。
使用PUT请求创建一个索引库

PUT /索引库名称
{
    "mappings":{
        "properties":{
            "字段名":{
            	"type":"text"
            	"analyzer":"ik_smart"
	        },
        	"字段名2":{
                "type":"keyword",
                "index":"false"
            },
            "字段名3":{
                "properties":{
                    "子字段":{
                        "type":"keyword"
                    }
                }
            }
            //...略
        }
    }
}

解释:
type:字段数据类型。text(可分词的文本)、keyword(精确词语,不参与分割,如品牌名,国家,ip地址等)。还有常见的long、integer、double等数值。还有boolean布尔值。还有日期data。还有对象object。

PUT: Restful风格请求模式。获取则用GET。删除则用DELETE。

index:是否创建索引,默认为true

analyzer:使用哪种分词器

properties:该字段的子字段

示例:

# 创建一个索引库
PUT /firstindex
{
  "mappings": {
    "properties": {
      "info": {
        "type": "text",
        "analyzer": "ik_smart"
      },
      "email": {
        "type": "keyword",
        "index": "false"
      },
      "name": {
        "properties": {
          "firstname": {
            "type": "keyword"
          },
          "lastname": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

结果:

创建成功

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

创建一个索引库时,主要考虑字段的名字和字段的类型。然后考虑要不要分词,要就是type属性,不需要分词就是keyword属性。如果要分词就考虑分词器是什么。如果不参与搜索就用关闭索引index: false。如果想同时根据多个字段搜索就使用copy_to。

查询索引库

使用GET请求

GEt /索引库名
# 示例
GET /firstindex

删除索引库

使用DELETE请求

DELETE /索引库名
# 示例
DELETE /firstindex

修改索引库

一般索引库创建成功后不能修改,不能修改已经存在的字段。
但是可以新增字段

PUT /索引库名/_mappin
{
	"properties":{
		"新字段名":{
			"type":类型
		}
	}
}

新增文档

使用POST请求
如果不指定文档id,es会随机生成一个

POST /索引库名/_doc/文档id
{
	"字段1":"值1",
	"字段2":"值2",
	"字段3":{
		"子属性1":"值1",
		"子属下2":"值2"
	}
}

查询文档

使用GET请求

GET /索引库名/_doc/文档id

# 批量查询文档
GET /索引库名/_search

删除文档

使用DELETE请求

DELETE /索引库名/_doc/文档id

修改文档

方法1:
全量修改。如果文档id存在,则修改;如果文档id不存在,则新增。

PUT /索引库名/_doc/文档id
{
	"字段1":"值1",
	"字段2":"值2"
}

方法2:
局部修改,只修改部分字段的值

POST /索引库名/_update/文档id
{
	"name":{
		"firstname":"haha"
	}
}

RestClient

ES官方提供了各种不同语言的客户端,用来操作ES。
这些客户端的本质就是组装DSL语句,通过http请求发送给ES。

1、导入依赖
注意:版本要和上面创建的ES版本保持一致
**注意2:**如果项目引入了spring-boot-dependencies依赖管理,那么es的版本需要强制指定,因为spring-boot-dependencies依赖管理里,默认指定了别的版本的es

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.12.1</version>
</dependency>

强制指定es版本
强制指定后dependency里面就不用指定版本了

<properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <elasticsearch.version>7.12.1</elasticsearch.version>
</properties>

2、初始化RestClient对象
下面再单元测试里演示,操作都是基于RestHighLevelClient客户端实现

public class FandouIndexTest {
    private RestHighLevelClient client;
    /**
     * 初始化操作,在启动单元测试前初始化该方法
     */
    @BeforeEach
    void init() {
        client = new RestHighLevelClient(RestClient.builder(
            //如果需要集群,继续往下写即可
            HttpHost.create("http://192.168.7.10:9200")
            //, HttpHost.create("http://192.168.7.11:9200")
            //, HttpHost.create("http://192.168.7.12:9200")
        ));
    }
    /**
     * 执行完单元测试后销毁client
     * 注意client需要close
     */
    @AfterEach
    void end() throws IOException {
        this.client.close();
    }
}

创建索引库

注意:CreateIndexRequest,导包需要导这个:org.elasticsearch.client.indices.CreateIndexRequest

其中,indices()该方法负责操作索引库

import org.apache.http.HttpHost;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
public class FandouIndexTest {
  	 //...略

    /**
     * 创建索引的dsl语句
     */
    public static final String DSL_INDEX_CREATE = "{\n" +
            "  \"mappings\": {\n" +
            "    \"properties\": {\n" +
            "      \"name\":{\n" +
            "        \"type\": \"keyword\"\n" +
            "      },\n" +
            "      \"sex\":{\n" +
            "        \"type\": \"keyword\"\n" +
            "      },\n" +
            "      \"degree\":{\n" +
            "        \"type\": \"keyword\"\n" +
            "      },\n" +
            "      \"occupation\":{\n" +
            "        \"type\": \"text\",\n" +
            "        \"analyzer\": \"ik_smart\"\n" +
            "      },\n" +
            "      \"email\":{\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"index\": false\n" +
            "      },\n" +
            "      \"tel\":{\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"index\": false\n" +
            "      },\n" +
            "      \"university\":{\n" +
            "        \"type\": \"text\",\n" +
            "        \"analyzer\": \"ik_smart\"\n" +
            "      }\n" +
            "    }\n" +
            "  }\n" +
            "}";
    @Test
    void createIndex() throws IOException {
        //1、创建request对象,并指定索引名
        CreateIndexRequest request = new CreateIndexRequest("fandou");
        //2、请求参数。传入DSl语句,内容类型是json
        request.source(DSL_INDEX_CREATE, XContentType.JSON);
        //3、发起请求
        client.indices().create(request, RequestOptions.DEFAULT);
    }
}

删除索引库

@Test
void deleteIndex() throws IOException {
    //1、创建request对象,指定索引名
    DeleteIndexRequest request = new DeleteIndexRequest("fandou");
    //2、发起请求
    client.indices().delete(request,RequestOptions.DEFAULT);
}

判断索引库存在

true表示存在,false表示不存在

@Test
void isExistsIndex() throws IOException {
    //1、创建request对象,指定索引名
    GetIndexRequest request = new GetIndexRequest("fandou");
    //2、发起请求,接收返回值
    boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
    System.out.println(exists);
}

新增文档

@Test
public void createDoc() throws IOException {
    //从数据库获取bean对象
    Tenant tenant =tenantService.findById(1L);

    //1、准备request对象,传入索引库名,以及指定文档id
    IndexRequest request = new IndexRequest("fandou").id(tenant.getId().toString());
    //2、准备DSL语句,json格式的文档。序列化bean得到json
    request.source(JSON.toJSONString(tenant), XContentType.JSON);
    //3、发送请求
    client.index(request, RequestOptions.DEFAULT);
}

查询文档

@Test
public void getDoc() throws IOException {
    //1.准备request对象
    GetRequest request = new GetRequest("fandou", "1");
    //2.发送请求,得到响应
    GetResponse response = client.get(request, RequestOptions.DEFAULT);
    //3.解析响应结果
    String json = response.getSourceAsString();
    Tenant tenant = JSON.parseObject(json,Tenant.class);
    System.out.println(tenant);
}

修改文档

与DSL语法一样,修改文档数据有两种方式:
全量更新:再次写入id一样的文档,就会删除就文档,添加新文档
局部更新:只更新部分字段

这里演示局部更新,注意参数之间时间逗号隔开,没有冒号

@Test
public void updateDoc() throws IOException {
    //1.准备request对象
    UpdateRequest request = new UpdateRequest("fandou","1");
    //2.准备参数
    request.doc(
            "name","梁忆萍",
            "degree","小学"
    );
    //3.发送请求
    client.update(request,RequestOptions.DEFAULT);
}

删除文档

@Test
public void deleteDoc() throws IOException {
    //1.准备request对象
    DeleteRequest request = new DeleteRequest("fandou","1");
    //2.发送请求
   client.delete(request,RequestOptions.DEFAULT);
}

批量操作

通过使用BulkRequest来做批量操作
bulkRequest提供了一个add()方法,用来接收Index、delete、updateRequest的请求。

索引bulkRequest可以做批量删除、批量新增、批量更新等操作

下面演示批量新增文档到es

@Test
    public void batch() throws IOException {
        //1.创建bulk对象
        BulkRequest request = new BulkRequest();
        //2.添加批量请求。
        request.add(new IndexRequest("fandou").id("").source("json", XContentType.JSON));
        request.add(new IndexRequest("fandou").id("").source("json", XContentType.JSON));
        request.add(new IndexRequest("fandou").id("").source("json", XContentType.JSON));
        request.add(new IndexRequest("fandou").id("").source("json", XContentType.JSON));
        //3.发起请求
        client.bulk(request,RequestOptions.DEFAULT);
    }

DSL高级语法

查询条件

  • 查询所有:查询出所有数据,match_all
  • 全文检索查询:利用分词器对用户输入的内容分词,然后去倒排索引库中匹配。
    • match_query
    • multi_match_query
  • 精确查询:根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型
    • ids
    • range
    • tern
  • 地理查询(geo):根据经纬度查询
    • gep_distance
    • geo_bounding_box
  • 符合查询:符合查询可以将上诉各种查询条件组合起来,合并查询
    • bool
    • funcation_score

基本语法

GET /索引库名/_search
{
 	"query":{
        "查询类型":{
            "查询条件":"条件值“
		}
    }
}

全文检索查询

全文检索查询,**会对用户输入的内容分词,**常用于搜索框搜索

match查询:全文检索查询的一种,会对用户输入的内容分词,到后去索引库查询
语法:

GET /索引库名/_search
{
	"query":{
		"match":{
			"FIELD":"text"
		}
	}
}

示例:
查询字段university,

GET /fandou/_search
{
  "query":{
    "match": {
      "university": "深圳信息"
    }
  }
}

multi_match:与match查询类似,只不过允许同时查询多个字段
语法:

GET /索引库名/_search
{
	"query":{
		"multi_match":{
			"query":"text",
			"fields":["FIELD1","FIELD2"]
		}
	}
}

示例:
查询多个字段,如果其中一个字段符合query,那就显示出来

GET /fandou/_search
{
	"query":{
		"multi_match":{
			"query":"女性",
			"fields":["name","occupation","university","sex"]
		}
	}
}

注意:参与搜索的字段越多,效率越低。应该把多个字段整合进一个字段all里面,进行搜索。例如在创建索引库时,使用copy_to功能,把多个字段复制进一个字段all里面,搜索的时候就直接使用all作为字段即可。

精确查询

精确查询一般是查找keyword、数值、日期、boolean等类型字段。所以不会对搜索条件分词。
有两种查询模式:

  • tern:根据词条精确查询
  • range:根据值访问查询

tern精确查询演示:

GET /索引库名/_search
{
	"query":{
		"tern":{
			"FIELD":{
				"value":"VALEUE"
			}
		}
	}
}

示例:
查询字段degree,必须等于”博士“这个值,否之不显示

GET /fandou/_search
{
  "query": {
    "term": {
      "degree": {
        "value": "博士"
      }
    }
  }
}

range范围查询语法:

GET /索引库/_search
{
  "query": {
    "range": {
      "FILED": {
        "gte": 10,
        "lte": 20
      }
    }
  }
}

演示:
这里表示查询id字段,范围是10-20的数值
gte表示大于等于
gt表示大于
le表示小于
lte表示小于等于

GET /fandou/_search
{
  "query": {
    "range": {
      "id": {
        "gte": 10,
        "lte": 20
      }
    }
  }
}

地理查询

geo_bounding_box:查询geo_point值落在某个矩形范围的所有数据

语法:

GET /索引库/_search
{
	"query":{
		"geo_bounding_box":{
			"FIELD":{
				"top_left":{
					"lat":31.1,
					"lon":121.5
				},
				"bottom_right":{
					"lat":30.9,
					"lon":121.7
				}
			}
		}
	}
}

geo_distance:查询到指定中心点小于某个距离值的所有数据
常见应用常见:附近的人

语法:
这里表示以(31.21,121.5)为中心点查询范围15km的文档

GET /索引库/_search
{
	"query":{
		"geo_distance":{
			"distance":"15km",
			"FIELD":"31.21,121.5"
		}
	}
}

复合查询

可以将上诉的查询条件组合起来,实现更复杂的搜索逻辑

fucation score:算分函数查询,可以控制文档的分数(_score),控制文档排名

示例:

GET /hotel/_search
{
	"query":{
		"function_score":{
			"query":{
				"match":{"
					"degree":"博士"
				"}
			},
			"functions":[
				{
					"filter":{"term":{"id":"1"}},
					"weight":10
				}
			],
			"boost_mode":"multiply"
		}
	}
}

这个例子的意思是,先用match查询到dgree字段的文档,es会根据相关性自行排名一遍,然后再根据term精确匹配到id字段为1的文档,给它权重设置为10。这样id为1的文档会把原先的默认权重,乘上自定义的权重10,得到最终权重。而boost_mode表示加权模式,multiply表示两者相乘。这种应用场景常见与竞价排名

微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)

boolean query:布尔查询是一个或多个查询子句的组合。

  • must:必须匹配每个子查询,类似“与”
  • should:选择性匹配子查询,类似“或”
  • must_not:必须不匹配,不参与算分,类似“非”
  • filter:必须匹配,不参与算分

示例:
可以结合多个查询条件

GET /索引库/_search
{
	"query":{
		"bool":{
			"must":[
				{"term":{"degree":"博士"}}
			],
			"should":[
				{"term":{"sex":"女性"}}}
			]
		}
	}
}

结果处理

排序

es支持对搜索结果排序,默认是根据相关度算分(_score)排序。可以排序字段有:keyword类型、数值类型、地理坐标类型、日期类型等。

排序方式:ASC(升序)、DESC(降序)

语法:

GET /索引库/_search
{
	"query":{
		"match_all":{}
	},
	"sort":[
		{"FILED":"DESC"},
		{"FILED2":"ASC"}
	]
}

分页

es默认情况下只返回10条数据

自定义分页演示:(类似mysql的limit)

GET /索引库/_search
{
	"query":{
		“match_all":{}
	},
	"from":100, //分页开始的未知,默认为0
	”size":10, //获取的文档总是,类似limit 100 0
}

高亮

就是在搜索结果中把搜索关键字突出显示

原理:

  • 将搜索结果中的关键字用标签标记出来
  • 在页面中给标片添加css样式

语法:

GET /索引名/_search
{
	"query":{
		"match":{
			"FILED":"TEXt"
		}
	},
	"highlight":{
		"fields":{
			"FIELD":{ //指定要高亮的字段,可以指定多个
				"pre_tags":"<em>", //用来标记高亮字段的前置标签
				"post_tags":"</em>" 、//后置标签
			}
		}
	}
}

未完待续

https://www.bilibili.com/video/BV1LQ4y127n4/?p=111&spm_id_from=pageDriver&vd_source=f71bfe4ea9de7e1bb5d4e3832e91832c

查询多个字段
语法:

GET /索引库名/_search
{
	"query":{
		"multi_match":{
			"query":"text",
			"fields":["FIELD1","FIELD2"]
		}
	}
}

示例:
查询多个字段,如果其中一个字段符合query,那就显示出来

GET /fandou/_search
{
	"query":{
		"multi_match":{
			"query":"女性",
			"fields":["name","occupation","university","sex"]
		}
	}
}

注意:参与搜索的字段越多,效率越低。应该把多个字段整合进一个字段all里面,进行搜索。例如在创建索引库时,使用copy_to功能,把多个字段复制进一个字段all里面,搜索的时候就直接使用all作为字段即可。

精确查询

精确查询一般是查找keyword、数值、日期、boolean等类型字段。所以不会对搜索条件分词。
有两种查询模式:

  • tern:根据词条精确查询
  • range:根据值访问查询

tern精确查询演示:

GET /索引库名/_search
{
	"query":{
		"tern":{
			"FIELD":{
				"value":"VALEUE"
			}
		}
	}
}

示例:
查询字段degree,必须等于”博士“这个值,否之不显示

GET /fandou/_search
{
  "query": {
    "term": {
      "degree": {
        "value": "博士"
      }
    }
  }
}

range范围查询语法:

GET /索引库/_search
{
  "query": {
    "range": {
      "FILED": {
        "gte": 10,
        "lte": 20
      }
    }
  }
}

演示:
这里表示查询id字段,范围是10-20的数值
gte表示大于等于
gt表示大于
le表示小于
lte表示小于等于

GET /fandou/_search
{
  "query": {
    "range": {
      "id": {
        "gte": 10,
        "lte": 20
      }
    }
  }
}

地理查询

geo_bounding_box:查询geo_point值落在某个矩形范围的所有数据

语法:

GET /索引库/_search
{
	"query":{
		"geo_bounding_box":{
			"FIELD":{
				"top_left":{
					"lat":31.1,
					"lon":121.5
				},
				"bottom_right":{
					"lat":30.9,
					"lon":121.7
				}
			}
		}
	}
}

geo_distance:查询到指定中心点小于某个距离值的所有数据
常见应用常见:附近的人

语法:
这里表示以(31.21,121.5)为中心点查询范围15km的文档

GET /索引库/_search
{
	"query":{
		"geo_distance":{
			"distance":"15km",
			"FIELD":"31.21,121.5"
		}
	}
}

复合查询

可以将上诉的查询条件组合起来,实现更复杂的搜索逻辑

fucation score:算分函数查询,可以控制文档的分数(_score),控制文档排名

示例:

GET /hotel/_search
{
	"query":{
		"function_score":{
			"query":{
				"match":{"
					"degree":"博士"
				"}
			},
			"functions":[
				{
					"filter":{"term":{"id":"1"}},
					"weight":10
				}
			],
			"boost_mode":"multiply"
		}
	}
}

这个例子的意思是,先用match查询到dgree字段的文档,es会根据相关性自行排名一遍,然后再根据term精确匹配到id字段为1的文档,给它权重设置为10。这样id为1的文档会把原先的默认权重,乘上自定义的权重10,得到最终权重。而boost_mode表示加权模式,multiply表示两者相乘。这种应用场景常见与竞价排名

[外链图片转存中…(img-S9WSKR47-1664193921875)]

boolean query:布尔查询是一个或多个查询子句的组合。

  • must:必须匹配每个子查询,类似“与”
  • should:选择性匹配子查询,类似“或”
  • must_not:必须不匹配,不参与算分,类似“非”
  • filter:必须匹配,不参与算分

示例:
可以结合多个查询条件

GET /索引库/_search
{
	"query":{
		"bool":{
			"must":[
				{"term":{"degree":"博士"}}
			],
			"should":[
				{"term":{"sex":"女性"}}}
			]
		}
	}
}

结果处理

排序

es支持对搜索结果排序,默认是根据相关度算分(_score)排序。可以排序字段有:keyword类型、数值类型、地理坐标类型、日期类型等。

排序方式:ASC(升序)、DESC(降序)

语法:

GET /索引库/_search
{
	"query":{
		"match_all":{}
	},
	"sort":[
		{"FILED":"DESC"},
		{"FILED2":"ASC"}
	]
}

分页

es默认情况下只返回10条数据

自定义分页演示:(类似mysql的limit)

GET /索引库/_search
{
	"query":{
		“match_all":{}
	},
	"from":100, //分页开始的未知,默认为0
	”size":10, //获取的文档总是,类似limit 100 0
}

高亮

就是在搜索结果中把搜索关键字突出显示

原理:

  • 将搜索结果中的关键字用标签标记出来
  • 在页面中给标片添加css样式

语法:

GET /索引名/_search
{
	"query":{
		"match":{
			"FILED":"TEXt"
		}
	},
	"highlight":{
		"fields":{
			"FIELD":{ //指定要高亮的字段,可以指定多个
				"pre_tags":"<em>", //用来标记高亮字段的前置标签
				"post_tags":"</em>" 、//后置标签
			}
		}
	}
}

未完待续

https://www.bilibili.com/video/BV1LQ4y127n4/?p=111&spm_id_from=pageDriver&vd_source=f71bfe4ea9de7e1bb5d4e3832e91832c

P111未看文章来源地址https://www.toymoban.com/news/detail-479253.html

到了这里,关于微服务技术栈笔记从入门到跑路-SpringCloud+Gateway+Nacos+MQ+ES(保姆级)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SpringCloud Alibaba入门7之引入服务网关Gateway

    我们需要在客户端和服务端之间加一个统一的入口,来作为请求的统一接入,而在微服务的体系中,承担这个角色的就是网关。我们只需要将网关的机器IP配置到DNS,或者接入负载,那么客户端的服务最终通过我们的网关,再转发到对应的服务端服务。 一、创建网关服务  1.引

    2024年02月11日
    浏览(35)
  • 【从删库到跑路】MySQL系列——数据库的介绍&&MySQL的启动

    🎊专栏【MySQL】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【如愿】 大一同学小吉,欢迎并且感谢大家指出我的问题🥰 数据库是一种用于存储、组织和管理数据的系统。它是一个结构化的数据集合,可以通过计算机系统进行访问、操作和更新。数据库

    2024年02月08日
    浏览(45)
  • 【从删库到跑路】MySQL数据库的索引(二)——索引的使用和选择

    🎊专栏【MySQL】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【The Right Path】 🥰欢迎并且感谢大家指出小吉的问题 🎈没有创建索引时,执行SQL语句,查看SQL的耗时 🎈 创建索引后 ,执行SQL语句,查看SQL的耗时 比较发现,时间减少了 🎈细节 创建索引 相

    2024年02月16日
    浏览(38)
  • 【MySQL从删库到跑路 | 基础第二篇】——谈谈SQL中的DML语句

    个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【MySQL学习专栏】🎈 本专栏旨在分享学习MySQL的一点学习心得,欢迎大家在评论区讨论💌 前面我们已经讲解了SQL语句中的DDL语句。今天我们继续来学习SQL的DML语句。

    2024年02月07日
    浏览(39)
  • 【从删库到跑路】一文带你明白MySQL数据库的 事务 操作

    🎊专栏【MySQL】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【如愿】 大一同学小吉,欢迎并且感谢大家指出我的问题🥰 建议大家先看目录 是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作

    2024年02月13日
    浏览(30)
  • 【从删库到跑路】MySQL系列——详细讲解SQL的DDL,DML,DQL,DCL语句

    🎊专栏【MySQL】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【如愿】 大一同学小吉,欢迎并且感谢大家指出我的问题🥰 内容有点多,建议大家先看目录。 建立在关系模型基础上,由多张相互连接的二维表组成的数据库 🏀使用表存储数据,格式统一,

    2024年02月09日
    浏览(39)
  • 【从删库到跑路】MySQL数据库的查询(单表查询,多表查询,内外连接,联合查询,子查询)

    🎊专栏【MySQL】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【如愿】 大一同学小吉,欢迎并且感谢大家指出我的问题🥰 在项目开发中,在进行数据库表结构设计时,会根据业务需求以及业务模块之间的关系,分析并设计表结构,由于业务之间相互关联

    2024年02月10日
    浏览(36)
  • 【从删库到跑路】MySQL数据库的索引(一)——索引的结构(BTree B+Tree Hash),语法等

    🎊专栏【MySQL】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【如愿】 🥰欢迎并且感谢大家指出小吉的问题 索引(index)是帮助MySQL 高效获取数据 的 有序 的 数据结构 在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方

    2024年02月16日
    浏览(42)
  • 【SpringCloud学习笔记】gateway网关

    核心概念: 路由(route):路由信息由id、目标url、一组断言和一组过滤器组成。如果断言路由为真,则说明请求的 URI 和配配 断言(predicate): Java8 中的断言函数,可以配置http请求的断言条件,断言成功会对匹配的路径进行路由 过滤器(filter):过滤器分为两种类型,分

    2024年02月11日
    浏览(30)
  • 【SpringCloud】Gateway服务网关

    Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等响应式编程和事件流技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。 Gateway网关是我们服务的守门神,所有微服务的统一入口。 网关的

    2024年02月13日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包