SpringCloud学习笔记

微服务之间如何沟通

-HttpClient

  1. Spring框架提供了一个httpclient对象RestTemplate,发起一个http的请求。
  2. 创建resttemplate对象,由spring管理

Eureka注册中心

搭建eureka注册中心

package com.zyl;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

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

配置文件

server:
  port: 10086
spring:
  application:
    name: eureka-server  #服务名
eureka:
  client:
    register-with-eureka: false  #是否禁用自己注册自己
    fetch-registry: false
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka  #地址

Ribbon

Ribbon是一个为客户端提供负载均衡功能的服务,它内部提供了一个叫做ILoadBalance的接口代表负载均衡器的操作,比如有添加服务器操作、选择服务器操作、获取所有的服务器列表、获取可用的服务器列表等等。

1.需要解决的问题:

① 如何在配置Eureka Client注册中心时不去硬编码Eureka Server的地址?
② 在微服务不同模块间进行通信时,如何不去硬编码服务提供者的地址?
③ 当部署多个相同微服务时,如何实现请求时的负载均衡?

2.Ribbon是什么?

Ribbon是Netflix发布的云中间层服务开源项目,其主要功能是提供客户端实现负载均衡算法。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,Ribbon是一个客户端负载均衡器,我们可以在配置文件中Load Balancer后面的所有机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器,我们也很容易使用Ribbon实现自定义的负载均衡算法。

3.配置

  1. 第一种配置方式(配置类申明)

    @Bean
    public IRule iRule(){
        return new RandomRule();
    }
    
  2. 第二种配置文件申明

    userservice:
      ribbon:
        NFLoadBalancerRuleClassName: com.netflix.loadbanlance.randomrule
    

加载机制

饥饿加载 加速访问速度

默认加载 第一次访问速度慢

Nacos注册中心

父工程

    <dependencyManagement>
        <dependencies>
            <!-- springCloud -->
            <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-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.5.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

导入依赖

<!-- nacos客户端依赖包 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

配置文件

server:
  port: 8080
spring:
  application:
    name: orderservice
  cloud:
    nacos:
      server-addr: localhost:8848

nacos的启动

在bin目录里命令行输入

startup.cmd - m standalone

集群配置

spring:
  application:
    name: userservice
  cloud:
    nacos:
      server-addr: localhost:8848
      discovery:
        cluster-name: hangzhou

配置选择服务规则负载均衡

userservice:
  ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule

配置服务权重负载均衡

环境隔离-namespace

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloud_order?useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
  application:
    name: orderservice
  cloud:
    nacos:
      server-addr: localhost:8848
      discovery:
        cluster-name: hangzhou
        namespace: 196f350f-64ed-44d1-940c-f12b44e72f6f #环境隔离id

分析

修改非临时实例

  1. 临时实例:自己主动向nacos发送请求表示自己还是正常的 挂了nacos就会自动剔除服务
  2. 非临时实例:nacos主动向服务请求 当服务挂了不会剔除而是等待服务恢复
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloud_order?useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
  application:
    name: orderservice
  cloud:
    nacos:
      server-addr: localhost:8848
      discovery:
        cluster-name: hangzhou
        namespace: 196f350f-64ed-44d1-940c-f12b44e72f6f
        ephemeral: false  #设置非临时实例

Nacos配置管理

1.统一配置管理

2.添加实现

bootstrap.yml的读取顺序比application.yml先

3.热更新

  1. 使用@refreshscope注解实现热更新
  2. 使用@configrationproperties

多环境配置共享

image-20230526145457215

Nacos集群的搭建

Http客户端Feign

为什么更换restTemplate

  1. 代码可读性差,编程体验不统一
  2. 参数复杂url难以维护

导入坐标

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

加上注解开启功能

@EnableFeignClients

编写接口

@FeignClient("userservice")
public interface UserClient {
    @GetMapping("/user/{id}")
    User getByid(@PathVariable Long id);
}

怎么优化feign(默认的没有连接池)

统一网关Gateway

  1. 身份认证和权限校验
  2. 服务路由,负载均衡
  3. 请求限流

创建gateway模块

1.导入依赖

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

2.配置文件

server:
  port: 10010
