当前位置: 首页 > news >正文

网站的建设周期与阶段网站与网页的区别

网站的建设周期与阶段,网站与网页的区别,电子商务公司创意名字,安徽国贸网站建设UidGenerator是什么 UidGenerator是百度开源的一款分布式高性能的唯一ID生成器#xff0c;更详细的情况可以查看官网集成文档 uid-generator是基于Twitter开源的snowflake算法实现的一款唯一主键生成器(数据库表的主键要求全局唯一是相当重要的)。要求java8及以上版本。 snow…UidGenerator是什么 UidGenerator是百度开源的一款分布式高性能的唯一ID生成器更详细的情况可以查看官网集成文档 uid-generator是基于Twitter开源的snowflake算法实现的一款唯一主键生成器(数据库表的主键要求全局唯一是相当重要的)。要求java8及以上版本。 snowflake算法 Snowflake算法描述指定机器 同一时刻 某一并发序列是唯一的。据此可生成一个64 bits的唯一IDlong。 将long的64位分为3部分时间戳、工作机器id和序列号位数分配如下 时间戳部分的时间单位一般为毫秒也就是说1台工作机器1毫秒可产生4096个id2的12次方。 UidGenerator算法 与原始的snowflake算法不同uid-generator支持自定义时间戳、工作机器id和序列号等各部分的位数以应用于不同场景。 sign(1bit)固定1bit符号标识即生成的UID为正数。delta seconds (28 bits)当前时间相对于时间基点2016-05-20的增量值单位秒最多可支持约8.7年worker id (22 bits)机器id最多可支持约420w次机器启动。内置实现为在启动时由数据库分配默认分配策略为用后即弃后续可提供复用策略。sequence (13 bits)每秒下的并发序列13 bits可支持每秒8192个并发。 这些字段的长度可以根据具体的应用需要进行动态的调整满足总长度为64位即可。 Snowflake和UidGenerator的对比 百度的worker id的生成策略和美团的生成策略不太一样美团的snowflake主要利用本地配置的port和IP来唯一确定一个workid美团的这种生成方式还是可以由于手工配置错误造成port重复最终产生重复ID的风险百度的这种生成方式每次都是新增的可能会一段时间后worker id用完的情况人工配置错误的可能性很小了。 源码分析 DefaultUidGenerator DefaultUidGenerator的产生id的方法与基本上就是常见的snowflake算法实现仅有一些不同如以秒为为单位而不是毫秒。DefaultUidGenerator的产生id的方法如下。 protected synchronized long nextId() {long currentSecond getCurrentSecond();if (currentSecond lastSecond) {long refusedSeconds lastSecond - currentSecond;throw new UidGenerateException(Clock moved backwards. Refusing for %d seconds, refusedSeconds);}if (currentSecond lastSecond) {sequence (sequence 1) bitsAllocator.getMaxSequence();if (sequence 0) {currentSecond getNextSecond(lastSecond);}} else {sequence 0L;}lastSecond currentSecond;return bitsAllocator.allocate(currentSecond - epochSeconds, workerId, sequence);}nextId方法主要负责ID的生成这种实现方式很简单如果毫秒数未发生变化在序列号加一即可毫秒数发生变化重置Sequence为0Leaf文章中讲过重置为0会造成如果利用这个ID分表的时候并发量不大的时候sequence字段会一直为0等会出现数据倾斜 CachedUidGenerator CachedUidGenerator支持缓存生成的id。 【采用RingBuffer来缓存已生成的UID, 并行化UID的生产和消费】【UidGenerator通过借用未来时间来解决sequence天然存在的并发限制】 基本实现原理 正如名字体现的那样这是一种缓存型的ID生成方式当剩余ID不足的时候会异步的方式重新生成一批ID缓存起来后续请求的时候直接的时候直接返回现成的ID即可。 在实现上, UidGenerator通过借用未来时间来解决sequence天然存在的并发限制; 采用RingBuffer来缓存已生成的UID, 并行化UID的生产和消费, 同时对CacheLine补齐避免了由RingBuffer带来的硬件级「伪共享」问题. 最终单机QPS可达600万。 使用RingBuffer缓存生成的id。RingBuffer是个环形数组默认大小为8192个里面缓存着生成的id。 CachedUidGenerator采用了双RingBufferUid-RingBuffer用于存储Uid、Flag-RingBuffer用于存储Uid状态(是否可填充、是否可消费) 由于数组元素在内存中是连续分配的可最大程度利用CPU cache以提升性能。但同时会带来「伪共享」FalseSharing问题为此在Tail、Cursor指针、Flag-RingBuffer中采用了CacheLine 补齐方式。 获取id 会从ringbuffer中拿一个id支持并发获取 public long getUID() {try {return ringBuffer.take();} catch (Exception e) {LOGGER.error(Generate unique id exception. , e);throw new UidGenerateException(e);}}RingBuffer缓存已生成的id RingBuffer为环形数组默认容量为sequence可容纳的最大值8192个可以通过boostPower参数设置大小。几个重要的数据结构采用了RingBuffer的方式来缓存相关UID信息。 tail指针、Cursor指针用于环形数组上读写slot Tail指针 指向当前最后一个可用的UID位置表示Producer生产的最大序号(此序号从0开始持续递增)。Tail不能超过Cursor即生产者不能覆盖未消费的slot。当Tail已赶上curosr此时可通过rejectedPutBufferHandler指定PutRejectPolicy Cursor指针 指向下一个获取UID的位置其一定是小于Tail表示Consumer消费到的最小序号(序号序列与Producer序列相同)。Cursor不能超过Tail即不能消费未生产的slot。当Cursor已赶上tail此时可通过rejectedTakeBufferHandler指定TakeRejectPolicy Tail - Cursor表示的是现在可用的UID数量当可用UID数量小于一定阈值的时候会重新添加一批新的UID在RingBuffer中。 填充id RingBuffer填充时机 程序启动时将RingBuffer填充满缓存着8192个id在调用getUID()获取id时检测到RingBuffer中的剩余id个数小于总个数的50%将RingBuffer填充满使其缓存8192个id。定时填充可配置是否使用以及定时任务的周期 因为delta seconds部分是以秒为单位的所以1个worker 1秒内最多生成的id书为8192个2的13次方。从上可知支持的最大qps为8192所以通过缓存id来提高吞吐量。 为什么叫借助未来时间 因为每秒最多生成8192个id当1秒获取id数多于8192时RingBuffer中的id很快消耗完毕在填充RingBuffer时生成的id的delta seconds 部分只能使用未来的时间。因为使用了未来的时间来生成id所以上面说的是【最多】可支持约8.7年 注意这里的RingBuffer不是Disruptor框架中的RingBuffer但是借助了很多Disruptor中RingBuffer的设计思想比如使用缓存行填充解决伪共享问题。 填充RingBuffer public void paddingBuffer() {LOGGER.info(Ready to padding buffer lastSecond:{}. {}, lastSecond.get(), ringBuffer);if (!running.compareAndSet(false, true)) {LOGGER.info(Padding buffer is still running. {}, ringBuffer);return;}boolean isFullRingBuffer false;while (!isFullRingBuffer) {List uidList uidProvider.provide(lastSecond.incrementAndGet());for (Long uid : uidList) {isFullRingBuffer !ringBuffer.put(uid);if (isFullRingBuffer) {break;}}}running.compareAndSet(true, false);LOGGER.info(End to padding buffer lastSecond:{}. {}, lastSecond.get(), ringBuffer);}生成id上面代码中的uidProvider.provide调用的就是这个方法) protected List nextIdsForOneSecond(long currentSecond) {int listSize (int) bitsAllocator.getMaxSequence() 1;List uidList new ArrayList(listSize);long firstSeqUid bitsAllocator.allocate(currentSecond - epochSeconds, workerId, 0L);for (int offset 0; offset listSize; offset) {uidList.add(firstSeqUid offset);}return uidList;}RingBuffer的代码 public class RingBuffer {private static final Logger LOGGER LoggerFactory.getLogger(RingBuffer.class);private static final int START_POINT -1;private static final long CAN_PUT_FLAG 0L;private static final long CAN_TAKE_FLAG 1L;public static final int DEFAULT_PADDING_PERCENT 50;private final int bufferSize;private final long indexMask;private final long[] slots;private final PaddedAtomicLong[] flags;private final AtomicLong tail new PaddedAtomicLong(START_POINT);private final AtomicLong cursor new PaddedAtomicLong(START_POINT);private final int paddingThreshold;private RejectedPutBufferHandler rejectedPutHandler this::discardPutBuffer;private RejectedTakeBufferHandler rejectedTakeHandler this::exceptionRejectedTakeBuffer;private BufferPaddingExecutor bufferPaddingExecutor;代码层面的优化 代码中通过字节的填充来避免伪共享的产生。 多核处理器处理相互独立的变量时一旦这些变量处于同一个缓存行不同变量的操作均会造成这一个缓存行失效影响缓存的实际效果造成很大的缓存失效的性能问题。下面图中线程处理不同的两个变量但这两个变量的修改都会造成整个整个缓存行的失效导致无效的加载、失效出现了伪共享的问题 RingBuffer中通过定义一个PaddedAtomicLong来独占一个缓存行代码中的实现填充可能需要根据具体的执行系统做一些调整保证其独占一个缓存行即可。 take先关id的源码 下面我们来看下如何获取相关的UID public long take() {long currentCursor cursor.get();long nextCursor cursor.updateAndGet(old - old tail.get() ? old : old 1);Assert.isTrue(nextCursor currentCursor, Curosr cant move back);long currentTail tail.get();if (currentTail - nextCursor paddingThreshold) {LOGGER.info(Reach the padding threshold:{}. tail:{}, cursor:{}, rest:{}, paddingThreshold, currentTail,nextCursor, currentTail - nextCursor);bufferPaddingExecutor.asyncPadding();}if (nextCursor currentCursor) {rejectedTakeHandler.rejectTakeBuffer(this);}int nextCursorIndex calSlotIndex(nextCursor);Assert.isTrue(flags[nextCursorIndex].get() CAN_TAKE_FLAG, Curosr not in can take status);long uid slots[nextCursorIndex];flags[nextCursorIndex].set(CAN_PUT_FLAG);return uid;}通过AtomicLong.updateAndGet来避免对整个方法进行加锁获取一个可以访问的UID的游标值根据这个下标获取slots中相关的uid直接返回 缓存中可用的uid(Tail - Cursor)小于一定阈值的时候需要启动另外一个线程来生成一批UID UID 的生成 public synchronized boolean put(long uid) { long currentTail tail.get(); long currentCursor cursor.get(); long distance currentTail - (currentCursor START_POINT ? 0 : currentCursor);if (distance bufferSize - 1) {rejectedPutHandler.rejectPutBuffer(this, uid);return false;}int nextTailIndex calSlotIndex(currentTail 1);if (flags[nextTailIndex].get() ! CAN_PUT_FLAG) {rejectedPutHandler.rejectPutBuffer(this, uid);return false;}slots[nextTailIndex] uid;flags[nextTailIndex].set(CAN_TAKE_FLAG);tail.incrementAndGet();return true; }获取Tail的下标值如果缓存区满的话直接调用RejectedPutHandler.rejectPutBuffer方法 未满的话将UID放置在slots数组相应的位置上同时将Flags数组相应的位置改为CAN_TAKE_FLAG CachedUidGenerator通过缓存的方式预先生成一批UID列表可以解决UID获取时候的耗时但这种方式也有不好点一方面需要耗费内存来缓存这部分数据另外如果访问量不大的情况下提前生成的UID中的时间戳可能是很早之前的DefaultUidGenerator应该在大部分的场景中就可以满足相关的需求了。 填充缓存行解决伪共享 关于伪共享可以参考这篇文章《伪共享false sharing并发编程无声的性能杀手》 private final PaddedAtomicLong[] flags;private final AtomicLong tail new PaddedAtomicLong(START_POINT);private final AtomicLong cursor new PaddedAtomicLong(START_POINT)PaddedAtomicLong的设计 public class PaddedAtomicLong extends AtomicLong {private static final long serialVersionUID -3415778863941386253L;public volatile long p1, p2, p3, p4, p5, p6 7L;public PaddedAtomicLong() {super();}public PaddedAtomicLong(long initialValue) {super(initialValue);}public long sumPaddingToPreventOptimization() {return p1 p2 p3 p4 p5 p6;}}Spring Boot工程集成全局唯一ID生成器 UidGenerator 基础工程创建 官网集成文档 创建数据表 执行如下SQL DROP TABLE IF EXISTS WORKER_NODE; CREATE TABLE WORKER_NODE ( ID BIGINT NOT NULL AUTO_INCREMENT COMMENT auto increment id, HOST_NAME VARCHAR(64) NOT NULL COMMENT host name, PORT VARCHAR(64) NOT NULL COMMENT port, TYPE INT NOT NULL COMMENT node type: ACTUAL or CONTAINER, LAUNCH_DATE DATE NOT NULL COMMENT launch date, MODIFIED TIMESTAMP NOT NULL COMMENT modified time, CREATED TIMESTAMP NOT NULL COMMENT created time, PRIMARY KEY(ID) )COMMENTDB WorkerID Assigner for UID Generator,ENGINE INNODB;在使用的数据库中创建表WORKER_NODE。(如果数据库版本较低,需要将TIMESTAMP类型换成datetime(3)一劳永逸的做法就是直接将TIMESTAMP换成datetime(3)) 引入Maven依赖 dependenciesdependencygroupIdorg.springframework.bootgroupIdartifactIdspring-boot-starter-webartifactIddependencydependencygroupIdorg.mybatis.spring.bootgroupIdartifactIdmybatis-spring-boot-starterartifactIdversion2.1.0versiondependencydependencygroupIdorg.springframework.bootgroupIdartifactIdspring-boot-starter-testartifactIdscopetestscopedependencydependencygroupIdmysqlgroupIdartifactIdmysql-connector-javaartifactIdscoperuntimescopeversion8.0.12versiondependencydependencygroupIdcom.alibabagroupIdartifactIddruid-spring-boot-starterartifactIdversion1.1.9versiondependencydependencygroupIdcom.baidu.fsggroupIdartifactIduid-generatorartifactIdversion1.0.0-SNAPSHOTversiondependency dependencies 互联网jar包引入(本文用的是此方式) 在maven仓库只找到了一个jar包。 dependencygroupIdcom.xfvape.uidgroupIdartifactIduid-generatorartifactIdversion0.0.4-RELEASEversion dependency排除冲突的依赖 uid-generator中依赖了logback和mybatis。一般在项目搭建过程中springboot中已经有了logback依赖mybatis会作为单独的依赖引入。如果版本和uid-generator中的依赖不一致的话就会导致冲突。为了防止出现这些问题直接排除一劳永逸。 dependencygroupIdcom.baidu.fsggroupIdartifactIduid-generatorartifactIdversion1.0.0-SNAPSHOTversionexclusionsexclusiongroupIdorg.mybatisgroupIdartifactId*artifactIdexclusionexclusions dependency排除冲突的依赖如下(使用本地项目引入的方式也需要排除以下依赖) dependencygroupIdcom.xfvape.uidgroupIdartifactIduid-generatorartifactIdversion0.0.4-RELEASEversionexclusionsexclusiongroupIdorg.mybatisgroupIdartifactId*artifactIdexclusionexclusions dependency我这里用的是mybatis-plusmybatis-plus官方要求的是如果要使用mybatis-plus就不能再单独引入mybatis了所以我这里也是必须排除mybatis的。 配置SpringBoot核心配置 修改配置文件application.properties注意MySQL地址、数据库名称账户等于之前建表的保持一致 server.port9999 spring.datasource.urljdbc:mysql://*.*.*.*:3306/baiduUidGenerator?useUnicodetruecharacterEncodingutf-8useSSLfalse spring.datasource.usernameroot spring.datasource.password* spring.datasource.driver-class-namecom.mysql.jdbc.Driver mybatis.mapper-locationsclasspath:mapper/*.xml mybatis.configuration.map-underscore-to-camel-casetrueMapperScan的dao层接口扫描 核心对象装配为spring的bean。 uid-generator提供了两种生成器: DefaultUidGenerator、CachedUidGenerator。 如对UID生成性能有要求, 请使用CachedUidGenerator。这里装配CachedUidGeneratorDefaultUidGenerator装配方式是一样的。 自定义DisposableWorkerIdAssigner 将源码DisposableWorkerIdAssigner类加入到自己的项目中并将其中的mapper方法修改成自己项目中的方法与启动类同级目录新建DisposableWorkerIdAssigner内容如下 public class DisposableWorkerIdAssigner implements WorkerIdAssigner {private static final Logger LOGGER LoggerFactory.getLogger(DisposableWorkerIdAssigner.class);private WorkerNodeMapper workerNodeMapper;public long assignWorkerId() {WorkerNodeEntity workerNodeEntity buildWorkerNode();workerNodeMapper.addWorkerNode(workerNodeEntity);LOGGER.info(Add worker node: workerNodeEntity);return workerNodeEntity.getId();}private WorkerNodeEntity buildWorkerNode() {WorkerNodeEntity workerNodeEntity new WorkerNodeEntity();if (DockerUtils.isDocker()) {workerNodeEntity.setType(WorkerNodeType.CONTAINER.value());workerNodeEntity.setHostName(DockerUtils.getDockerHost());workerNodeEntity.setPort(DockerUtils.getDockerPort());} else {workerNodeEntity.setType(WorkerNodeType.ACTUAL.value());workerNodeEntity.setHostName(NetUtils.getLocalAddress());workerNodeEntity.setPort(System.currentTimeMillis() - RandomUtils.nextInt(100000));}return workerNodeEntity;} }public class UidGeneratorConfig {public DisposableWorkerIdAssigner disposableWorkerIdAssigner(){DisposableWorkerIdAssigner disposableWorkerIdAssigner new DisposableWorkerIdAssigner();return disposableWorkerIdAssigner; }public CachedUidGenerator initCachedUidGenerator(WorkerIdAssigner workerIdAssigner) {CachedUidGenerator cachedUidGenerator new CachedUidGenerator();cachedUidGenerator.setWorkerIdAssigner(workerIdAssigner);cachedUidGenerator.setBoostPower(3);cachedUidGenerator.setPaddingFactor(50);cachedUidGenerator.setScheduleInterval(60L);return cachedUidGenerator;} }详细配置信息控制 public DefaultUidGenerator defaultUidGenerator(WorkerIdAssigner disposableWorkerIdAssigner) {DefaultUidGenerator defaultUidGenerator new DefaultUidGenerator();defaultUidGenerator.setWorkerIdAssigner(disposableWorkerIdAssigner);defaultUidGenerator.setTimeBits(32);defaultUidGenerator.setWorkerBits(22);defaultUidGenerator.setSeqBits(9);defaultUidGenerator.setEpochStr(2020-01-01);return defaultUidGenerator;}public CachedUidGenerator cachedUidGenerator(WorkerIdAssigner disposableWorkerIdAssigner) {CachedUidGenerator cachedUidGenerator new CachedUidGenerator();cachedUidGenerator.setWorkerIdAssigner(disposableWorkerIdAssigner);cachedUidGenerator.setTimeBits(32);cachedUidGenerator.setWorkerBits(22);cachedUidGenerator.setSeqBits(9);cachedUidGenerator.setEpochStr(2020-01-01);cachedUidGenerator.setBoostPower(3);cachedUidGenerator.setScheduleInterval(60L);return cachedUidGenerator;}mapper服务接口 与启动类同级目录新建WorkerNodeMapper内容如下 public interface WorkerNodeMapper {WorkerNodeEntity getWorkerNodeByHostPort( String host, String port);void addWorkerNode(WorkerNodeEntity workerNodeEntity); }WorkerNodeMapper mapper namespaceorg.zxp.uidgeneratortest.WorkerNodeMapperresultMap idworkerNodeRestypecom.baidu.fsg.uid.worker.entity.WorkerNodeEntityid columnID jdbcTypeBIGINT propertyid/result columnHOST_NAME jdbcTypeVARCHAR propertyhostName/result columnPORT jdbcTypeVARCHAR propertyport/result columnTYPE jdbcTypeINTEGER propertytype/result columnLAUNCH_DATE jdbcTypeDATE propertylaunchDate/result columnMODIFIED jdbcTypeTIMESTAMP propertymodified/result columnCREATED jdbcTypeTIMESTAMP propertycreated/resultMapinsert idaddWorkerNode useGeneratedKeystrue keyPropertyidparameterTypecom.baidu.fsg.uid.worker.entity.WorkerNodeEntityINSERT INTO WORKER_NODE(HOST_NAME,PORT,TYPE,LAUNCH_DATE,MODIFIED,CREATED)VALUES (#{hostName},#{port},#{type},#{launchDate},NOW(),NOW())insertselect idgetWorkerNodeByHostPort resultMapworkerNodeResSELECTID,HOST_NAME,PORT,TYPE,LAUNCH_DATE,MODIFIED,CREATEDFROMWORKER_NODEWHEREHOST_NAME #{host} AND PORT #{port}select mapper 创建UidGenService逻辑类 public class UidGenService {private UidGenerator uidGenerator;public long getUid() {return uidGenerator.getUID();} }分享资源 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4Yf1KiFl-1692670384862)(https://pic.imgdb.cn/item/64d0dc6a1ddac507cc857b30.png)] 获取以上资源请访问开源项目 点击跳转
http://www.yutouwan.com/news/383719/

