分布式,微服务的概念
微服务主要是面对现在技术发展后,业务复杂度提升、用户量激增,单体项目很难再在这种高并发的场景下发挥作用。且业务的复杂度的提升,代码量几何倍数增长,在一个系统,一个项目下放置所有代码,对于代码的维护与解耦,都是很大的挑战。所以我们使用微服务的架构,将一个大的项目,拆分成多个独立的的系统,使用网络通信来让他们相互协调与合作,达到形如一个系统的效果。
当然,微服务架构在解决问题的同时,也会出现很多单体项目下不会出现的新的挑战,在开发模式、使用的技术上,都会有很大的不同,并且在面对网络通信不稳定的情况下,又会有新的复杂性出现。
下面我们来看看分布式系统常用的工具
Spring-Cloud:Spring Cloud为开发人员提供工具,以快速构建分布式系统中的一些常见模式 (例如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状态)。分布式系统的协调存在一些通用的模式,使用Spring Cloud开发人员可以快速支持实现这些模式的服务和应用程序。它们将在任何分布式环境中运行良好,包括开发人员自己的笔记本电脑、裸机数据中心以及云铸造等托管平台。
Spring-cloud下面有很多实用的工具,包括Spring-Cloud官方的gateway 、loadbalancer 、openfeign 等;阿里的nacos、dubbo、sentinel等;netflix的ribbon、hyxtrix等。
我们这里的项目,采用nacos作为服务发现、配置管理的选项,sentinel-服务容错,seata-分布式事务、gateway-服务网关、openfeign-服务调用、ribbon-负载均衡;
服务发现:
每个服务因为是相对独立的系统,他们直接需要通信只有通过网络,服务发现就是找到服务实例的地址的过程,我们这里使用nacos;
1.导入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2.修改配置
spring:
application:
name: website
cloud:
nacos:
config:
server-addr: localhost:8848
3.添加注解
@EnableDiscoveryClient
4.启动服务器
Nacos 快速开始 下载服务器并启动。
服务发现的过程:
- 生产者启动时进行服务注册
- 消费者拉取服务列表
- 服务注册表对生产者进行健康检测
- 如果发生变更,服务注册表则通知消费者
- 消费者重新拉取服务列表
做完操作后,查看 Nacos Discovery 注册中心
(账号名/密码为 nacos/nacos)打开Nacos 控制台:
负载均衡:
这里我们采用netflix-ribbon作为负载均衡服务,由nacos-discovery引入。
ribbon的内置策略
策略名 |
策略声明 |
策略描述 |
实现说明 |
BestAvailableRule 最小并发数 |
public class BestAvailableRule extends ClientConfigEnabledRoundRobinRule |
选择一个最小的并发请求的server |
逐个考察Server,如果Server被tripped了,则忽略,在选择其中ActiveRequestsCount最小的server |
AvailabilityFilteringRule |
public class AvailabilityFilteringRule extends PredicateBasedRule |
过滤掉那些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的的后端server(active connections 超过配置的阈值) |
使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status里记录的各个server的运行状态 |
WeightedResponseTimeRule 响应时间加权 |
public class WeightedResponseTimeRule extends RoundRobinRule |
根据响应时间分配一个weight,响应时间越长,weight越小,被选中的可能性越低。 |
一个后台线程定期的从status里面读取评价响应时间,为每个server计算一个weight。Weight的计算也比较简单responsetime 减去每个server自己平均的responsetime是server的权重。当刚开始运行,没有形成statas时,使用roubine策略选择server。 |
RetryRule |
public class RetryRule extends AbstractLoadBalancerRule |
对选定的负载均衡策略机上重试机制。 |
在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server |
RoundRobinRule 轮询 |
public class RoundRobinRule extends AbstractLoadBalancerRule |
roundRobin方式轮询选择server |
轮询index,选择index对应位置的server |
RandomRule 随机 |
public class RandomRule extends AbstractLoadBalancerRule |
随机选择一个server |
在index上随机,选择index对应位置的server |
ZoneAvoidanceRule |
public class ZoneAvoidanceRule extends PredicateBasedRule |
复合判断server所在区域的性能和server的可用性选择server |
使用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前一个判断判定一个zone的运行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate用于过滤掉连接数过多的Server。 |
策略1:
/**
* 全局替换成随机规则
* @return
*/
@Bean
public IRule loadBalanceRule(){
return new RandomRule();
}
策略2:
# 为某一个服务指定负载均衡规则
user1:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
服务调用
这里我们采用spring-cloud-openfeign,声明式服务调用组件
1.引入maven依赖
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
2.加注解
@SpringBootApplication
@EnableFeignClients("com.woniuxy.cloud.order1.client")
public class Order1Application {
public static void main(String[] args) {
SpringApplication.run(Order1Application.class, args);
}
}
@FeignClient("user1")
public interface UserClient {
@GetMapping("mallUser/getUser")
String getUser(@RequestParam(value = "uid") Integer uid);
@PostMapping("mallUser/putUser")
String putUser(@RequestBody MallUserUpdateParam form);
@DeleteMapping("mallUser/deleteUser")
String deleteUser(@RequestParam(value = "uid") Integer uid);
@PostMapping("mallUser/register")
String register(@RequestBody MallUserParam form);
}
3.进行调用
@Resource
private MallUserClient userClient;
@GetMapping("/deleteUser")
public String deleteUser(@RequestParam(value = "uid") Integer uid) {
return userClient.deleteUser(uid);
}
接口共享
将项目的接口和实现类,拆分开,项目的接口单独打包成模块,在另外一个项目中通过导入依赖的形式,使接口能够共享
如我们这个项目的分层使用一下分类
将接口放在client层,另外一个项目需要使用时,在infra层调用。
分布式事务
我们这里使用seata提供的AT模式来进行分布式事务处理:
AT执行分布式事务主要是通过两个阶段的操作来实现:
阶段1:预备阶段
执行业务,在此时seata会代理数据源,获取所有的sql操作,并且获取到数据库sql操作的前后镜像,并且存入undo_log表,和sql的操作一些进行事务的提交
阶段2:提交阶段
如果是正常操作则删除undo_log表的数据,如果是失败操作,则获取undo_log表中的数据,进行反存回数据库的操作
AT模式实际操作
1.导入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
2.建立undo_log表
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8
3.加注解@GlobalTransactional
4.配置yml
5.启动seata-server作为TC
AT模式操作流程图
服务网关
我们这里采用gateway作为网关:
服务网关是作为系统唯一的一个入口,肩负一些通用的公共功能,比如黑白名单,动态路由,认证,授权等操作。
搭建网关:
新建网关项目,导入依赖,写入配置:
单点登录
我们这里采用基于redis共享+cookie的方式 sa-token作为单点登录的选择。
单点登录主要是实现在一个服务中登录后,其他服务也能共享登录的状态,我们一般会单独创建一个专门登录的服务器,由它来判断用户登录的逻辑和判断用户是否登录的状态。
sa-token是通过cookies来保存token,每次请求的时候,携带token访问不同的服务时,都能通过token来获取登录的状态。
服务容错
我们这里采用Sentinel来作为服务容错的选择。官网: home
sentinel提供流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。
启动sentinel控制台后,可以通过控制台来操作及定义服务
可通过规则定义sentinel的推送规则
qps:每秒查询数
warm up:冷启动,可以逐步提高通过的请求数,直到设定值