springcloud
开始
首先我们使用微服务的话,需要一个父工程,父工程不需要什么东西,但需要一个pom.xml文件来聚合这些依赖
添加或者修改他
<packaging>pom</packaging>
然后<properties>来定义依赖的版本
<dependencyManagement> 里面包含的依赖是可以被子项目给依赖的,如果子项目直接<artifactId>引入则使用的是父工程的,如果把坐标的信息写的完整且有版本号,则子项目使用的是自己的依赖
本次使用的坐标 父工程
<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>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.16.18</lombok.version>
<mysql.version>5.1.47</mysql.version>
<druid.version>1.1.16</druid.version>
<mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
</properties>
<dependencyManagement>
<dependencies>
<!--spring boot 2.2.2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud Hoxton.SR1-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud alibaba 2.1.0.RELEASE-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>·</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.boot.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
</dependencies>
</dependencyManagement>
Eureka
坐标跟换
客户端
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
服务端
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
其他的跟老版本的一样,但是eureka已经停止更新了,我们需要找一个可以替代他的产品,如zookeeper和consul
zookeeper
安装zookeeper
坐标
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
注意,出现了那安装的zookeeper和maven的版本不一致的会报错,所以我们可以吧zookeeper给剔除
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version> <!--引入和安装的zookeeper相同的版本-->
</dependency>
yml
------------------
spring:
application:
name: 服务名称
cloud:
zookeeper:
connect-string: 127.0.0.1:2181 #zookeeper的地址,2181默认端口
在启动类添加注解,@EnableDiscoveryClient
Consul
作用
服务发现:提供http和dns两种发现方式
使用
下载
https://www.consul.io/downloads
安装
使用
https://www.springcloud.cc/spring-cloud-consul.html
启动后可以访问8500端口可以查看服务
新建子工程
添加坐标
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
其他基础坐标就不说了
yml
-------------------
spring:
application:
name: 服务名称
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
默认端口就是8500
启动类依然加上@EnableDiscoveryClient注解
我们的服务提供者和消费者都是同样的配置
三个注册中心的区别
cap
c:Consistency强一致性
a:Availability 可用性
p:partition tolerance 分区容错性
该理论关注的粒度是数据,不是设计的策略
ap eureka
cp zookeeper/consul
补充:
注意,当使用ribbon时自定义策略时,那么该配置不能再@ComponentScan所扫描的包下及其子包
替换策略时还可以这样
在主启动类上加
@RibbonClient(name = "服务名称",configuration = MySelfRule.class)
MySelfRule是负载均衡策略
openFeign
openFeign是一个声明式的webService客户端,是定义一个服务接口然后在上面添加注解,
支持springmvc标准注解,
使用
--------------
坐标
---------------------
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
里面整合了ribbon,hystrix
启动类
--------------
@EnableFeignClients
yml
----------------------
openfigen默认等一秒,过后就超时
#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络状况正常的情况下, 两端连接所用的时间
ReadTimeout: 5000
#指的是建立连接后从服务器读取到可用资源所用的时间
ConnectTimeout: 5000
接口
----------
@Component
@FeignClient(value = "服务名称")
public interface PaymentFeignService {
@GetMapping(value = "/payment/get/{id}")
CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
@GetMapping(value = "/payment/feign/timeout")
String paymentFeignTimeout();
}
日志
openfigen提供了日志打印功能,对openfeign的接口调用情况进行监控输出
日志级别:
NONE :默认,不显示任何日志
BASIC:仅记录请求方法,url,响应状态码及执行时间
HEADERS:除了BASIC中定义的信息之外,还有请求响应的头信息
FULL:除了HEADRES中定义信息外,还有请求响应的正文及元数据
配置
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
yml需要开启日志的feign客户端
logging:
level:
com.atguigu.springcloud.service.PaymentFeignService: debug
gateway新一代网关
注意是springboot2.x版本使用的作用
-----------------
反向代理
鉴权
流量控制
熔断
日志监控
特性
--------------
动态路由,能够匹配任何请求
可以对路由指定Predicate(断言)和Filter(过滤器)
集成Hystrix的断路器功能
集成cloud的服务发现功能
支持路径重写,
请求限流功能
与zuul.x的区别
zuul是一个基于阻塞i/o的api gateway gateway不阻塞
gateway模型 spring webflux是一个非阻塞的响应式框架
核心
1.路由 路由是构建网关的基本模块,由id,目标uri,一系列的断言和过滤器组成,断言为!true则匹配该路由, 2.断言 如果请求与断言相匹配则进行路由 3.过滤 使用过滤器,可以在请求被路由前或者之后队请求进行修改新建工程
基础坐标这里就不说了
加入网关
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
yml
--------------------------
spring:
application:
name: 服务名称
cloud:
gateway:
discovery:
locator:
enabled: false #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2
uri: http://localhost:8001
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
访问
localhost:9527/payment/get/1 由于上面我们配置了这个断言,
所以该请求会被路由到http://localhost:8001服务上的/payment/get/1 接口上
或者换成这个
uri:lb://cloud-payment-service 指定服务名称 ,不写端口和ip
注意:lb表示启用负载均衡的功能
路由的第二种配置方式
创建RouteLocator的bena
@Configuration
public class GateWayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
routes.route("path_route_atguigu", r -> r.path("/payment/get/**").uri("lb://cloud-payment-service"))
.build();
return routes.build();
}
}
这样子路由也是可以的
Route Predicate Factories
Gateway内置了多种路由谓词工厂,用来匹配http请求。当多个一起使用时,是and的关系,即需要所有的谓词工厂都匹配,才匹配这个路由1.After
表示当前请求在指定时间之后才匹配
datetime 数据类型是 ZonedDateTime,带有时区
使用
predicates:
- After=2020-11-01T11:05:08.020+08:00[Asia/Shanghai]
2.Before
则和上面的相反,表示当前请求在指定时间之前才匹配
predicates:
- Before=2020-11-01T11:05:08.020+08:00[Asia/Shanghai]
3.Between
当前请求在指定时间中间才匹配,并且date1要小于date2
predicates:
- Between=2020-11-01T11:08:08.020+08:00[Asia/Shanghai],2020-11-01T11:15:08.020+08:00[Asia/Shanghai]
4.Cookie
当前请求中的cookie值匹配配置的cookie参数值时生效
predicates:
- Cookie=token,tokenvalue\d+
token 是请求cookie中的参数的名字
tokenvalue是 请求cookie中的参数的值,配置的值是Java中的正则表达式形式。
d+表示后面可以加数字,仅数字
5.Header
当前请求中的header值匹配配置的header参数值时生效
predicates:
- Header=X-Token-Id,\d+
表示请求头中必须存在X-Token-Id且它的值必须是数字
6.Host
匹配请求头中的Host的值
predicates:
- Host=**.gateway.com,{study}.baidu.com
**.gateway.com 表示请求中的Host的值需要配置这种ant风格
{study}.baidu.com study这个模版变量可以在GatewayFilter中获取到
ServerWebExchange.getAttributes().get(ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE)获取
7.Method
匹配请求头中的Method的值,methods 需要匹配的方法,比如GET、POST等
predicates:
- Method=GET,POST,PUT # 只有get,post,put请求才能匹配上方这个路由
8.Path
匹配请求路径。
predicates:
- Path=/product/findOne/{productId},/product//**
支持uri模版变量
productId可以在GatewayFilter中获取
Map<String, String> uriVariables = ServerWebExchangeUtils.getPathPredicateVariables(exchange);
String segment = uriVariables.get("productId");
9.Query
匹配请求参数
predicates:
#- Query=pwd
- Query=username,\d+
Query=pwd 表示请求中必须存在 pwd这个请求参数
Query=username,\d+ 表示请求中必须存在username这个参数,且它的值必须是数字
10.RemoteAddr
匹配请求的ip地址,支持ipv4和ipv6。
predicates:
- RemoteAddr=127.0.0.1/16
sources 地址列表eg:127.0.0.1/16 后方的/16是子网掩码
11.Weight
根据权重来分发请求,权重是根据group来计算的。
- id: product-provider
uri: https://weighthigh.org
predicates:
- Weight=group1,6
- id: user-consumer
uri: https://weightlow.org
predicates:
- Weight=group1,4
group 组,权重根据组来计算
weight 权重值,是一个 Int 的值
60%的请求会落在product-provider, 40%的请求会落在user-consumer
自定义route predicate factory
编写一个类,实现RoutePredicateFactory接口,
或继承AbstractRoutePredicateFactory。
1.AbstractRoutePredicateFactory 中的 C 表示 配置文件类。
2.重写shortcutFieldOrder此方法,表示的配置文件中参数的位置
3.重写apply(C c)方法,表示的是具体的业务逻辑
我们自己编写的类必须要以RoutePredicateFactory结尾
@Slf4j
@Component
public class TokenRoutePredicateFactory extends AbstractRoutePredicateFactory<TokenRoutePredicateFactory.Config> {
public TokenRoutePredicateFactory() {
super(Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Lists.newArrayList("gww");
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
log.info("判断请求头中是否存在gww这个参数");
String gww = exchange.getRequest().getQueryParams().getFirst(config.getGww());
return StringUtils.isNotBlank(gww);
};
}
@Data
public static class Config {
private String gww;
}
}
使用
------------------
predicates:
- gww=gww
路由过滤器
gateway内置了多种过滤器,他们都由gatewayfilter的工厂类来产生1.AddRequestHeader 过滤器工厂
通过名称我们可以快速明白这个过滤器工厂的作用是添加请求头。
符合规则匹配成功的请求,将添加 X-Request-Foo:bar 请求头,将其传递到后端服务中,后方服务可以直接获取请求头信息
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: http://c.biancheng.net
filters:
- AddRequestHeader=X-Request-Foo, Bar
2.RemoveRequestHeader 过滤器工厂
RemoveRequestHeader 是移除请求头的过滤器工厂,可以在请求转发到后端服务之前进行 Header 的移除操作。
spring:
cloud:
gateway:
routes:
- id: removerequestheader_route
uri: http://c.biancheng.net
- RemoveRequestHeader=X-Request-Foo
3.SetStatus 过滤器工厂
SetStatus 过滤器工厂接收单个状态,用于设置 Http 请求的响应码
spring:
cloud:
gateway:
routes:
- id: setstatusint_route
uri: http://c.biancheng.net
filters:
- SetStatus=401
4.RedirectTo过滤器工厂
RedirectTo 过滤器工厂用于重定向操作,比如我们需要重定向到百度。
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: http://c.biancheng.net
filters:
- RedirectTo=302, http://baidu.com
自定义Spring Cloud Gateway过滤器工厂
需要继承 AbstractGatewayFilterFactory 类,重写 apply 方法的逻辑,命名需要以 GatewayFilterFactory 结尾@Component
public class CheckAuth2GatewayFilterFactory
extends AbstractGatewayFilterFactory<CheckAuth2GatewayFilterFactory.Config> {
public CheckAuth2GatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
System.err.println("进入了CheckAuth2GatewayFilterFactory" + config.getName());
ServerHttpRequest request = exchange.getRequest().mutate()
.build();
return
chain.filter(exchange.mutate().request(request).build());
}
}
public static class Config {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
}
使用如下:
filters:
- name: CheckAuth2
args:
name: 张三
如果你的配置是 Key、Value 这种形式的,那么可以不用自己定义配置类,直接继承 AbstractNameValueGatewayFilterFactory 类即可。
AbstractNameValueGatewayFilterFactory 类继承了 AbstractGatewayFilterFactory,定义了一个 NameValueConfig 配置类,NameValueConfig 中有 name 和 value 两个字段。
继承 AbstractNameValueGatewayFilterFactory 方式定义过滤器工厂
@Component
public class CheckAuthGatewayFilterFactory extends AbstractNameValueGatewayFilter-actory {
@Override
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> {
System.err.println("进入了CheckAuthGatewayFilterFactory" + config.getName() + "\t" + config.getValue());
ServerHttpRequest request = exchange.getRequest().mutate().build();
return chain.filter(exchange.mutate().request(request).build());
};
}
}
使用:
filters:
- CheckAuth=zhangsan,男