微服务技术栈学习笔记(自用)

微服务技术栈学习笔记(自用)

1.导学

  • 微服务技术:

在这里插入图片描述

  • 持续集成:

自动化编译(Jenkins)+打包(Docker)+自动化部署(kubernetes/rancher)

  • 特点:

在这里插入图片描述

  • 技术对比:

在这里插入图片描述

常用:SpringCloud+Feign、SpringCloudAlibaba+Feign(服务接口为Restful风格)

SpringCloudAlibaba+Dubbo、Dubbo原始模式(服务接口为Dubbo方式)

SpringCloud:基于Springboot实现了各种微服务组件的自动装配

2.Eureka

2.1微服务远程调用:

RestTemplate(发送各种http请求,使服务之间可以通信)

OrderApplication.java:
    //创建RestTemplate并注入容器
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

OrderService.java:
    public Order queryOrderById(Long orderId) {
        // 1.查询订单
        Order order = orderMapper.findById(orderId);
        //2.发起http请求,查询用户
        String url = "http://localhost:8081/user/" + order.getUserId();
        User user = restTemplate.getForObject(url, User.class);
        //3.封装
        order.setUser(user);
        // 4.返回
        return order;
    }

2.2搭建Eureka

在这里插入图片描述

  1. 搭建注册中心:
pom.xml:        
<!--eureka服务端-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

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

application.yml:
server:
  port: 10086
spring:
  application:
    name: eurekaserver
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka  # 把自己也注册到eureka上(集群用)

2.服务注册:

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

spring:
  application:
    name: orderservice
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka  # 注册

多实例注册:

在某一实例中右击Copy Configuration

并修改相应端口(VM options: -Dserver.port=8082)

3.服务发现:

orderservice.java:
//根据id查询
String url = "http://userservice/user/" + order.getUserId();
User user = restTemplate.getForObject(url, User.class);

OrderApplication.java:
	@Bean
    @LoadBalanced	//负载均衡设置
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

3.Ribbon负载均衡

  • 底层流程:

在这里插入图片描述

  • 策略类型:

在这里插入图片描述

默认为轮询机制

  • 负载均衡策略:
//代码方案(作用于全局:所有服务)
OrderApplication.java:
@Bean
public IRule randomRule(){
	return new RandomRule();
}

//配置文件方案
order-service的application.yml:
userservice:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule  # 负载均衡策略
  • 饥饿加载:

项目启动时就开始创建LoadBalanceClient,降低第一次访问的耗时

(Ribbon默认为懒加载:第一次访问才创建LoadBalanceClient,请求时间很长)

ribbon:
  eager-load:
    enabled: true	# 开启饥饿加载
    clients: userservice	# 指定对userservice这个服务采用饥饿加载

4.Nacos

  • 下载解压:https://github.com/alibaba/nacos/releases

bin:启动脚本

conf:配置文件(默认端口为8848,此处修改端口)

  • 单机启动:startup.cmd -m standalone

访问:默认账号和密码都是nacos

4.1服务注册

//父工程
<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>

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

//application.yaml:
  cloud:
    nacos:
      server-addr: localhost:8848

4.2 分级存储

服务-----集群-----实例

服务集群属性:

  cloud:
    nacos:
      server-addr: localhost:8848
      discovery:
        cluster-name: SH	# 集群设置
        
//然后在nacos服务详情中即可查看
  • 根据集群负载均衡:
userservice:
  ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
    
//注意将服务权重均设为1 
//当跨集群访问发生时,控制台中会有黄色提示
  • 实例权重设置:直接在nacos服务中设置即可(0~1)

环境隔离:注意设置的是命名空间id

//新建命名空间:直接在nacos命名空间中新建即可

//配置
  cloud:
    nacos:
      server-addr: localhost:8848 # nacos服务地址
      discovery:
        cluster-name: HZ
        namespace: f7f1ce9b-8d30-4250-a656-850a8e17d82b # dev命名空间id
  • 临时实例:一旦挂了就消除了相关记录(如果为非临时的话,在主动删除之前一直保有记录)
