高级JAVA开发 分布式系统部分

分布式系统

参考和摘自:
中华石杉 《Java工程师面试突击第1季》

分布式系统接口的幂等性

思路:缓存记录标识(类似分布式锁,要注意保证操作的原子性) 或 基于数据库unique key。

分布式锁

Redis 普通实现

    # 加锁用Set附带NX(IF NOT EXIST)、PX(WITH EXPIRE TIME)参数保证一个命令的原子性:
    SET lockKey randomValue NX PX 10000

    # 解锁用Lua脚本保证原子性,判断randomValue是否为加锁时放进去的
    # 只有持有锁的人才能释放锁,
    # 锁过期后被另一进程获取,原进程尝试释放锁randomValue不等,操作失败:
    if redis.call("get",KEYS[1]) == ARGV[1]
    then return redis.call("del",KEYS[1])
    else return 0
    end

缺点:
1.Redis单机部署,Redis挂掉后锁失效。 
2.Redis高可用主从同步时是异步的,从库得到“锁”数据会有延时。
  如果slave还没来得及同步完锁数据master挂掉了,锁就失效了。

基于Redis的分布式锁框架:Redisson、RedLock

待补充

基于zookeeper的分布式锁

实现思想:
基于EPHEMERAL临时节点的普通锁:
A线程尝试在zk上创建一个临时节点,如果临时节点不存在,创建成功,拿到锁。释放锁就删除临时节点。
B线程尝试在zk上创建一个相同名称的临时节点,如果节点已经存在了,创建失败,没拿到锁,并在临时节点上注册监听器。一旦临时节点被删除,监听器触发,重复尝试创建临时节点。

基于EPHEMERAL_SEQUENTIAL有序临时节点的公平锁:
线程创建有序临时节点(会在zk目录下创建一个带编号的临时节点)并且取回节点集,对节点集排序拿出编号最小节点,如果最小节点是当前线程创建的节点,那么当前线程持有锁并返回true,反之获得锁失败。然后取得当前临时节点的前一个节点添加监听,然后阻塞,等待前者释放锁通知监听唤醒。这样根据创建节点顺序依次执行。

分布式系统Session共享

1.Tomcat可以直接配置基于Redis的Session共享:

# 单机Redis:
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
         host="{redis.host}"
         port="{redis.port}"
         database="{redis.dbnum}"
         maxInactiveInterval="60"/>

# Redis:
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
	 sentinelMaster="mymaster"
	 sentinels="<sentinel1-ip>:26379,<sentinel2-ip>:26379,<sentinel3-ip>:26379"
	 maxInactiveInterval="60"/>

缺点:依赖于web容器,更换web容器还需要另外方案。

2.spring session + redis

# pom.xml配置:
<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session-data-redis</artifactId>
  <version>1.2.1.RELEASE</version>
</dependency>
<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>2.8.1</version>
</dependency>

# spring配置:
<bean id="redisHttpSessionConfiguration" class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
	<property name="maxInactiveIntervalInSeconds" value="600"/>
</bean>

<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <property name="maxTotal" value="100" />
    <property name="maxIdle" value="10" />
</bean>

<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy">
    <property name="hostName" value="${redis_hostname}"/>
    <property name="port" value="${redis_port}"/>
    <property name="password" value="${redis_pwd}" />
    <property name="timeout" value="3000"/>
    <property name="usePool" value="true"/>
    <property name="poolConfig" ref="jedisPoolConfig"/>
</bean>

# web.xml配置:
<filter>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

# 示例代码
@Controller
@RequestMapping("/test")
public class TestController {

	@RequestMapping("/putIntoSession")
	@ResponseBody
    public String putIntoSession(HttpServletRequest request, String username){
        request.getSession().setAttribute("name",  “leo”);
        return "ok";
    }

	@RequestMapping("/getFromSession")
	@ResponseBody
    public String getFromSession(HttpServletRequest request, Model model){
        String name = request.getSession().getAttribute("name");
        return name;
    }
}