相关文章:

  • 彩票网站建设应该要注意哪些wordpress多语言企业网站
  • 佛山市网站建设公司如何用ps做网站首页图片
  • 做网赌网站需要多少钱承德的网站建设公司
  • 有哪些做的比较好的网站装修房子的效果图 三室二厅二卫
  • nodejs做网站容易被攻击吗如何备份网站 整站
  • 延边网站建设网页制作免费教程
  • 沧州泊头纯设计网站制作wordpress 查询语句
  • 重庆装修设计网站网站开发诺亚科技
  • 护栏板销售网站怎么做照明灯企业网站织梦模板
  • 建设手机网站经验分享做衬衫的作业网站
  • 分类网站模板企业网站建设合同(一)
  • 龙岗平湖网站开发做一网站困难吗
  • 拖拽建站系统源码网站建设公司 北京
  • 关于加快信用平台网站建设通知网站建设的基本需求有哪些
  • 网站业务郑州优化网站收费标准
  • 余姚本地网站排名3322做网站
  • 怎么样新建一个网站应用商店关键词优化
  • 网站流量用完wordpress权限不能更新
  • 怎样做php网站腾讯企业邮箱登录登录入口
  • 视频网站 界面设计wordpress装多个博客
  • 如何用照片做模板下载网站wordpress++分页
  • 我想做个网站 详解怎么做网站怎么做边框
  • 打折网站运营思路nas怎么做自己的网站
  • 哪个网站做农产品外包公司设计完网站谁负责运营
  • 电子商务网站开发需求分析人与畜禽狗croproation
  • 服务器网站建设维护合同那里做直播网站
  • 昆山苏州网站建设wordpress 主题自定义
  • 网站改版 优势有那些网站可以做推广
  • 网站开发亿玛酷信赖迁移wordpress
  • dw怎么做网站标题图标网页设计与制作教程第三版答案