//配置
  cloud:
    nacos:
      server-addr: localhost:8848 # nacos服务地址
      discovery:
        ephemeral: false # 设置为非临时实例

在这里插入图片描述

在这里插入图片描述

配置管理服务:配置更改热更新

直接在配置管理中添加设置即可

[服务名称]-[profile].[后缀名]

  • 配置拉取:
<!--        nacos的配置管理依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
     
# 必须是这个名称     
bootstrap.yaml:     
spring:
  application:
    name: userservice
  profiles:
    active: dev # 环境
  cloud:
    nacos:
      server-addr: localhost:8848 # nacos地址
      config:
        file-extension: yaml # 文件后缀名        

# 检验日期配置用
    @Value("${pattern.dateformat}")
    private String dateformat;

    @GetMapping("now")
    public String now(){
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat));
    }
  • 配置热更新:

第一种方式是通过配置文件方式(PatternProperties.java);

第二种方式是通过注解@Value(“${yaml里定义的键值对}”)的方式

优先级:Nacos带环境的配置 > Nacos不带环境的配置 > 本地yaml文件配置

Nacos带环境可以理解为专属化配置(开发环境和生产环境)、肯定优先于Nacos不带环境的全局配置;

  • 集群搭建:
cluster.conf:
# 自己的IP和端口号
127.0.0.1:8845
127.0.0.1:8846
127.0.0.1:8847

Nacos的application.properties:
spring.datasource.platform=mysql
db.num=1
db.url=xxx
xxx		# 配置自己的数据源
...
server.port=8845
server.port=8846
server.port=8847	# 配置端口

5.Feign

引入:

		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>2.2.9.RELEASE</version>
        </dependency>

自定义配置:

# 配置文件方式
feign:
  client:
    config:
      default:	# 全局生效
        loggerLevel: FULL # 日志级别
        
feign:
  client:
    config:
      userservice:	# 局部生效
        loggerLevel: FULL # 日志级别        
        
//代码方式
//先声明一个Bean
public class FeignClientConfiguration{
	@Bean
	public Logger.Level feignLogLevel(){
		return Logger.Level.BASIC
	}
}
//全局
@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)
//局部
@FeignClients(value = "userservice" , configuration = FeignClientConfiguration.class)

优化:

底层的客户端实现是

URLConnection(默认实现,不支持连接池)
Apache HttpClient(支持连接池,常用)
OKHttp(支持连接池)

第一种方式是默认的,不支持连接池。

所以性能优化指的是:换成第二种方式或者第三种方式。

其中第二种方式 Apache HttpClient 常用于模拟postman的样式,发送一个form-data样式的post请求,也可在这个post请求里上传文件。
步骤:

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

feign:
  httpclient:
    enabled: true # 支持HttpClient的开关
    max-connections: 200 # 最大连接数
    max-connections-per-route: 50 # 单个路径的最大连接数

6.Gateway

功能:身份认证和权限校验;服务路由、负载均衡;请求限流

在SpringCloud中网关技术包括两种:gateway和zuul

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

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

6.1搭建

新建模块

# 配置文件:

server:
  port: 10010
spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848 # nacos地址
    gateway:
      routes:
        - id: user-service # 路由标识,必须唯一
          uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟着是`服务名称`
          predicates: # 路由断言,判断请求是否符合规则
            - Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是则符合规则
        - id: order-service
          uri: lb://orderservice
          predicates:
            - Path=/order/**

流程:

在这里插入图片描述

6.2路由断言工厂

在这里插入图片描述

如果请求不符合路由断言,那请求就会被拒绝,返回一个404。

因此可以通过配置路由断言工厂的方式来过滤某些请求。

6.3 过滤器

过滤器还可对服务响应进行过滤

在这里插入图片描述

在这里插入图片描述

全局过滤器GlobalFilter:可以自定义过滤逻辑代码实现

public interface GlobalFilter{
//exchange 请求上下文,可获取请求、响应信息
//chain 把请求委托给下一个过滤器
	Mono<Void> filter(ServerWebExchange exchange, GatewayFilter chain);
}

过滤器链执行顺序:

在这里插入图片描述

跨域问题处理

域名不一致就是跨域:域名不同;域名相同,端口不同

浏览器禁止请求的发起者和服务端发生跨域ajax请求(只有ajax请求会被浏览器拦截)

解决方案:CORS配置

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

8.ElasticSearch

8.1 倒排索引

在这里插入图片描述

类似于查字典,拿到一本字典的目录,不会按页码挨个查找(正向索引),

而是先看目录的关键字,然后找到关键字之后,再去看关键字旁边的页码,最后再根据页码翻到书对应的那一页

概念:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sp679X1m-1668340524144)在这里插入图片描述

架构:

Mysql:擅长事务类型操作,可确保数据安全性与一致性

ES:海量数据搜索、分析、计算

写入数据用Mysql,读取数据(搜索查找)用ES,Mysql与ES保持数据同步

8.2 安装部署

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

解释:

配置堆内存(JVM)。因为ES底层是java实现的,所以要配置jvm内存大小。默认值是1T,对于轻量级服务器太大了,所以适当减少为512M-

单点模式运行(区别于集群模式运行)

数据卷挂载,分别是数据保存目录(data),和插件目录(plugins)

将ES容器加入到创建的docker网络中

9200是用户访问的http协议端口,9300是ES容器节点互联的端口

8.3 分词器配置

选择IK分词器(来源于github,专门适配了中文)

作用:对文档进行分词;对输入内容进行分词

IK的模式:ik_smart(智能切分);ik_max_word(最细切分)

拓展/停用词条:config目录的IkAnalyzer.cfg.xml文件设置

8.4 索引库操作

mapping映射属性

在这里插入图片描述

创建索引库PUT

# 创建索引库
PUT /heima
{
  "mappings": {
    "properties": {
      "info": {
        "type": "text",
        "analyzer": "ik_smart"
      },
      "email": {
        "type": "keyword",
        "index": false
      },
      "name": {
        "type": "object",
        "properties": {
          "firstName": {
            "type": "keyword"
          },
          "lastName": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

查看索引库:GET /索引库名

删除索引库:DELETE /索引库名

修改索引库从设计上被禁止了,索引库和mapping一旦创建无法修改,

但是可以用PUT添加新的字段 (该字段必须是全新的字段)

8.5 文档操作

# 插入文档
POST /heima/_doc/1
{
  "info": "黑马程序员java讲师",
  "email": "112837@qq.com",
  "name":{
    "firstName":"云",
    "lastName":"赵"
  }
}

# 查询
GET /heima/_doc/1

# 删除
DELETE /heima/_doc/1

每次写操作的时候,都会使得文档的"_version"字段+1

全量修改:用PUT(用POST的话就是新建了)

# 插入一个文档
PUT /heima/_doc/1
{
  "info": "黑马程序员java讲师",
  "email": "112837@qq.com",
  "name":{
    "firstName":"云",
    "lastName":"赵"
  }
}

//如果id在索引库里面不存在,并不会报错,而是直接新增
//如果索引库存在该记录,就会先删掉该记录,然后增加一个全新的。

增量修改:用POST

# 局部修改文档字段:必须跟一个doc
POST /heima/_update/1
{
  "doc": {  
    "email":"lbwnb@qq.com"
  }
}

8.6 用RestClient操作索引库和文档

编写DSL语句

# 酒店的mapping
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"
      },
      "starName":{
        "type": "keyword"
      },
      "business":{
        "type": "keyword",
        "copy_to": "all"
      },
      "location":{
        "type": "geo_point"	# 地理位置特殊数据类型
      },
       "pic":{
        "type": "keyword"
        , "index": false
      },
      "all":{
        "type": "text",
        "analyzer": "ik_max_word"
      }
    }
  }
}

index如果设置成false,则既不参与索引也不参与分词

索引库的id总是被要求成keyword(也就是String)类型,即使数据库的主键id可能是int

DSL查询语法:

在这里插入图片描述

# match 和 multi_match	全文检索
GET /hotel/_search
{
  "query": {
    "match": {
      "address": "如家外滩"
    }
  }
}

GET /hotel/_search
{
  "query": {
    "multi_match": {
      "query": "外滩如家",
      "fields": ["brand","name","business"]
    }
  }
}
# 精确查询(term查询)
GET /hotel/_search
{
  "query": {
    "term": {
      "city": {
        "value": "上海"
      }
    }
  }
}

# 精确查询(范围range)
GET /hotel/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 100,
        "lte": 300
      }
    }
  }
}

8.7 打分算法

文档结果根据关联度打分返回

在这里插入图片描述

eg:使"如家"排名靠前

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

复合查询:

在这里插入图片描述

算分条件越多,性能就会越差。所以能使用filter的就别使用must,能不算分就不算分

eg:包含“如家”,价格不高于400,在坐标31.21,121.5周围10km范围内的酒店

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

搜索结果处理:

排序:“FIELD”: “asc” “FIELD”: “desc”

分页:

“from”: 20

, “size”: 5

高亮:“highlight”

深度分页问题

在这里插入图片描述

解决方案:

在这里插入图片描述

Java RestClient查询语法:

要构建查询条件,只需记住一个类:QueryBuilders。
要构建搜索DSL,只需记住一个API:SearchRequest的source()方法(支持链式编程)

高亮结果解析:

在这里插入图片描述

8.8 数据聚合

类似于MySQL的group by(对数据的统计分析和计算)

聚合不能是text类型,不能分词

在这里插入图片描述

1.Bucket聚合

# 聚合功能
GET hotel/_search
{
  "size": 0,
  "aggs": {
    "brandAgg": {
      "terms": {
        "field": "brand",
        "size": 10,
        "order": {
          "_count": "asc"  #结果按照count升序排列
        }
      }
    }
  }
}

2.Metric聚合

# 嵌套聚合metric
GET hotel/_search
{
  "size": 0,
  "aggs": {
    "brandAgg": {
      "terms": {
        "field": "brand",
        "size": 10,
        "order": {
          "scoreAgg.avg": "asc"   # 根据下面的子聚合结果的avg进行升序排序
        }
      },
      "aggs": {
        "scoreAgg": {
          "stats": {
            "field": "score"
          }
        }
      }
    }
  }
}

Java Restclient对应Json的图例:

在这里插入图片描述

在这里插入图片描述

8.9 数据补全

补全分词器(https://github.com/medcl/elasticsearch-analysis-pinyin)

拼音适合创建倒排索引使用,不能在搜索中使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CrkfvlFe-1668340524156)(C:\Users\29851\Desktop\学习笔记\微服务技术栈学习笔记\assets\ES13)]

因此在创建时用my_analyzer,搜索时用ik_smart

8.10 ES与Mysql之间的数据同步

在微服务中,操作MySQL的业务和操作ES的业务可能在不同的微服务上,这种情况应该怎么实现数据同步呢?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fZMpy7cf-1668340524157)(C:\Users\29851\Desktop\学习笔记\微服务技术栈学习笔记\assets\ES14)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UevAmcQN-1668340524158)(C:\Users\29851\Desktop\学习笔记\微服务技术栈学习笔记\assets\ES15)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nOS76oAZ-1668340524159)(C:\Users\29851\Desktop\学习笔记\微服务技术栈学习笔记\assets\ES16)]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值