分布式事务

https://blog.csdn.net/u011040537/article/details/105840442

分库分表

拆分方案

垂直拆分:将一张表垂直拆分成多张表
比如一张表: Id col1 col2 col3 col4 col5 col6
拆成表1:Id col1 col2 col3
表2:Id col4 col5 col6

水平拆分:单表数据量过大,按Id或者createTime拆分成多张表,表结构不变,表名加上序号,分装到多个数据库中。利于数据不断膨胀的表,比如 订单表、流水表等。
按照range分表:方便扩容,但是对单库压力可能会很大(访问新数据的频率比旧数据高)
按照hash分表:不方便扩容,扩容会导致数据迁移,但是可以均摊数据库压力。

如何分表需参照具体业务衡量。

全局ID如何生成

  1. 新建一个全局数据库专门生成ID,适合并发量低,数据量大的系统
  2. UUID:uuid太长,作为主键性能太差,不适合用于主键。
  3. 基于当前时间生成:时间+userId+… 保证唯一
  4. snowflake算法:
    0 | 0001100 10100010 10111110 10001001 01011100 00 | 10001 | 11001 | 0000 00000000
    永远是0表示整数 | 毫秒时间戳转换成2进制并填0对齐41位 | 机房ID,最大(2^5)32个机房 | 机器ID,最大(2^5)32个机器 | 当前毫秒生成的Id序号,最大(2 ^ 12 - 1)4096个
    最后把拼装的2进制转换成10进制得到ID号

Mysql读写分离相关问题

主库宕机数据丢失问题:
semi-sync复制(半同步复制),主库写入binlog日志之后,将强制立即将数据同步到从库,从库将日志写入自己本地的relay log之后返回一个ack给主库,主库接收到至少一个从库ack后才会认为写操作完成了。

延迟问题:
并行复制,从库开启多个线程,并行读取relay log中不同库的日志,然后并行重放不同库的日志,这是库级别的并行。(多库并发重放)缓解延迟问题。

mysql > show status;
Seconds_Behind_Master,可以看到从库复制主库的数据落后了几ms。
  • 拆库拆表降低单库并发,提高主从同步效率。
  • 打开Mysql并行复制,缓解延迟问题。
  • 重写代码,尽量避免写入后立马读出来。
  • 如果业务需求插入更新数据后立马读出来,那么只能对这个查询设置直连主库,但是并不推荐这种做法。
  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然可以,以下是一份关于您的Java SpringBoot项目的简介描述: 探索Java的SpringBoot魔法 亲爱的开发者们,准备好一起踏上这次深入Java的SpringBoot之旅了吗?如果您正在寻找一个现代化、高效且易于扩展的开发框架,那么这个SpringBoot项目将是您的理想选择。 主要特点: 快速开发:SpringBoot通过自动配置和约定大于配置的原则,大大简化了项目设置和开发过程。 易于扩展:SpringBoot与生俱来的灵活性使您可以轻松集成各种服务和数据库,以满足您日益增长的需求。 安全性:内置的安全特性,如OAuth2和Spring Security,确保您的应用程序安全无虞。 微服务支持:SpringBoot是微服务架构的理想选择,可以帮助您构建模块化、可扩展的应用程序。 社区支持:全球的开发者社区意味着您可以在遇到问题时找到大量的资源和支持。 无论您是初出茅庐的Java新手,还是经验丰富的开发者,这个项目都将为您提供一个深入了解SpringBoot的机会。无论您是想学习新的技能,还是想提高现有应用程序的性能,这个项目都将是您的宝贵资源。 内容概览 项目设置和初始化 控制器设计和实现 数据持久化与数据库集成 安全性和身份验证 性能优化和监控 部署和生产环境考虑 现在,是时候让您的Java技能得到充分发挥,并掌握SpringBoot的魔法了!这个项目将带领您从基础到高级,探索SpringBoot的每一个角落。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值