spring:
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      routes:
        - id: user-service  #路由标识唯一
          uri: lb://userservice #路由地址
          predicates:  #判断是否符合规则
            - Path=/user/**

  application:
    name: gateway

路由断言工厂

过滤器

  • 对服务的请求和返回的数据进行过滤

1.全局过滤器

2.默认过滤器

3.路由过滤器

全局过滤器代码实现

@Order(-1)  
@Component
public class AuthorizeFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取请求参数
        ServerHttpRequest request = exchange.getRequest();
        MultiValueMap<String, String> queryParams = request.getQueryParams();
        //获取参数
        String authorization = queryParams.getFirst("user");
        if ("admin".equals(authorization)) {
            //放行
            return chain.filter(exchange);
        }
        //拦截
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        return exchange.getResponse().setComplete();
    }
}

执行顺序

解决跨域问题

spring:
  application:
    name: gateway  #服务名称
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #nacos地址
    gateway:
      globalcors:
        add-to-simple-url-handler-mapping: true
        cors-configurations:
          '[/**]':  #拦截的请求
            allowedOrigins: #允许跨域的请求
              - "http://localhost:8080"
            allowedMethods: #运行跨域的请求方式
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*" #允许请求中携带的头信息
            allowedCredentials: true #是否允许携带cookie
            maxAge: 36000 #跨域检测的有效期,单位s

Docker

  • 决各种服务之间依赖的冲突和版本控制

什么是镜像容器等

image-20230526184013889

Docker基本操作

1.容器的启动

systemctl docker start

2.帮助

docker --help

3.保存镜像

docker save -o redis.tar redis

4.加载镜像

docker load -i redis.tar

5.查看状态

docker ps

6.进入容器

docker exec -it 容器名 bash
image-20230526191147292

数据卷

  • 是一个虚拟目录,指向宿主机文件系统的一个真实的目录(方便配置的修改)

1.创建数据卷

docker volume create 卷名

2.查看所有数据卷

docker volume ls

3.查看数据卷详细信息

docker volume inspect 卷名

4.删除数据卷

docker volume rm 卷名

5.挂载容器到数据卷

docker run --name r -p 9999:80 -v html:/usr/share/nginx/html  -d nginx

自定义镜像结构

1.入口

镜像运行的入口,一般是程序启动的脚本和参数

2.层

在baseImage的基础上添加安装包,依赖,配置等,每次的操作都形成新的一层

3.基础镜像

应用依赖的系统函数库,环境,配置,文件等

docker-compose

docker-compose是docker提供的一个命令行工具,用来定义和运行由多个容器组成的应用。

Elasticsearch

elasticsearch是一个非常强大的关键字搜索引擎,还可以日志统计,可视化分析

索引库的操作

1.创建索引库

PUT /zyl
{
  "mappings": {
    "properties": {
      "info":{
        "type": "text",
        "analyzer": "ik_smart"
      },
      "email":{
        "type": "keyword",
        "index": false
      },
      "name":{
        "type": "object",
        "properties": {
          "firstname":{
            "type": "keyword"
          },
          "lastname":{
            "type": "keyword"
          }
        }
      }
    }
  }
}

2.删除索引库

delete /zyl

3.禁止修改索引库

PUT /zyl/_mapping
{
  "properties": {
    "age":{
      "type": "integer"
    }
  }
}

4.查询

get /zyl

文档操作

1.新增文档

POST /zyl/_doc/1
{
  "email": "2040@qq.com",
  "info": "朱岳磊喜欢白嫖",
  "name":{
    "firstname":"云",
    "lastname":"赵"
  }
}

2.查看文档

GET /zyl/_doc/1

3.删除

delete /zyl/_doc/1

4.修改文档

  • 全量修改,id存在会删除旧的,然后新增,如果id不存在就会直接新增
PUT /zyl/_doc/1
{
  "email": "2040@qq.com",
  "info": "朱岳磊喜欢白嫖",
  "name":{
    "firstname":"s",
    "lastname":"赵"
  }
}
  • 增量修改
POST /zyl/_update/1
{
  "doc": {
    "email":"9999@aa"
  }
}

java相关的api操作

	 private RestHighLevelClient client;

    @BeforeEach
    void setUp() {
        this.client = new RestHighLevelClient(
                RestClient.builder(HttpHost.create("8.130.94.244:9200")));
    }

    @AfterEach
    void tearDown() throws IOException {
        this.client.close();
    }

    @Test
    void test1() throws IOException {
        //创建request对象
        CreateIndexRequest request = new CreateIndexRequest("hotel");
        //准备请求参数  dsl语句
        request.source(MAPPING_TEMPLATE, XContentType.JSON);
        //发送请求
        client.indices().create(request, RequestOptions.DEFAULT);
    }

    @Test
    void test2() throws IOException {
        //创建request对象
        DeleteIndexRequest request = new DeleteIndexRequest("hotel");
        //发送请求
        client.indices().delete(request, RequestOptions.DEFAULT);
    }

    @Test
    void test3() throws IOException {
        //创建request对象
        GetIndexRequest request = new GetIndexRequest("hotel");
        //发送请求
        boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
        System.out.println(exists);
    }

操作文档

    private RestHighLevelClient client;
    @Autowired
    private IHotelService iHotelService;
    @Test
    void test1() throws IOException {
        //查询数据库
        Hotel hotel = iHotelService.getById(61083L);
        //类型转换
        HotelDoc hotelDoc = new HotelDoc(hotel);
        //创建request对象
        IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString());
        //准备请求参数  dsl语句
        request.source(JSON.toJSONString(hotelDoc), XContentType.JSON);
        //发送请求
        client.index(request,RequestOptions.DEFAULT);
    }

    @Test
    void test2() throws IOException {
        //创建request对象
        GetRequest request = new GetRequest("hotel","61083");
        //发送请求
        GetResponse response = client.get(request, RequestOptions.DEFAULT);
        //解析
        String json = response.getSourceAsString();
        HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
        System.out.println(hotelDoc);
    }

    @Test
    void test3() throws IOException {
        //创建request对象
        UpdateRequest request = new UpdateRequest("hotel", "61083");
        //修改的参数
        request.doc(
                "price","1000",
                "starName","四钻"
        );
        //发送请求
        client.update(request, RequestOptions.DEFAULT);
    }
    @Test
    void test4() throws IOException {
        //创建request对象
        DeleteRequest request = new DeleteRequest("hotel", "61083");
        //发送请求
        client.delete(request, RequestOptions.DEFAULT);
    }
    @Test
    void test5() throws IOException {
        //查询数据
        List<Hotel> hotels = iHotelService.list();
        //创建request对象
        BulkRequest bulkRequest = new BulkRequest();
        //批量添加
        for (Hotel hotel : hotels) {
            HotelDoc hotelDoc = new HotelDoc(hotel);
            bulkRequest.add(new IndexRequest("hotel")
                    .id(hotelDoc.getId().toString())
                    .source(JSON.toJSONString(hotelDoc),XContentType.JSON));
        }
        //发送请求
        client.bulk(bulkRequest, RequestOptions.DEFAULT);
    }
    @BeforeEach
    void setUp() {
        this.client = new RestHighLevelClient(
                RestClient.builder(HttpHost.create("8.130.94.244:9200")));
    }

    @AfterEach
    void tearDown() throws IOException {
        this.client.close();
    }

DSL查询语法

1.查询所有

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

2.全文检索查询

  • 使用了copy_to all
GET /hotel/_search
{
  "query": {
    "match": {
      "all": "上海如家"
    }
  }
}

3.精确查询

GET /hotel/_search
{
  "query": {
    "term": {
      "city": "上海1"
    }
  }
}

4.范围查询

GET /hotel/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 100,
        "lte": 200
      }
    }
  }
}

5.位置查询

GET /hotel/_search
{
  "query": {
    "geo_distance": {
      "distance":"5km",
    "location": "31.21,121.5"
    }
  }
}

6.相关性算分

GET /hotel/_search
{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "all": "外滩"
        }
      },
      "functions": [
        {
          "filter": {
            "term": {
              "brand": "如家"
            }
          },
          "weight": 10
        }
      ],
      "boost_mode": "multiply"
    }
  }
}

7.复合查询

GET /hotel/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "如家"
          }
        }
      ],
      "must_not": [
        {
          "range": {
            "price": {
              "gt": 400
            }
          }
        }
      ],
      "filter": [
        {
          "geo_distance": {
            "distance": "10km",
            "location": {
              "lat": 31.21,
              "lon": 121.5
            }
          }
        }
      ]
    }
  }
}

8.排序

GET /hotel/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "score": "desc"
    },
    {
      "price": "asc"
    }
  ]
}

9.分页查询

GET /hotel/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "score": "desc"
    },
    {
      "price": "asc"
    }
  ],
  "from": "10",
  "size": 20
}

10.关键字高亮

GET /hotel/_search
{
  "query": {
    "match": {
      "all": "如家"
    }
  },
  "highlight": {
    "fields": {
      "all": {
        "require_field_match": "false"
      }
    }
  }
}

java的搜索 api

    private RestHighLevelClient client;

    @Test
    void test1() throws IOException {
        //创建request对象
        SearchRequest request = new SearchRequest("hotel");
        //准备请求参数  dsl语句
        request.source().query(QueryBuilders.matchAllQuery());
        //发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //解析数据
        SearchHits hits = response.getHits();
        //总数据数
        TotalHits total = hits.getTotalHits();
        System.out.println("总条数-----"+total);
        //具体数据
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : searchHits) {
            String json = hit.getSourceAsString();
            System.out.println(json);
        }
    }

    @Test
    void test2() throws IOException {
        //创建request对象
        SearchRequest request = new SearchRequest("hotel");
        //准备请求参数  dsl语句
        request.source().query(QueryBuilders.matchQuery("all","如家"));
        //发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //解析数据
        SearchHits hits = response.getHits();
        //总数据数
        TotalHits total = hits.getTotalHits();
        System.out.println("总条数-----"+total);
        //具体数据
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : searchHits) {
            String json = hit.getSourceAsString();
            System.out.println(json);
        }
    }

    @Test
    void test3() throws IOException {
        //创建request对象
        SearchRequest request = new SearchRequest("hotel");
        //准备请求参数  dsl语句
        BoolQueryBuilder booledQuery = QueryBuilders.boolQuery();
        //具体查询
        booledQuery.must(QueryBuilders.termQuery("city","上海"));
        //范围查询
        booledQuery.filter(QueryBuilders.rangeQuery("price").lte(200));
        request.source().query(booledQuery);
        //发送请求
        SearchResponse response = client.search(request,RequestOptions.DEFAULT);
        //解析数据
        SearchHits hits = response.getHits();
        //总数据数
        TotalHits total = hits.getTotalHits();
        System.out.println("总条数-----"+total);
        //具体数据
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : searchHits) {
            String json = hit.getSourceAsString();
            System.out.println(json);
        }
    }
    @Test
    void test4() throws IOException {
        //创建request对象
        SearchRequest request = new SearchRequest("hotel");
        //准备请求参数  dsl语句
        request.source().query(QueryBuilders.matchAllQuery());
        //排序 sort
        request.source().sort("price", SortOrder.ASC);
        //分页
        request.source().from(0).size(7);
        //发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //解析数据
        SearchHits hits = response.getHits();
        //总数据数
        TotalHits total = hits.getTotalHits();
        System.out.println("总条数-----"+total);
        //具体数据
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : searchHits) {
            String json = hit.getSourceAsString();
            System.out.println(json);
        }
    }
    @Test
    void test5() throws IOException {
        //创建request对象
        SearchRequest request = new SearchRequest("hotel");
        //准备请求参数  dsl语句
        request.source().query(QueryBuilders.matchQuery("all","如家"));
        request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
        //发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //解析数据
        SearchHits hits = response.getHits();
        //总数据数
        TotalHits total = hits.getTotalHits();
        System.out.println("总条数-----"+total);
        //具体数据
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : searchHits) {
            String json = hit.getSourceAsString();
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (!CollectionUtils.isEmpty(highlightFields)){
                HighlightField name = highlightFields.get("name");
                String string = name.getFragments()[0].toString();
                System.out.println(string);
                System.out.println(json);
            }
        }
    }
    @BeforeEach
    void setUp() {
        this.client = new RestHighLevelClient(
                RestClient.builder(HttpHost.create("8.130.94.244:9200")));
    }

    @AfterEach
    void tearDown() throws IOException {
        this.client.close();
    }
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    //解析数据
    SearchHits hits = response.getHits();
    //总数据数
    TotalHits total = hits.getTotalHits();
    System.out.println("总条数-----"+total);
    //具体数据
    SearchHit[] searchHits = hits.getHits();
    for (SearchHit hit : searchHits) {
        String json = hit.getSourceAsString();
        Map<String, HighlightField> highlightFields = hit.getHighlightFields();
        if (!CollectionUtils.isEmpty(highlightFields)){
            HighlightField name = highlightFields.get("name");
            String string = name.getFragments()[0].toString();
            System.out.println(string);
            System.out.println(json);
        }
    }
}
@BeforeEach
void setUp() {
    this.client = new RestHighLevelClient(
            RestClient.builder(HttpHost.create("8.130.94.244:9200")));
}

@AfterEach
void tearDown() throws IOException {
    this.client.close();
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值