微服务
系统架构演变
Eureka服务注册中心
Hystrix服务熔断
Zuul服务网关
服务链路追踪
Spring Cloud Stream 消息中间件
Spring Cloud Config 服务配置中心
Spring Cloud Bus 消息总线
Nacos 服务注册中心
- 命名空间(Namespace):用于不同环境隔离。例如生产环境、测试环境、开发环境
- 配置分组(Group):用于不同的服务分为一组。一般一个项目配置分到同一组。
- 配置集(Data ID):一个配置文件就是一个配置集。
服务注册到Nacos
1、添加依赖
<!--nacos客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2、主类添加@EnbaleDiscoverClient注解
@SpringBootApplication
@EnableDiscoveryClient //开启服务注册发现功能
public class OrderApplication{
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
3、在application.yml添加nacos服务地址
spring:
cloud:
nacos:
discovery: #nacos服务注册和发现配置
server-addr: 192.168.57.130:8848 #Nacos地址
#设置负载均衡
product-server:
ribbon: #负载均衡策略
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #随机方式
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #随机方式
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule #权重策略,根据每个服务提供者的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性也就越低。
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.BestAvailableRule #最小连接数策略,也叫最小并发数策略,它是遍历服务提供者列表,选取连接数最小的⼀个服务实例。如果有相同的最小连接数,那么会调用轮询策略进行选取。
4、使用discoverClient获取服务信息
后续使用openfeign替换discoveryClient
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
@RestController
public class OrderController {
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private RestTemplate restTemplate;
@GetMapping("/order/pod/{pid}")
public Product getProduct(@PathVariable("pid") Integer pid){
ServiceInstance serviceInstance = discoveryClient.getInstances("service-product").get(0);
String url = serviceInstance.getHost() + ":" + serviceInstance.getHost();
//通过restTemplate调用商品微服务
Product product = restTemplate.getForObject("http://" + url + "/product/" + pid, Product.class);
}
}
Feign服务调用(集成Ribbon负载均衡)
通俗的讲,负载均衡就是将负载(工作任务、访问请求)进行分摊到多个操作单元(服务器、组件)上进行执行。
根据负载均衡发生位置的不同,一般分为服务端负载均衡和客户端负载均衡。
服务端负载均衡指的是发生在服务提供者一方,比如常见的nginx负载均衡
而客户端负载均衡指的是发生在服务请求的一方,也就是在发送请求之前已经选好了由哪个实例处理请求。
我们在微服务调用关系中一般会选择客户端负载均衡,也就是在服务调用的一方来决定服务由哪个提供者执行。
远程调用方式
- RPC(Remote Procedure Call):通过网络连接来实现程序之间的调用。RPC将本地的函数调用封装成网络数据包发送给服务端,服务端接收到请求后执行相应的函数并返回结果。
- RESTful API:REST(Representational State Transfer)是一种基于HTTP协议设计和构建网络应用程序的架构风格。RESTful API提供了标准化的API接口,客户端可以通过HTTP协议访问服务器上提供的API接口并获取资源。
例如Feign、RestTemplate - Socket编程:Socket编程是一种底层的网络编程方式,它允许两个进程在网络中建立连接并进行数据传输。通过Socket编程可以实现自定义协议、控制数据传输速率等高级功能。
例如WebSocket - Message Queue:消息队列(Message Queue)是一种异步通信模式,它允许生产者向队列中添加消息,并由消费者从队列中取出消息进行处理。通过消息队列可以实现分布式系统中各个节点之间的异步通信。
Nacos很好的兼容了Feign, Feign默认集成了 Ribbon, 所以在Nacos下使用Fegin默认就实现了负载均衡的效果。
1、引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2、主类添加Feign注解
添加@EnableFeignClients
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
}
3、创建一个interface,指定调用方法
@FeignClient("product-server")
public interface ProductServiceFeignClient {
//指定调用提供者的哪个方法
//@FeignClient+@GetMapping 就是一个完整的请求路径 http://product-service/product/{pid}
@GetMapping("/product/{id}")
public Product getProduct(@PathVariable("pid") Integer pid);
}
4、使用Feign调用
@RestController
public class OrderController {
@Autowired
private ProductServiceFeignClient productServiceFeignClient;
@GetMapping("/order/pod/{pid}")
public Product getProduct(@PathVariable("pid") Integer pid){
//通过feign调用微服务
Product product = productServiceFeignClient.getProduct(pid);
}
}
5、设置负载均衡策略
spring:
cloud:
nacos:
discovery: #nacos服务注册和发现配置
server-addr: 192.168.57.130:8848 #Nacos地址
feign:
compression:
request:
enabled: true #开启请求压缩
min-request-size: 2048 #设置触发压缩得大小下限
mime-types: text/html,application/xml,application/json #设置压缩的数据类型
response:
enabled: true #开启响应压缩
client:
config:
product-server: #对应FeignClient服务名称
loggerLevel: FULL
logging:
level:
com.itcast.consumer.OpenfeignConsumerApplication: debug
#设置负载均衡
product-server: #对应FeignClient服务名称
ribbon: #负载均衡策略
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #随机方式
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #随机方式
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule #权重策略,根据每个服务提供者的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性也就越低。
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.BestAvailableRule #最小连接数策略,也叫最小并发数策略,它是遍历服务提供者列表,选取连接数最小的⼀个服务实例。如果有相同的最小连接数,那么会调用轮询策略进行选取。
Nacos持久化
Nacos默认自带derby数据库持久化我们注册的服务与配置,可以使用外部数据库如mysql存储nacos数据。
Nacos 服务配置中心
注意:不能使用原来的application.yml作为配置文件,而是新建一个bootstrap.yml作为配置文件
配置文件优先级(由高到低):
bootstrap.properties -> bootstrap.yml -> application.properties -> application.yml
1、引入依赖
<!-- nacos配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2、配置application.yml
server:
port: 8600
spring:
application:
name: my-nacos-server
cloud:
nacos:
discovery: #nacos服务注册和发现配置
server-addr: 192.168.57.130:8848 #Nacos地址
config:
server-addr: 192.168.57.130:8848 #Nacos地址
file-extension: yaml #这里指定的文件格式需要和nacos上新建的配置文件后缀相同,否则读不到
group: DEFAULT_GROUP
namespace: dev
3、Nacos配置
4、使用 通过@Value注入
5、配置动态刷新
在读取配置类上面加上@RefreshScope
@RestController
@RefreshScope //动态刷新配置
public class NacosConfigController {
@Value("${config.appName}")
private String appName;
@GetMapping("/getAppName")
public String getAppName(){
return appName;
}
}
6、共享配置
在bootstrap.yml添加shared-configs的data-id配置指向共享文件
spring:
cloud:
nacos:
discovery: #nacos服务注册和发现配置
server-addr: 192.168.57.130:8848 #Nacos地址
config:
server-addr: 192.168.57.130:8848 #Nacos地址
file-extension: yaml #这里指定的文件格式需要和nacos上新建的配置文件后缀相同,否则读不到
group: DEFAULT_GROUP
namespace: 404ad727-33e0-40ba-933e-484222d9c57a
shared-configs: #共享配置
- data-id: share-config1-data.yaml #共享文件配置1
- data-id: share-config2-data.yaml #共享文件配置2
@RestController
@RequestMapping("/share")
public class ShareConfigController {
@Value("${share.config1.name}")
private String name1;
@Value("${share.config1.age}")
private int age1;
@Value("${share.config2.name}")
private String name2;
@Value("${share.config2.age}")
private int age2;
/**
* 测试获取nacos配置的共享文件
* 配置文件名称 share-config1-data.yaml 对应 bootstrap.yaml 的 shared-configs 配置的 dataId
* @return
*/
@GetMapping("/getShareConfig1")
public String getShareConfig1(){
return name1 + age1;
}
@GetMapping("/getShareConfig2")
public String getShareConfig2(){
return name2 + age2;
}
}
Gateway服务网关
Nginx网关和GateWay网关的区别
- Nignx是流量网关,GateWay是业务网关。流量网关相当于访问的一个总入口,前端页面的一个容器,类似于防火。主要的功能有管理日志,流量监控,黑白名单,请求的负载均衡,全局限流等。而业务网关是针对具体的后端应用和服务,主要的功能是缓存策略、鉴权策略等
- 一般流量网关配置在前,业务网关配置在后
- Nginx是C语言写的,GateWay是java语言写的
- GateWay主要是路由、断言和过滤器,利用这些可以做流控。Nginx主要是负载均衡,反向代理,以及做web服务器
1、依赖
<dependencies>
<!-- nacos作为注册中心的依赖 -->
<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>
</dependencies>
2、配置
server:
port: 8081 # 网关端口
spring:
cloud:
nacos:
server-addr: 192.168.57.130:8848 # nacos地址
gateway:
routes: # 网关路由配置
- id: consumer-server # 路由id,自定义,只要唯一即可
# uri: http://192.168.57.130:8082 # 路由的目标地址 http就是固定地址
uri: lb://consumer-server # 路由的目标地址 lb就是负载均衡,后面跟服务名称
predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
- Path=/consumer/** # 这个是按照路径匹配,只要以/user/开头就符合要求
- id: provider-server
# uri: http://192.168.57.130:8083
uri: lb://provider-server
predicates:
- Path=/provider/**
路由断言工厂
官网12种示例地址 : docs.spring.io/spring-clou…
GatewayFilter路由过滤器
官网示例地址 : docs.spring.io/spring-clou…
GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理.。
跨域
spring:
cloud:
gateway:
# 。。。
globalcors: # 全局的跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://192.168.57.130:8090"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 这次跨域检测的有效期
Sentinel服务熔断
几种服务熔断器对比
防止服务雪崩,常见容错方案
- 隔离:它是指将系统按照一定的原则划分为若干个服务模块,各个模块之间相对独立,无强依赖。当有故障发生时,能将问题和影响隔离在某个模块内部,而不扩散风险,不波及其它模块,不影响整体的系统服务。常见的隔离方式有:线程池隔离和信号量隔离。
- 超时:在上游服务调用下游服务的时候,设置一个最大响应时间,如果超过这个时间,下游未作出反应,就断开请求,释放掉线程。
- 限流:限流就是限制系统的输入和输出流量已达到保护系统的目的。为了保证系统的稳固运行,一旦达到的需要限制的阈值,就需要限制流量并采取少量措施以完成限制流量的目的。
- 熔断:在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。这种牺牲局部,保全整体的措施就叫做熔断。
- 降级:降级其实就是为服务提供一个托底方案,一旦服务无法正常调用,就使用托底方案。
1、引入依赖
<dependencies>
<!-- nacos作为注册中心的依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--引入sentinel依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
</dependencies>
2、配置连接sentinel
spring:
cloud:
sentinel:
transport:
dashboard: 192.168.57.130:8080 #sentinel控制台地址
3、配置限流 通过资源名称限流 通过URL限流
/**
* 测试限流功能
*/
@RestController
@RequestMapping("/rateLimit")
public class RateLimitController {
/**
* 按资源名称限流,需要指定限流处理逻辑
* 限流规则在sentinel-dashboard配置
* @return
*/
@GetMapping("/byResource")
@SentinelResource(value = "byResource",blockHandler = "handlerException") //@SentinelResource 定义一些限流行为,value对应Sentinel控制台配置流量规则资源名
public String byResource(){
return "访问/byResource,正常返回";
}
/**
* 按URL限流,有默认的限流处理逻辑
* 限流规则在sentinel-dashboard配置
* @return
*/
@GetMapping("/byUrl")
@SentinelResource(value = "byUrl",blockHandler = "handlerException") //@SentinelResource 定义一些限流行为,value对应Sentinel控制台配置流量规则资源名
public String byUrl(){
return "访问/byUrl,正常返回";
}
/**
* 按资源限流,使用自定义限流处理逻辑
* @return
*/
@GetMapping("/byMyCustomBlock")
@SentinelResource(value = "byMyCustomBlock",blockHandler = "handlerException",blockHandlerClass = CustomBlockHandler.class) //@SentinelResource 定义一些限流行为,value对应Sentinel控制台配置流量规则资源名
public String byMyCustomBlock(){
return "访问/byMyCustomBlock,正常返回";
}
/**
* 限流处理逻辑
* @param exception
* @return
*/
public String handlerException(BlockException exception){
HashMap<String, Object> map = new HashMap<>();
ObjectMapper objectMapper = new ObjectMapper();
map.put("name","限流");
map.put("msg",exception.getClass().getCanonicalName());
try {
String result = objectMapper.writeValueAsString(map);
return result;
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
}
自定义限流处理逻辑
/**
* 自定义限流处理逻辑
*/
public class CustomBlockHandler {
public static String handlerException(BlockException exception){
return "返回自定义限流处理逻辑CustomBlockHandler";
}
}
4、配置熔断
- 方式一:配合RestTemplate;
- 方式二:配合OpenFeign
方式一:配合RestTemplate
使用@SentinelRestTemplate包装RestTemplate
@Configuration
public class RibbonConfig {
@Bean
@SentinelRestTemplate
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
fallback设置降级逻辑
/**
* 测试熔断功能
*/
@Slf4j
@RestController
@RequestMapping("/break")
public class CircleBreakerController {
@Autowired
private RestTemplate restTemplate;
@Value("${service-url.user-service}")
private String userServiceUrl;
@RequestMapping("/fallback/{id}")
@SentinelResource(value = "fallback",fallback = "handleFallback")
public CommonResult fallback(@PathVariable Long id) {
return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
}
@RequestMapping("/fallbackException/{id}")
@SentinelResource(value = "fallbackException",fallback = "handleFallback2", exceptionsToIgnore = {NullPointerException.class})
public CommonResult fallbackException(@PathVariable Long id) {
if (id == 1) {
throw new IndexOutOfBoundsException();
} else if (id == 2) {
throw new NullPointerException();
}
return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
}
public CommonResult handleFallback(Long id) {
User defaultUser = new User(-1L, "defaultUser", "123456");
return new CommonResult<>(defaultUser,"服务降级返回",200);
}
public CommonResult handleFallback2(@PathVariable Long id, Throwable e) {
log.error("handleFallback2 id:{},throwable class:{}", id, e.getClass());
User defaultUser = new User(-2L, "defaultUser2", "123456");
return new CommonResult<>(defaultUser,"服务降级返回",200);
}
}
方式二:配合OpenFeign
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
feign:
sentinel:
enabled: true #激活Sentinel对Feign的支持
#自定义配置信息
service-url:
# 项目位置在study/study04-cloud-hystrix/user-service
user-service: http://nacos-server
创建一个UserService接口,用于定义对nacos-user-service服务的调用:
@FeignClient(value = "${service-url.user-service}",fallback = UserFallbackService.class)
public interface UserService {
@PostMapping("/user/create")
CommonResult create(@RequestBody User user);
@GetMapping("/user/{id}")
CommonResult<User> getUser(@PathVariable Long id);
@GetMapping("/user/getByUsername")
CommonResult<User> getByUsername(@RequestParam String username);
@PostMapping("/user/update")
CommonResult update(@RequestBody User user);
@PostMapping("/user/delete/{id}")
CommonResult delete(@PathVariable Long id);
}
创建UserFallbackService类实现UserService接口,用于处理服务降级逻辑
@Component
public class UserFallbackService implements UserService {
@Override
public CommonResult create(User user) {
User defaultUser = new User(-1L, "defaultUser", "123456");
return new CommonResult<>(defaultUser,"服务降级返回",200);
}
@Override
public CommonResult<User> getUser(Long id) {
User defaultUser = new User(-1L, "defaultUser", "123456");
return new CommonResult<>(defaultUser,"服务降级返回",200);
}
@Override
public CommonResult<User> getByUsername(String username) {
User defaultUser = new User(-1L, "defaultUser", "123456");
return new CommonResult<>(defaultUser,"服务降级返回",200);
}
@Override
public CommonResult update(User user) {
return new CommonResult("调用失败,服务被降级",500);
}
@Override
public CommonResult delete(Long id) {
return new CommonResult("调用失败,服务被降级",500);
}
}
5、使用Nacos储存规则 默认情况下,当我们在Sentinel控制台中配置规则一旦我们重启应用,规则将消失。可以把规则保存到nacos config里。
spring:
application:
name: sentinel-server
cloud:
nacos:
discovery:
server-addr: 192.168.57.130:8848 #nacos地址
sentinel:
transport:
dashboard: 192.168.57.130:8858 #nacos地址
port: 8719 #sentinel-dashboard的端口
datasource: #sentinel配置持久化,sentinel相关配置保存在nacos,注意目前只支持public命名空间
ds1:
nacos:
server-addr: 192.168.57.130:8848
dataId: ${spring.application.name}
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
相关参数解释:
- resource:资源名称;
- limitApp:来源应用;
- grade:阈值类型,0表示线程数,1表示QPS;
- count:单机阈值;
- strategy:流控模式,0表示直接,1表示关联,2表示链路;
- controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
- clusterMode:是否集群。
SMS短信服务
Seata分布式事务
事务
事务在一般情况下都是指代数据库事务,它是由系统对数据进行访问和更新的操作所组成的一个逻辑单元,具备原子性( Atomicity )、一致性( Consistency )、隔离性( Isolation )、持久性( Durability )四大特征,用来确保无论发生什么情况数据都处在一个合理的状态,在日常开发过程中能够不需要考虑网络波动、服务器宕机等问题。所以从某种角度来看,事务是用来服务应用层的。
为什么需要分布式事务
单体应用可以依赖数据库事务,将一系列操作限制在一个会话之中同时成功或失败,但是在分布式系统中,每个服务本身的数据库会话之间是隔离的,就无法单纯的依赖数据库事务,在这种场景下想要保证数据的一致性,就需要引入分布式事务。