文章目录
- 微服务之间如何沟通
- Eureka注册中心
- 搭建eureka注册中心
- Ribbon
- 加载机制
- Nacos注册中心
- nacos的启动
- 集群配置
- 配置选择服务规则负载均衡
- 环境隔离-namespace
- 修改非临时实例
- Nacos配置管理
- 多环境配置共享
- Nacos集群的搭建
- Http客户端Feign
- 怎么优化feign(默认的没有连接池)
- 统一网关Gateway
- 创建gateway模块
- 路由断言工厂
- 过滤器
- 全局过滤器代码实现
- 执行顺序
- 解决跨域问题
- Docker
- 什么是镜像容器等
- Docker基本操作
- 数据卷
- 自定义镜像结构
- docker-compose
- Elasticsearch
- 索引库的操作
- 文档操作
- java相关的api操作
- 操作文档
- DSL查询语法
- java的搜索 api
微服务之间如何沟通
-HttpClient
- Spring框架提供了一个httpclient对象RestTemplate,发起一个http的请求。
- 创建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.配置
-
第一种配置方式(配置类申明)
@Bean public IRule iRule(){ return new RandomRule(); }
-
第二种配置文件申明
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
分析
修改非临时实例
- 临时实例:自己主动向nacos发送请求表示自己还是正常的 挂了nacos就会自动剔除服务
- 非临时实例: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.热更新
- 使用@refreshscope注解实现热更新
- 使用@configrationproperties
多环境配置共享
Nacos集群的搭建
Http客户端Feign
为什么更换restTemplate
- 代码可读性差,编程体验不统一
- 参数复杂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
- 身份认证和权限校验
- 服务路由,负载均衡
- 请求限流
创建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
- 决各种服务之间依赖的冲突和版本控制
什么是镜像容器等
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
数据卷
- 是一个虚拟目录,指向宿主机文件系统的一个真实的目录(方便配置的修改)
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();
}