目录
一.微服务技术栈
1.SpringCloud Config 配置中心
集中管理配置文件(Git)
结合SpringCloud Bus动态刷新配置
2.安全控制
SpringCloud SpringSecurity/OAuth2
单点登录、统一认证、资源授权
JWT
同行令牌、身份信息载体
3.限流、降级
Hystrix
SpringCloud Alibaba Sentinel
4.Spring Cloud Alibaba阿里系技术栈集成
SpringCloud Alibaba RocketMQ
SpringCloud Alibaba Seata
SpringCloud Alibaba Nacos
SpringCloud Alibaba Sentinel
SpringCloud Alibaba Dubbo
SpringCloud Alibaba Sidecar
5.链路追踪
Sleuth+Zipkin
skywalking
6.消息组件
SpringCloud Stream
发布与订阅
消息分片
分组消费
集成MQ
SpringCloud Bus数据总线
基于SpringCloud Stream
多服务事件通信
7.微服务网关
Zuul
SpringCloud Gateway
8.服务调用
OpenFeign
服务间调用(Http协议)
支持数据压缩
RestTemplate
Http请求工具
Rest API风格操作
Ribbon
客户端负载均衡
9.注册中心与配置中心
SpringCloud Eureka
SpringCloud Alibaba Nacos
Spring Cloud Consul
Spring Cloud Zookeeper
二.微服务部分技术栈现状
1.Eureka闭源
Eureka 2.0 的开源工作已经停止,依赖于开源库里面的 Eureka 2.x 分支构建的项目或者相关代码,风险自负。
Eureka在微服务项目中主要承担服务注册与发现工作,可以替代的技术方案比较多,而且很多方案都比Eureka优秀,比如Consul、Nacos等。
2.Hystrix停止更新
官方宣布将不在开发,我们更推荐使用功能更加强悍的 SpringCloud Alibaba Sentinel 。
3.Zuul过时
Zuul 是一个微服务网关技术,但 Zuul1.x 使用的是阻塞式的API,不支持长连接,没有提供异步,高并发场景下性能低。 SpringCloud 官网推出了全新的微服务网关技术 SpringCloud Gateway ,比 Zuul 性能更强悍、功能更丰富、且支持异步等多种特性。
4.SpringCloud Config实用性差
SpringCloud Config 主要用于管理项目的配置文件,每次要使用 SpringCloud Config 的时候,总得经过一波操作和配置的折腾,才可以使用 SpringCloud Config 实现配置管理,而且单独使用无法支持配置实时刷新,在项目中用起来,真比较头疼的。
当前有很多技术可以取代 SpringCloud Config ,比如携程的 Apollo 、 SpringCloud Alibaba Nacos ,功能都比 SpringCloud Config 强,并且支持实时刷新配置。
5.SpringCloud Bus实用性差
SpringCloud Bus是服务消息总线,主要实现通知多个服务执行某个任务,一般和SpringCloud Config一起使用。这个功能其实不太使用,因为很多任务组件基本都具备消息通知功能,比如Nacos、Apollo都能实现所有服务订阅执行相关操作。
三.微服务注册中心之Consul
1.Consul简介
Consul 用于实现分布式系统的服务发现与配置。与其它分布式服务注册与发现的方案相比,Consul 的方案更“一站式”,内置了服务注册与发现框架、分布一致性协议实现、健康检查、Key/Value 存储、多数据中心方案,不再需要依赖其它工具(比如 ZooKeeper 等)。使用起来也较 为简单。Consul 使用 Go 语言编写,因此具有天然可移植性(支持 Linux、windows和Mac OS X);安装包仅包含一个可执行文件,方便部署,与 Docker 等轻量级容器可无缝配合。
2.consul官网连接
3.consul角色
client: 客户端, 无状态, 将 HTTP 和 DNS 接口请求转发给局域网内的服务端集群。
server: 服务端, 保存配置信息, 高可用集群, 在局域网内与本地客户端通讯, 通过广域网与其它数据中心通讯。 每个数据中心的 server 数量推荐为 3 个或是 5 个。
4.consul安装(Windows下安装步骤)
(1)下载地址:
Downloads | Consul by HashiCorp
(2)配置环境变量:将解压后consul.exe所在的目录添加到环境变量Path中
(3)启动consul:cmd运行 consul agent -dev
(4)访问:http://localhost:8500
(5)启动优化:按照上面的命令启动,有个问题就是每次consul启动,之前的配置就不存在了,这里可以用新的启动方法代替
consul agent -server -bootstrap-expect=1 -ui -bind=127.0.0.1 -client=0.0.0.0 -data-dir=D:/develop/consul/data -config-dir=D:/develop/consul
这样每次重启consul,配置信息依然存在。
(6)启动脚本编写
为了方便后续启动,可以编写启动脚本start.bat,脚本内容如下
@echo off
color 09
consul agent -server -bootstrap-expect=1 -ui -bind=127.0.0.1 -client=0.0.0.0 -data-dir=D:/develop/consul/data -config-dir=D:/develop/consul
(7)项目中配置consul
四.微服务网关SpringCloud Gateway
1.概述
微服务网关就是一个系统,通过暴露该微服务网关系统,方便我们进行相关的鉴权,安全控制,日志统一处理,易于监控,限流等相关功能。
2.实现微服务网关的技术
Nginx,zuul,SpringCloud Gateway
3.Gateway路由配置
基于配置路由设置如下
4.Gateway-Predicate配置详解
在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性实现了各种路由匹配规则,通过 Header、请求参数等不同的条件来作为条件匹配到对应的路由。
(1) Cookie配置:
Gateway的Cookie匹配接收两个参数:一个是 Cookie name ,一个是正则表达式。路由规则就是通过获取对应的 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行。如下配置,要求请求携带cookie为username的数据,并且值为itheima,就允许通过。
gateway:
routes:
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
predicates:
- Path=/driver/**
- Cookie=username,itheima
(2)Header 匹配:
Header 匹配 和 Cookie 匹配 一样,也是接收两个参数,一个 header 中属性名称和一个正则表达式,这个属性值和正则表达式匹配则执行。配置如下,就是要求请求头要有token属性,并且值必须为数字和字母组 合的正则表达式,例如携带token= 19and30 就可以通过访问。
gateway:
routes:
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
predicates:
- Path=/driver/**
- Header=token,^(?!\d+$)[\da-zA-Z]+$
(3)Method请求方式匹配:
通过请求的方式是 POST、GET、PUT、DELETE 等进行路由。配置如下:
gateway:
routes:
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
predicates:
- Path=/driver/**
- Method=GET,POST
5.Gateway过滤器
(1)Spring Cloud Gateway根据作用范围划分为GatewayFilter和 GlobalFilter。
过滤器作为Gateway的重要功能。常用于请求
鉴权
、
服务调用时长统计
、
修改请求或响应header
、
限流
、
去除路径
等等。
(2)
GatewayFilter
配置方式1:通过spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上
配置方式2:通过spring.cloud.default-filters配置在全局,作用在所有路由上。
(3)
GlobalFilter
无需配置:全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器,它为请求业务以及路由的URI转换为真实业务服务的请求地址的核心过滤器,不需要配置,系统初始化时加载,并作用在每个路由上。
(4)过滤器分类:
默认过滤器:出厂自带,实现好了拿来就用,不需要实现
全局默认过滤器
局部默认过滤器
自定义过滤器:根据需求自己实现,实现后需配置,然后才能用哦。
全局过滤器:作用在所有路由上。
局部过滤器:配置在具体路由下,只作用在当前路由上。
过滤器有很多,常见的过滤器有如下几个
(5)默认过滤器的使用(系统自带的过滤器)
<1> 添加响应头
spring:
cloud:
gateway:
# 配置全局默认过滤器 作用在所有路由上,也可单独为某个路由配置
default-filters:
# 往响应过滤器中加入信息
- AddResponseHeader=X-Response-Default-MyName,itheima
接口的响应头中就上了如上配置的信息
<2>前缀处理
gateway:
routes:
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
predicates:
- Path=/api/driver/**
filters:
- StripPrefix=1
此处 – StripPrefix=1 表示真实请求地址是当前用户请求以 /api 开始的 uri中去除第1个路径 /api
StripPrefix=1
路由地址:http://localhost:8001/api/driver/info/2
访问地址:http://localhost:8001/driver/info/2
StripPrefix=2
路由地址:http://localhost:8001/api/test/driver/info/2
访问地址:http://localhost:8001/driver/info/2
有时候为了简化用户请求地址,比如用户请求
http://localhost:8001/info/1
我们想统一路由到
http://localhost:18081/driver/info/1
可以使用 – PrefixPath = /driver 过滤器增加前缀。
6.Gateway跨域配置
(1)跨域概念
同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。
所谓同源就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)。
(2)Spring Cloud Gateway中配置跨域是非常简单的,如下面 application.yml 所示
gateway:
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins: "*"
allowedMethods:
- GET
- POST
- PUT
7.限流
(1)令牌桶算法
令牌桶算法是常见的限流算法之一,下面是令牌桶算法的处理步骤
1)所有的请求在处理之前都需要拿到一个可用的令牌才会被处理;
2)根据限流大小,设置按照一定的速率往桶里添加令牌;
3)桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒
绝;
4)请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务
逻辑,处理完业务逻辑之后,将令牌直接删除;
5)令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后
将不会删除令牌,以此保证足够的限流
(2)gateway限流案例
首先引入redis依赖
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redisreactive</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
配置redis
redis:
host: 127.0.0.1
port: 6379
在Application引导类中定义KeyResolver
/**
* IP限流
* @return
*/
@Bean(name = "ipKeyResolver")
public KeyResolver userKeyResolver(){
return new KeyResolver() {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
//获取远程客户端IP
String hostName = exchange
.getRequest()
.getRemoteAddress()
.getAddress()
.getHostAddress();
System.out.println("hostName:"+hostName);
return Mono.just(hostName);
}
};
}
在配置文件中添加限流配置
filters:
#请求数限流 名字不能随便写 ,使用默认的facatory
- name: RequestRateLimiter
args:
#用于通过SPEL表达式来指定使用哪一个KeyResolver.
key-resolver: "#{@ipKeyResolver}"
#表示1秒内,允许1个请求通过,令牌桶的填充速率也是1秒钟添加1个令牌。
redis-rate-limiter.replenishRate: 1
#最大突发状况 也只允许1秒内有1次请求,可以根据业务来调整 。
redis-rate-limiter.burstCapacity: 1
五.Spring Cloud OpenFeign
1.Feign
Feign是Spring Cloud组件中的一个轻量级RESTful的HTTP服务客户端,内置了Ribbon,用来做客户端负载均衡,Feign本身不支持Spring MVC的注解,它有一套自己的注解。
2.OpenFeign
OpenFeign是Spring Cloud 在Feign的基础上支持了Spring MVC的注解,如@RequesMapping等。
3.OpenFeign应用
使用OpenFeign实现服务之间调用,可以按照如下步骤实现:
1:导入openfeign依赖
2:编写openfeign客户端接口-将请求地址写到该接口上
3:消费者启动引导类开启openfeign功能注解
4:访问接口测试
需求分析:
下单时,需要更新订单状态,此时order服务需要调用driver服务的更新状态的方法。
<1> 将需要远程调用的接口单独抽取成一个工程模块,并导入OpenFeign依赖
<!--配置feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<2> 创建Feign客户端接口
@FeignClient(value = "hailtaxi-driver")//value = "hailtaxidriver"指定服务的名字
public interface DriverFeign {
/****
* 更新司机信息,该方法和hailtaxi-driver服务中的方法保持一致
* Feign会通过动态代理,帮我们生成实现类。
* 注解@FeignClient声明Feign的客户端,注解value指明服务名称
* 接口定义的方法,采用SpringMVC的注解。Feign会根据注解帮我们生成URL地址
* 注解@RequestMapping中的/driver,不要忘记。因为Feign需要拼接可访问地址
*/
@PutMapping(value = "/driver/status/{id}/{status}")
Driver status(@PathVariable(value = "id")String id,@PathVariable(value = "status")Integer status);
}
hailtaxi-driver中controller代码如下
@RestController
@RequestMapping(value = "/driver")
@Slf4j
public class DriverController {
@Autowired
private DriverService driverService;
@Value("${server.port}")
private int port;
/****
* 更新司机信息
*/
@PutMapping(value = "/status/{id}/{status}")
public Driver status(@PathVariable(value = "id")String id,@PathVariable(value = "status")Integer status){
log.info("当前服务占用的端口为:{}",port);
//修改状态
driverService.update(id,status);
//修改状态后的司机信息
return driverService.findById(id);
}
}
<3> hailtaix-order服务调用feign
@Autowired
private DriverFeign driverFeign;
/***
* 下单
*/
@PostMapping
public OrderInfo add(){
//更新司机状态
driverFeign.status("1",2);
//创建订单
OrderInfo orderInfo = new OrderInfo("No"+((int)(Math.random()*10000)), (int)(Math.random()*100), new Date(), "深圳北站", "罗湖港", null);
orderInfoService.add(orderInfo);
return orderInfo;
}
<4>hailtaix-order消费者服务启动引导类加上注解
@EnableFeignClients(basePackages = “com.itheima.driver.feign”)
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.itheima.driver.feign")
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
}
<5>OpenFeign远程调用测试
六.Spring Cloud 拦截器
1.拦截器的作用
用 Feign 来调用远程服务,比如远程服务的权限验证,需要在 header 中传递 token 之类的。在方法中显示传递又过于麻烦了,这时候就可以考虑使用 Feign 提供的 RequestInterceptor 接口,只要实现了该接口,那么 Feign 每次做远程调用之前都可以被它拦截下来在进行包装
2.拦截器的应用
<1> 在OpenFeign的公共接口调用模块中添加拦截器
/**
* 1、直接实现接口
* 2、继承 BaseRequestInterceptor
*/
@Slf4j
public class MyRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
String url = template.url();
Map<String, Collection<String>> headers =template.headers();
String method = template.method();
Map<String, Collection<String>> queries =template.queries();
Request.Body body = template.requestBody();
log.info("url={},headers={},method={},queries={},body={}",url,headers,method,queries,body);
//添加头信息
template.header("GlobalId",UUID.randomUUID().toString());
}
}
<2>在需要调用远程服务的客户端加入容器即可
@Configuration
public class InterceptorConfiguration {
@Bean
public RequestInterceptor interceptor() {
return new MyRequestInterceptor();
}
}