• spring-cloud


    目录

    地址:

    描述:

    RestTemplate远程访问:

     eureka访问:

    nacos注册中心

    配置共享

    feign远程调用

    首先创建一个 module,命名为 feign-api

     新增api

     引入 feign 依赖:

    order-service中 的 UserClient、User 都复制到 feign-api 项目中 加入依赖

    在 order-service 启动类添加注解开启 Feign

    测试

    Gateway 网关

    创建 SpringBoot 工程 gateway,引入网关依赖

    编写启动类

    流程图

    全局过滤器

    跨域问题

    RabbitMQ消息中间件

    镜像拉取

    启动容器

    publisher实现

    consumer实现

    SpringAMQP

    pom:

    配置:

    在 consumer 服务中添加监听队列

    在 publisher 服务中添加发送消息的测试类

    WorkQueue

    Fanout

    Direct

    topic

    消息转换器

     修改序列化:

    ELasticsearch搜索引擎

    es安装:

    安装ik分词器

    基本操作: 

    索引操作:

    文档操作:

    消息同步:

    查询映射:

    自动补全

    JMeter压力测试

    Sentine流量组件

    springCloud中整合 Sentinel

    Feign整合Sentinel 

    Seata分布式事务

    Seata的架构

     部署TC服务

    Seata微服务集成


    地址:

    引用:

    微服务技术栈 - 乐心湖's Blog | 技术小白的技术博客

    GitHub:

    描述:

    RestTemplate远程访问:

    1. @Bean
    2. public RestTemplate restTemplate(){
    3. return new RestTemplate();
    4. }
    5. public Order queryOrderAndUserById(Long orderId) {
    6. // 1.查询订单
    7. Order order = orderMapper.findById(orderId);
    8. // TODO: 2021/8/15 2.查询用户
    9. User user = restTemplate.getForObject("http://localhost:8081/user/" + order.getUserId(), User.class);
    10. // 3. 将用户信息封装进订单
    11. order.setUser(user);
    12. // 4.返回
    13. return order;
    14. }

     eureka访问:

    新建eureka-service 服务

    1. //pom 依赖
    2. org.springframework.cloud
    3. spring-cloud-starter-netflix-eureka-server
    4. //@EnableEurekaServer 开启 eureka 的注册中心功能
    5. import org.springframework.boot.SpringApplication;
    6. import org.springframework.boot.autoconfigure.SpringBootApplication;
    7. import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
    8. @SpringBootApplication
    9. @EnableEurekaServer
    10. public class EurekaApplication {
    11. public static void main(String[] args) {
    12. SpringApplication.run(EurekaApplication.class, args);
    13. }
    14. }
    15. // application.yml
    16. server:
    17. port: 10086
    18. spring:
    19. application:
    20. name: eureka-server
    21. eureka:
    22. client:
    23. service-url:
    24. defaultZone: http://127.0.0.1:10086/eureka

    将order user 注册到eureka服务

    1. //将 user-service、order-service 都注册到 eureka
    2. org.springframework.cloud
    3. spring-cloud-starter-netflix-eureka-client
    4. 在启动类上添加注解:@EnableEurekaClient
    5. spring:
    6. application:
    7. #name:orderservice
    8. name: userservice
    9. eureka:
    10. client:
    11. service-url:
    12. defaultZone: http://127.0.0.1:10086/eureka

    服务拉取

    1. //@LoadBalanced 注解,用于开启负载均衡。
    2. //SpringCloud 底层提供了一个名为 Ribbon 的组件,来实现负载均衡功能。
    3. @Bean
    4. @LoadBalanced
    5. public RestTemplate restTemplate(){
    6. return new RestTemplate();
    7. }

    nacos注册中心

    nacos安装

    1. //拉取镜像
    2. docker pull nacos/nacos-server
    3. //运行
    4. docker run --name nacos -d -p 8848:8848 --privileged=true --restart=always -e JVM_XMS=256m -e JVM_XMX=256m -e MODE=standalone -e PREFER_HOST_MODE=hostname nacos/nacos-server
    5. //访问
    6. http://localhost:8848/nacos/#/configurationManagement?dataId=&group=&appName=&namespace=&pageSize=&pageNo=

    服务注册

    1. //在 cloud-demo 父工程中引入 SpringCloudAlibaba 的依赖:
    2. <dependency>
    3. <groupId>com.alibaba.cloud</groupId>
    4. <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    5. <version>2.2.6.RELEASE</version>
    6. <type>pom</type>
    7. <scope>import</scope>
    8. </dependency>
    9. //然后在 user-service 和 order-service 中的pom文件中引入 nacos-discovery 依赖:
    10. <dependency>
    11. <groupId>com.alibaba.cloud</groupId>
    12. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    13. </dependency>
    14. //在 user-service 和 order-service 的 application.yml 中添加 nacos 地址:
    15. spring:
    16. cloud:
    17. nacos:
    18. server-addr: 127.0.0.1:8848
    19. //浏览器访问:http://localhost:8080/order/101,正常访问,同时负载均衡也正常。

    配置文件

    nacos 地址必须放在优先级最高的 bootstrap.yml 文件

    1. //首先,在 user-service 服务中,引入 nacos-config 的客户端依赖
    2. <!--nacos配置管理依赖-->
    3. <dependency>
    4. <groupId>com.alibaba.cloud</groupId>
    5. <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    6. </dependency>
    7. //然后,在 user-service 中添加一个 bootstrap.yml 文件,内容如下:
    8. spring:
    9. application:
    10. name: userservice # 服务名称
    11. profiles:
    12. active: dev #开发环境,这里是dev
    13. cloud:
    14. nacos:
    15. server-addr: localhost:8848 # Nacos地址
    16. config:
    17. file-extension: yaml # 文件后缀名
    18. //在nacos控制台新增配置文件 :
    19. data-id: userservice-dev.yaml
    20. logging:
    21. level:
    22. com.xn2001: debug
    23. pattern:
    24. dateformat: MM-dd HH:mm:ss:SSS
    25. //在 user-service 中的 UserController 中添加业务逻辑,读取 pattern.dateformat 配置并使用:
    26. @Value("${logging.pattern.dateformat}")
    27. private String dateformat;
    28. @GetMapping("now")
    29. public String now(){
    30. //格式化时间
    31. return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat));
    32. }
    33. //启动服务后,访问:http://localhost:8081/user/now

    配置共享

    其实在服务启动时,nacos 会读取多个配置文件,例如:

    • [spring.application.name]-[spring.profiles.active].yaml,例如:userservice-dev.yaml
    • [spring.application.name].yaml,例如:userservice.yaml

    这里的 [spring.application.name].yaml 不包含环境,因此可以被多个环境共享

    feign远程调用

    首先创建一个 module,命名为 feign-api

     新增api

    1. @FeignClient("userservice")
    2. public interface UserClient {
    3. @GetMapping("/user/{id}")
    4. User findById(@PathVariable("id") Long id);
    5. }

     引入 feign 依赖:

    1. <dependency>
    2. <groupId>org.springframework.cloud</groupId>
    3. <artifactId>spring-cloud-starter-openfeign</artifactId>
    4. </dependency>

    order-service中 的 UserClient、User 都复制到 feign-api 项目中 加入依赖

    1. <dependency>
    2. <groupId>com.xn2001.feign</groupId>
    3. <artifactId>feign-api</artifactId>
    4. <version>1.0</version>
    5. </dependency>

    在 order-service 启动类添加注解开启 Feign

    @EnableFeignClients(basePackages = "com.xn2001.feign.clients")

    测试

    1. @Autowired
    2. private UserClient userClient;
    3. public Order queryOrderAndUserById(Long orderId) {
    4. // 1.查询订单
    5. Order order = orderMapper.findById(orderId);
    6. // TODO: 2021/8/20 使用feign远程调用
    7. User user = userClient.findById(order.getUserId());
    8. // 3. 将用户信息封装进订单
    9. order.setUser(user);
    10. // 4.返回
    11. return order;
    12. }

    Gateway 网关

    创建 SpringBoot 工程 gateway,引入网关依赖

    1. <!--网关-->
    2. <dependency>
    3. <groupId>org.springframework.cloud</groupId>
    4. <artifactId>spring-cloud-starter-gateway</artifactId>
    5. </dependency>
    6. <!--nacos服务发现依赖-->
    7. <dependency>
    8. <groupId>com.alibaba.cloud</groupId>
    9. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    10. </dependency>
    11. //创建 application.yml 文件,内容如下:
    12. server:
    13. port: 10010 # 网关端口
    14. spring:
    15. application:
    16. name: gateway # 服务名称
    17. cloud:
    18. nacos:
    19. server-addr: localhost:8848 # nacos地址
    20. gateway:
    21. routes: # 网关路由配置
    22. - id: user-service # 路由id,自定义,只要唯一即可
    23. # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
    24. uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称
    25. predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
    26. - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
    1. 编写启动类

    2. 编写基础配置和路由规则
    3. 启动网关服务进行测试

     http://localhost:10010/user/1

    流程图

    1. 路由id:路由的唯一标示
    2. 路由目标(uri):路由的目标地址,http代表固定地址,lb代表根据服务名负载均衡
    3. 路由断言(predicates):判断路由的规则
    4. 路由过滤器(filters):对请求或响应做处理

    全局过滤器

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

    • 参数中是否有 authorization
    • authorization 参数值是否为 admin
    1. @Component
    2. public class AuthorizeFilter implements GlobalFilter, Ordered {
    3. // 测试:http://localhost:10010/order/101?authorization=admin
    4. @Override
    5. public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    6. // 获取第一个 authorization 参数
    7. String authorization = exchange.getRequest().getQueryParams().getFirst("authorization");
    8. if ("admin".equals(authorization)){
    9. // 放行
    10. return chain.filter(exchange);
    11. }
    12. // 设置拦截状态码信息
    13. exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
    14. // 设置拦截
    15. return exchange.getResponse().setComplete();
    16. }
    17. // 设置过滤器优先级,值越低优先级越高
    18. // 也可以使用 @Order 注解
    19. @Override
    20. public int getOrder() {
    21. return 0;
    22. }
    23. }

    跨域问题

    1. spring:
    2. cloud:
    3. gateway:
    4. globalcors: # 全局的跨域处理
    5. add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
    6. corsConfigurations:
    7. '[/**]':
    8. allowedOrigins: # 允许哪些网站的跨域请求 allowedOrigins: “*” 允许所有网站
    9. - "http://localhost:8090"
    10. allowedMethods: # 允许的跨域ajax的请求方式
    11. - "GET"
    12. - "POST"
    13. - "DELETE"
    14. - "PUT"
    15. - "OPTIONS"
    16. allowedHeaders: "*" # 允许在请求中携带的头信息
    17. allowCredentials: true # 是否允许携带cookie
    18. maxAge: 360000 # 这次跨域检测的有效期

    RabbitMQ消息中间件

    镜像拉取

    docker pull rabbitmq:3-management

    启动容器

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

    去掉 \ 

    访问:

    http://localhost:15672/#/queues/%2F/object.queue

    publisher实现

    1. public class PublisherTest {
    2. @Test
    3. public void testSendMessage() throws IOException, TimeoutException {
    4. // 1.建立连接
    5. ConnectionFactory factory = new ConnectionFactory();
    6. // 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
    7. factory.setHost("192.168.211.128");
    8. factory.setPort(5672);
    9. factory.setVirtualHost("/");
    10. factory.setUsername("admin");
    11. factory.setPassword("123456");
    12. // 1.2.建立连接
    13. Connection connection = factory.newConnection();
    14. // 2.创建通道Channel
    15. Channel channel = connection.createChannel();
    16. // 3.创建队列
    17. String queueName = "simple.queue";
    18. channel.queueDeclare(queueName, false, false, false, null);
    19. // 4.发送消息
    20. String message = "Hello RabbitMQ!";
    21. channel.basicPublish("", queueName, null, message.getBytes());
    22. System.out.println("发送消息成功:[" + message + "]");
    23. // 5.关闭通道和连接
    24. channel.close();
    25. connection.close();
    26. }
    27. }

    consumer实现

    1. public class ConsumerTest {
    2. public static void main(String[] args) throws IOException, TimeoutException {
    3. // 1.建立连接
    4. ConnectionFactory factory = new ConnectionFactory();
    5. // 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
    6. factory.setHost("192.168.211.128");
    7. factory.setPort(5672);
    8. factory.setVirtualHost("/");
    9. factory.setUsername("admin");
    10. factory.setPassword("123456");
    11. // 1.2.建立连接
    12. Connection connection = factory.newConnection();
    13. // 2.创建通道Channel
    14. Channel channel = connection.createChannel();
    15. // 3.创建队列
    16. String queueName = "simple.queue";
    17. channel.queueDeclare(queueName, false, false, false, null);
    18. // 4.订阅消息
    19. channel.basicConsume(queueName, true, new DefaultConsumer(channel) {
    20. @Override
    21. public void handleDelivery(String consumerTag, Envelope envelope,
    22. AMQP.BasicProperties properties, byte[] body) {
    23. // 5.处理消息
    24. String message = new String(body);
    25. System.out.println("接收到消息:[" + message + "]");
    26. }
    27. });
    28. System.out.println("等待接收消息中");
    29. }
    30. }

    SpringAMQP

    pringAMQP 是基于 RabbitMQ 封装的一套模板,并且还利用 SpringBoot 对其实现了自动装配,使用起来非常方便。

    SpringAMQP 的官方地址:Spring AMQP

    pom:

    1. <!--AMQP依赖,包含RabbitMQ-->
    2. <dependency>
    3. <groupId>org.springframework.boot</groupId>
    4. <artifactId>spring-boot-starter-amqp</artifactId>
    5. </dependency>

    配置:

    1. spring:
    2. rabbitmq:
    3. host: 192.168.150.101 # 主机名
    4. port: 5672 # 端口
    5. virtual-host: / # 虚拟主机
    6. username: admin # 用户名
    7. password: 123456 # 密码

    在 consumer 服务中添加监听队列

    1. @Component
    2. public class RabbitMQListener {
    3. @RabbitListener(queues = "simple.queue")
    4. public void listenSimpleQueueMessage(String msg) throws InterruptedException {
    5. System.out.println("消费者接收到消息:【" + msg + "】");
    6. }
    7. }

    在 publisher 服务中添加发送消息的测试类

    1. @RunWith(SpringRunner.class)
    2. @SpringBootTest
    3. public class SpringAmqpTest {
    4. @Autowired
    5. private RabbitTemplate rabbitTemplate;
    6. @Test
    7. public void testSimpleQueue() {
    8. // 队列名称
    9. String queueName = "simple.queue";
    10. // 消息
    11. String message = "你好啊,乐心湖!";
    12. // 发送消息
    13. rabbitTemplate.convertAndSend(queueName, message);
    14. }
    15. }

    WorkQueue

    Fanout

    路由

    Direct

    在 Fanout 模式中,一条消息,会被所有订阅的队列都消费。但是,在某些场景下,我们希望不同的消息被不同的队列消费。这时就要用到 DirectExchange

    1. @RabbitListener(bindings = @QueueBinding(
    2. value = @Queue(value = "direct.queue1"),
    3. exchange = @Exchange(value = "xn2001.direct"),
    4. key = {"a","b"}
    5. ))
    6. public void listenDirectQueue1(String msg){
    7. System.out.println("接收到direct.queue1的消息:【" + msg + "】" + LocalTime.now());
    8. }
    9. @RabbitListener(bindings = @QueueBinding(
    10. value = @Queue(value = "direct.queue2"),
    11. exchange = @Exchange(value = "xn2001.direct"),
    12. key = {"a","c"}
    13. ))
    14. public void listenDirectQueue2(String msg){
    15. System.out.println("接收到direct.queue2的消息:【" + msg + "】" + LocalTime.now());
    16. }
    1. /**
    2. * direct
    3. * 向交换机发送消息
    4. */
    5. @Test
    6. public void testDirectExchangeToA() {
    7. // 交换机名称
    8. String exchangeName = "xn2001.direct";
    9. // 消息
    10. String message = "hello, i am direct to a!";
    11. rabbitTemplate.convertAndSend(exchangeName, "a", message);
    12. }
    13. /**
    14. * direct
    15. * 向交换机发送消息
    16. */
    17. @Test
    18. public void testDirectExchangeToB() {
    19. // 交换机名称
    20. String exchangeName = "xn2001.direct";
    21. // 消息
    22. String message = "hello, i am direct to b!";
    23. rabbitTemplate.convertAndSend(exchangeName, "b", message);
    24. }

    topic

    Topic 与 Direct相比,都是可以根据RoutingKey把消息路由到不同的队列。只不过Topic 类型可以让队列在绑定Routing key 的时候使用通配符!

    消息转换器

    Spring 会把你发送的消息序列化为字节发送给 MQ,接收消息的时候,还会把字节反序列化为 Java 对象。

     修改序列化:

    1. <dependency>
    2. <groupId>com.fasterxml.jackson.dataformat</groupId>
    3. <artifactId>jackson-dataformat-xml</artifactId>
    4. <version>2.9.10</version>
    5. </dependency>

    1. @Bean
    2. public MessageConverter jsonMessageConverter(){
    3. return new Jackson2JsonMessageConverter();
    4. }

    ELasticsearch搜索引擎

    es安装:

    挂载指令:

    docker run -it -v /D/docker/wordcount:/home/root/a_dir/ IMAGE
    冒号前后分别为本地路径和要挂载到的路径
    IMAGE为镜像名称

    注意

    本地路径不能像通常那样写作D:/,而是要写成/D/,否则会报错Error response from daemon: invalid mode
    要挂载的路径/home/root/a_dir/原先就存在

    docker run -d  --name es  -e "ES_JAVA_OPTS=-Xms512m -Xmx512m"  -e "discovery.type=single-node"  -v es-data:/D/es/data  -v es-plugins:/D/es/plugins --privileged  --network es-net  -p 9200:9200  -p 9300:9300  elasticsearch:7.12.1

    这里磁盘挂在了,也有文件,但是没有生效,容器内在线安装

    安装ik分词器

    1. docker exec -it es /bin/bash
    2. ./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.12.1/elasticsearch-analysis-ik-7.12.1.zip

    参考:

    docker安装es、ik分词器、kabana_Andy_Health的博客-CSDN博客_docker es安装ik

    基本操作: 

    GET /hotel/_search
    {
      "query": {
        "match_all": {}
      }
    }

    POST /_analyze
    {
      "analyzer": "ik_max_word",
      "text": "努力工作天天向上"
    }


    PUT /hotel
    {
      "mappings": {
        "properties": {
          "id": {
            "type": "keyword"
          },
          "name":{
            "type": "text",
            "analyzer": "ik_max_word",
            "copy_to": "all"
          },
          "address":{
            "type": "keyword",
            "index": false
          },
          "price":{
            "type": "integer"
          },
          "score":{
            "type": "integer"
          },
          "brand":{
            "type": "keyword",
            "copy_to": "all"
          },
          "city":{
            "type": "keyword",
            "copy_to": "all"
          },
          "starName":{
            "type": "keyword"
          },
          "business":{
            "type": "keyword"
          },
          "location":{
            "type": "geo_point"
          },
          "pic":{
            "type": "keyword",
            "index": false
          },
          "all":{
            "type": "text",
            "analyzer": "ik_max_word"
          }
        }
      }
    }

    GET hotel


    POST /md/_doc/1
    {
      "info":"study",
      "email":"123@163.com"
    }

    GET /hotel/_doc/61083


    // 查询所有
    GET /hotel/_search
    {
      "query": {
        "match_all": {
        }
      }
    }


    //条件查询
    GET /hotel/_search
    {
      "query": {
        "match": {
          "name": "7天"
        }
      }
    }

    // term查询
    GET /hotel/_search
    {
      "query": {
        "term": {
          "city.keyword": {
            "value": "上海"
          }
        }
      }
    }


    // range查询
    GET /indexName/_search
    {
      "query": {
        "range": {
          "FIELD": {
            "gte": 10, // 这里的gte代表大于等于,gt则代表大于
            "lte": 20 // lte代表小于等于,lt则代表小于
          }
        }
      }
    }

    // geo_bounding_box查询 范围查询
    GET /indexName/_search
    {
      "query": {
        "geo_bounding_box": {
          "FIELD": {
            "top_left": { // 左上点
              "lat": 31.1,
              "lon": 121.5
            },
            "bottom_right": { // 右下点
              "lat": 30.9,
              "lon": 121.7
            }
          }
        }
      }
    }

    // geo_distance 查询 附近查询
    GET /indexName/_search
    {
      "query": {
        "geo_distance": {
          "distance": "15km", // 半径
          "FIELD": "31.21,121.5" // 圆心
        }
      }
    }


    //距离排序
    GET /hotel/_search
    {
      "query": {
        "match_all": {}
      },
      "sort": [
        {
          "_geo_distance" : {
              "location": "31.034661,121.612282", //圆心
              "order" : "asc", //排序
              "unit" : "km" //单位
          }
        }
      ]
    }

    索引操作:

    1. package com.md.es;
    2. import com.md.es.constants.HotelConstants;
    3. import org.apache.http.HttpHost;
    4. import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
    5. import org.elasticsearch.client.RequestOptions;
    6. import org.elasticsearch.client.RestClient;
    7. import org.elasticsearch.client.RestHighLevelClient;
    8. import org.elasticsearch.client.indices.CreateIndexRequest;
    9. import org.elasticsearch.client.indices.GetIndexRequest;
    10. import org.elasticsearch.common.xcontent.XContentType;
    11. import org.junit.jupiter.api.AfterEach;
    12. import org.junit.jupiter.api.BeforeEach;
    13. import org.junit.jupiter.api.Test;
    14. import java.io.IOException;
    15. /**
    16. * TODO add description
    17. *

    18. * Created at 2022/11/1 11:28
    19. *
    20. * @author cengzq5
    21. */
    22. public class EsIndexTest {
    23. private RestHighLevelClient restHighLevelClient;
    24. @BeforeEach
    25. public void test() throws IOException {
    26. restHighLevelClient = new RestHighLevelClient(RestClient.builder(HttpHost.create("http://127.0.0.1:9200")));
    27. }
    28. @AfterEach
    29. public void close() throws IOException {
    30. restHighLevelClient.close();
    31. }
    32. /**
    33. * 创建索引
    34. */
    35. @Test
    36. void createHotelIndex() throws IOException {
    37. //指定索引库名
    38. CreateIndexRequest hotel = new CreateIndexRequest("hotel");
    39. //写入JSON数据,这里是Mapping映射
    40. hotel.source(HotelConstants.MAPPING_TEMPLATE, XContentType.JSON);
    41. //创建索引库
    42. restHighLevelClient.indices().create(hotel, RequestOptions.DEFAULT);
    43. }
    44. /** 判断索引是否存在
    45. * @Description p
    46. * @Author cengzq5
    47. * @Date 2022/11/1 14:02
    48. * @return void
    49. */
    50. @Test
    51. void existHotelIndex() throws IOException {
    52. GetIndexRequest hotel = new GetIndexRequest("hotel");
    53. boolean exists = restHighLevelClient.indices().exists(hotel, RequestOptions.DEFAULT);
    54. System.out.println(exists);
    55. }
    56. /**
    57. * @Description 删除索引
    58. */
    59. @Test
    60. void deleteHotelIndex() throws IOException {
    61. DeleteIndexRequest hotel = new DeleteIndexRequest("hotel");
    62. restHighLevelClient.indices().delete(hotel,RequestOptions.DEFAULT);
    63. }
    64. }

    文档操作:

    1. package com.md.es;
    2. import com.alibaba.fastjson.JSON;
    3. import com.md.es.pojo.Hotel;
    4. import com.md.es.pojo.HotelDoc;
    5. import com.md.es.pojo.PageResult;
    6. import com.md.es.service.HotelService;
    7. import com.md.es.service.HotelServiceImpl;
    8. import org.apache.http.HttpHost;
    9. import org.elasticsearch.action.bulk.BulkRequest;
    10. import org.elasticsearch.action.delete.DeleteRequest;
    11. import org.elasticsearch.action.get.GetRequest;
    12. import org.elasticsearch.action.get.GetResponse;
    13. import org.elasticsearch.action.index.IndexRequest;
    14. import org.elasticsearch.action.search.SearchRequest;
    15. import org.elasticsearch.action.search.SearchResponse;
    16. import org.elasticsearch.action.update.UpdateRequest;
    17. import org.elasticsearch.client.RequestOptions;
    18. import org.elasticsearch.client.RestClient;
    19. import org.elasticsearch.client.RestHighLevelClient;
    20. import org.elasticsearch.common.xcontent.XContentType;
    21. import org.elasticsearch.index.query.QueryBuilders;
    22. import org.junit.Test;
    23. import org.junit.jupiter.api.AfterEach;
    24. import org.junit.jupiter.api.BeforeEach;
    25. import org.junit.runner.RunWith;
    26. import org.springframework.boot.test.context.SpringBootTest;
    27. import org.springframework.test.context.junit4.SpringRunner;
    28. import javax.annotation.Resource;
    29. import java.io.IOException;
    30. import java.util.List;
    31. /**
    32. * TODO add description
    33. *

    34. * Created at 2022/11/1 14:29
    35. *
    36. * @author cengzq5
    37. */
    38. @SpringBootTest
    39. @RunWith(SpringRunner.class)
    40. public class EsDocTest {
    41. @Resource
    42. private RestHighLevelClient restHighLevelClient;
    43. // @BeforeEach
    44. // public void test() throws IOException {
    45. // restHighLevelClient = new RestHighLevelClient(RestClient.builder(HttpHost.create("http://127.0.0.1:9200")));
    46. // }
    47. //
    48. // @AfterEach
    49. // public void close() throws IOException {
    50. // restHighLevelClient.close();
    51. // }
    52. @Resource
    53. private HotelService hotelService;
    54. private final String index = "hotel";
    55. /**
    56. * @Description 新增文档
    57. * @Author cengzq5
    58. * @Date 2022/11/1 15:11
    59. * @return void
    60. */
    61. @Test
    62. public void insertDoc() throws IOException {
    63. Hotel hotel = hotelService.getById(61083L);
    64. HotelDoc hotelDoc = new HotelDoc(hotel);
    65. IndexRequest indexRequest = new IndexRequest(index).id(hotelDoc.getId().toString());
    66. indexRequest.source(JSON.toJSONString(hotelDoc), XContentType.JSON);
    67. restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
    68. }
    69. /**
    70. * @Description 查询文档
    71. * @Author cengzq5
    72. * @Date 2022/11/1 15:14
    73. * @return void
    74. */
    75. @Test
    76. public void getDocById() throws IOException {
    77. GetRequest hotel = new GetRequest(index,"61083");
    78. GetResponse document = restHighLevelClient.get(hotel, RequestOptions.DEFAULT);
    79. String sourceAsString = document.getSourceAsString();
    80. HotelDoc hotelDoc = JSON.parseObject(sourceAsString, HotelDoc.class);
    81. System.out.println(hotelDoc);
    82. }
    83. /**
    84. * @Description 删除文档
    85. * @Author cengzq5
    86. * @Date 2022/11/1 15:15
    87. * @return void
    88. */
    89. @Test
    90. public void testDeleteDocumentById() throws IOException {
    91. DeleteRequest hotel = new DeleteRequest("hotel", "61083");
    92. restHighLevelClient.delete(hotel,RequestOptions.DEFAULT);
    93. }
    94. /**
    95. * @Description 增量修改文档
    96. *
    97. * 修改文档有两种方式:
    98. *
    99. * 全量修改:直接覆盖原来的文档
    100. * 增量修改:修改文档中的部分字段
    101. * 在 RestClient 的 API 中,全量修改与新增的 API 完全一致,判断依据是 ID
    102. *
    103. * 如果新增时,ID已经存在,则修改
    104. * 如果新增时,ID不存在,则新增
    105. *
    106. * @Author cengzq5
    107. * @Date 2022/11/1 15:18
    108. * @return void
    109. */
    110. @Test
    111. public void updateDoc() throws IOException {
    112. UpdateRequest request = new UpdateRequest(index,"61083");
    113. request.doc(
    114. "price","333",
    115. "starName","钻石"
    116. );
    117. restHighLevelClient.update(request,RequestOptions.DEFAULT);
    118. }
    119. @Test
    120. public void testBulk() throws IOException {
    121. BulkRequest bulkRequest = new BulkRequest();
    122. List<Hotel> hotelList = hotelService.list();
    123. hotelList.forEach(item -> {
    124. HotelDoc hotelDoc = new HotelDoc(item);
    125. bulkRequest.add(new IndexRequest("hotel")
    126. .id(hotelDoc.getId().toString())
    127. .source(JSON.toJSONString(hotelDoc), XContentType.JSON));
    128. });
    129. restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
    130. }
    131. /**
    132. * @Description 全量查询
    133. * @Author cengzq5
    134. * @Date 2022/11/1 15:58
    135. * @return void
    136. */
    137. @Test
    138. public void matchAll() throws IOException {
    139. SearchRequest request = new SearchRequest(index);
    140. request.source().query(QueryBuilders.matchAllQuery());
    141. SearchResponse search = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    142. PageResult pageResult = HotelServiceImpl.handleResponse(search);
    143. System.out.println(pageResult);
    144. }
    145. }

    消息同步:

    1. package com.md.es;
    2. import com.md.es.constants.MqConstants;
    3. import org.junit.Test;
    4. import org.junit.runner.RunWith;
    5. import org.springframework.amqp.rabbit.core.RabbitTemplate;
    6. import org.springframework.boot.test.context.SpringBootTest;
    7. import org.springframework.test.context.junit4.SpringRunner;
    8. import javax.annotation.Resource;
    9. /**
    10. * TODO add description
    11. *

    12. * Created at 2022/11/1 16:34
    13. *
    14. * @author cengzq5
    15. */
    16. @RunWith(SpringRunner.class)
    17. @SpringBootTest
    18. public class MqTest {
    19. @Resource
    20. private RabbitTemplate rabbitTemplate;
    21. @Test
    22. public void deleteDoc(){
    23. rabbitTemplate.convertAndSend(MqConstants.HOTEL_EXCHANGE, MqConstants.HOTEL_DELETE_KEY, 61083L);
    24. }
    25. }

    查询映射:

    聚合(aggregations可以让我们极其方便的实现对数据的统计、分析、运算。

    桶(Bucket)聚合:用来对文档做分组

    度量(Metric)聚合:用以计算一些值,比如:最大值、最小值、平均值等

    管道(pipeline)聚合:其它聚合的结果为基础做聚合

    自动补全

    当用户在搜索框输入字符时,我们应该提示出与该字符有关的搜索项,提示完整词条的功能,就是自动补全了。

    JMeter压力测试

    下载后直接执行 bin 目录就是执行的脚本 jmeter.bat,其中包含启动脚本 

    Sentine流量组件

    Sentinel是阿里巴巴开源的一款微服务流量控制组件

    下载后 jar 包后,运行代码:java -jar sentinel-dashboard-1.8.1.jar

    java -Dserver.port=8090 -jar sentinel-dashboard-1.8.1.jar

    访问 http://localhost:8080 页面,就可以看到 Sentinel 的控制台了。

    springCloud中整合 Sentinel

    order-service 中整合 Sentinel

    1. 1)引入 Sentinel 依赖
    2. <!--sentinel-->
    3. <dependency>
    4. <groupId>com.alibaba.cloud</groupId>
    5. <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    6. </dependency>
    7. 2)配置控制台
    8. 修改 application.yml 文件,添加下面内容:
    9. server:
    10. port: 8088
    11. spring:
    12. cloud:
    13. sentinel:
    14. transport:
    15. dashboard: localhost:8080

     10个请求有一半失败

    Feign整合Sentinel 

    feign: sentinel: enabled: true # 开启feign对sentinel的支持

     显示出链路

    Seata分布式事务

    分布式事务,就是指不是在单个服务或单个数据库架构下,产生的事务,例如

    • 跨数据源的分布式事务
    • 跨服务的分布式事务
    • 综合情况

    在数据库水平拆分、服务垂直拆分之后,一个业务操作通常要跨多个数据库、服务才能完成。例如电商行业中比较常见的下单付款案例,包括下面几个行为:

    1. 创建新订单
    2. 扣减商品库存
    3. 从用户账户余额扣除金额

     在没有分布式情况下,新增订单成功,数据库新增一条数据,但是账号服务或者库存服务会失败,不会插入数据。

    Seata的架构

    Seata 事务管理中有三个重要的角色

    • TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚。
    • TM (Transaction Manager) - 事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务。
    • RM (Resource Manager) - 资源管理器:管理分支事务处理的资源,与 TC 交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

     部署TC服务

    下载 seata-server 包,下载中心

    1.4.2 版本

    修改 conf 目录下的 registry.conf 文件

    1. registry {
    2. # tc服务的注册中心类,这里选择nacos,也可以是eureka、zookeeper等
    3. type = "nacos"
    4. nacos {
    5. # seata tc 服务注册到 nacos的服务名称,可以自定义
    6. application = "seata-tc-server"
    7. serverAddr = "127.0.0.1:8848"
    8. group = "DEFAULT_GROUP"
    9. namespace = ""
    10. cluster = "default"
    11. username = "nacos"
    12. password = "nacos"
    13. }
    14. }
    15. config {
    16. # 读取tc服务端的配置文件的方式,这里是从nacos配置中心读取,这样如果tc是集群,可以共享配置
    17. type = "nacos"
    18. # 配置nacos地址等信息
    19. nacos {
    20. serverAddr = "127.0.0.1:8848"
    21. namespace = ""
    22. group = "DEFAULT_GROUP"
    23. username = "nacos"
    24. password = "nacos"
    25. dataId = "seataServer.properties"
    26. }
    27. }

    为了让 TC 服务的集群可以共享配置,我们选择了 Nacos 作为统一配置中心。因此服务端配置文件 seataServer.properties 文件需要在Nacos 中配好。在 Nacos 后台新建一个配置文件:http://localhost:8848/nacos/

    1. # 数据存储方式,db代表数据库
    2. store.mode=db
    3. store.db.datasource=druid
    4. store.db.dbType=mysql
    5. # 这是MySQL8的驱动,MySQL5使用的是com.mysql.jdbc.Driver
    6. store.db.driverClassName=com.mysql.cj.jdbc.Driver
    7. # 数据库地址、用户名、密码都需要修改成你自己的数据库信息。
    8. store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
    9. store.db.user=root
    10. store.db.password=123456
    11. store.db.minConn=5
    12. store.db.maxConn=30
    13. store.db.globalTable=global_table
    14. store.db.branchTable=branch_table
    15. store.db.queryLimit=100
    16. store.db.lockTable=lock_table
    17. store.db.maxWait=5000
    18. # 事务、日志等配置
    19. server.recovery.committingRetryPeriod=1000
    20. server.recovery.asynCommittingRetryPeriod=1000
    21. server.recovery.rollbackingRetryPeriod=1000
    22. server.recovery.timeoutRetryPeriod=1000
    23. server.maxCommitRetryTimeout=-1
    24. server.maxRollbackRetryTimeout=-1
    25. server.rollbackRetryTimeoutUnlockEnable=false
    26. server.undo.logSaveDays=7
    27. server.undo.logDeletePeriod=86400000
    28. # 客户端与服务端传输方式
    29. transport.serialization=seata
    30. transport.compressor=none
    31. # 关闭metrics功能,提高性能
    32. metrics.enabled=false
    33. metrics.registryType=compact
    34. metrics.exporterList=prometheus
    35. metrics.exporterPrometheusPort=9898

    进入 bin 目录,运行其中的 seata-server.bat 即可

    进入cmd启动,运行seata-server.bat

    注意:默认启动端口为8091,确认是否被占用

    Seata微服务集成

    pom依赖

    1. dependency>
    2. <groupId>com.alibaba.cloud</groupId>
    3. <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    4. <exclusions>
    5. <!--版本较低,1.3.0,因此排除-->
    6. <exclusion>
    7. <artifactId>seata-spring-boot-starter</artifactId>
    8. <groupId>io.seata</groupId>
    9. </exclusion>
    10. </exclusions>
    11. </dependency>
    12. <!--seata starter 采用1.4.2版本-->
    13. <dependency>
    14. <groupId>io.seata</groupId>
    15. <artifactId>seata-spring-boot-starter</artifactId>
    16. <version>${seata.version}</version>
    17. </dependency>

     注意,所有服务都要配置

    1. seata:
    2. registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
    3. type: nacos # 注册中心类型 nacos
    4. nacos:
    5. server-addr: 127.0.0.1:8848 # nacos地址
    6. namespace: "" # namespace,默认为空
    7. group: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUP
    8. application: seata-tc-server # seata服务名称
    9. username: nacos
    10. password: nacos
    11. tx-service-group: seata-demo # 事务组名称
    12. service:
    13. vgroup-mapping: # 事务组与cluster的映射关系
    14. seata-demo: default

    使用模式: 

    1. seata:
    2. data-source-proxy-mode: XA

    2)给发起全局事务的入口方法添加 @GlobalTransactional 注解

    重启服务,发送多于库存的订单请求

    发现并没有新增库存订单

  • 相关阅读:
    一文读懂!机器人自动化解决方案的应用领域和前景
    计算机毕业论文选题推荐|软件工程|系列八
    Andorid项目源码(167套)
    同一个页面同一区域两个el-table在v-if下样式重叠问题
    编程实例:多人同时计时计费管理系统软件,可适用于钓场计时等管理
    SpringCloud和Kubernetes的区别
    安踏携手华为运动健康共同验证冠军跑鞋 创新引领中国体育
    我国智慧城市场景中物联网终端评测与认证体系研究
    golang gorm —— 事务、回滚、savepoint
    【尚硅谷Java版】Flink1.13 转换算子之物理分区
  • 原文地址:https://blog.csdn.net/zzqtty/article/details/127323957