信息发布网站源码分享(网站内信息发布工具)

大家好,关于信息发布网站源码分享很多朋友都还不太明白,今天小编就来为大家分享关于网站内信息发布工具的知识,希望对各位有所帮助!

精品专栏

《GitHub:中文详细注释的开源项目》

摘要:原创出处http://www.iocoder.cn/Apollo/portal-publish/「芋道源码」欢迎转载,保留摘要,谢谢!

1.概述2.实体2.1Release2.2ReleaseHistory2.3ReleaseMessage3.Portal侧3.1ReleaseController3.2ReleaseService3.3ReleaseAPI4.AdminService侧4.1ReleaseController4.2ReleaseService4.3ReleaseRepository4.4ReleaseHistoryService4.5ReleaseHistoryRepository666.彩蛋

1.概述

老艿艿:本系列假定胖友已经阅读过《Apollo官方wiki文档》。

从本文开始,我们进入Apollo最最最核心的流程配置发布后的实时推送设计。

在配置中心中,一个重要的功能就是配置发布后实时推送到客户端。下面我们简要看一下这块是怎么设计实现的。

配置发布

上图简要描述了配置发布的大致过程:

用户在Portal操作配置发布Portal调用AdminService的接口操作发布AdminService发布配置后,发送ReleaseMessage给各个ConfigServiceConfigService收到ReleaseMessage后,通知对应的客户端

本文分享Portal发布配置,对应上述第一、二步,大体流程如下:

流程

这个流程过程中,我们先不考虑灰度发布,会涉及配置合并的过程。

老艿艿:因为Portal是管理后台,所以从代码实现上,和业务系统非常相像。也因此,本文会略显啰嗦。

2.实体

2.1Release

com.ctrip.framework.apollo.biz.entity.Release,继承BaseEntity抽象类,Release实体。代码如下:

@Entity\n@Table(name=”Release”)\n@SQLDelete(sql=”UpdateReleasesetisDeleted=1whereid=?”)//标记删除\n@Where(clause=”isDeleted=0″)\npublicclassReleaseextendsBaseEntity{\n/**\n*ReleaseKey\n*\n*【TODO6006】用途?\n*/\n@Column(name=”ReleaseKey”,nullable=false)\nprivateStringreleaseKey;\n/**\n*标题\n*/\n@Column(name=”Name”,nullable=false)\nprivateStringname;\n/**\n*备注\n*/\n@Column(name=”Comment”,nullable=false)\nprivateStringcomment;\n/**\n*App编号\n*/\n@Column(name=”AppId”,nullable=false)\nprivateStringappId;\n/**\n*Cluster名字\n*/\n@Column(name=”ClusterName”,nullable=false)\nprivateStringclusterName;\n/**\n*Namespace名字\n*/\n@Column(name=”NamespaceName”,nullable=false)\nprivateStringnamespaceName;\n/**\n*配置Map字符串,使用JSON格式化成字符串\n*/\n@Column(name=”Configurations”,nullable=false)\n@Lob\nprivateStringconfigurations;\n/**\n*是否被回滚(放弃)\n*/\n@Column(name=”IsAbandoned”,columnDefinition=”Bitdefault’0′”)\nprivatebooleanisAbandoned;\n}\n

releaseKey字段,【TODO6006】用途?name字段,发布标题。comment字段,发布备注。appId+clusterName+namespaceName字段,指向对应的Namespace记录。configurations字段,发布时的完整配置Map字符串,使用JSON格式化成字符串。和Commit.changeSets字段,格式一致,只是它是变化配置Map字符串。例子如下:

{“huidu01″:”huidu01”}\n

xisAbandoned字段,是否被回滚(放弃)。

2.2ReleaseHistory

com.ctrip.framework.apollo.biz.entity.ReleaseHistory,继承BaseEntity抽象类,ReleaseHistory实体,记录每次Release相关的操作日志。代码如下:

@Entity\n@Table(name=”ReleaseHistory”)\n@SQLDelete(sql=”UpdateReleaseHistorysetisDeleted=1whereid=?”)//标记删除\n@Where(clause=”isDeleted=0″)\npublicclassReleaseHistoryextendsBaseEntity{\n/**\n*App编号\n*/\n@Column(name=”AppId”,nullable=false)\nprivateStringappId;\n/**\n*Cluster名字\n*/\n@Column(name=”ClusterName”,nullable=false)\nprivateStringclusterName;\n/**\n*Namespace名字\n*/\n@Column(name=”NamespaceName”,nullable=false)\nprivateStringnamespaceName;\n/**\n*Branch名\n*\n*主干,使用Cluster名字\n*分支,使用子Cluster名字\n*/\n@Column(name=”BranchName”,nullable=false)\nprivateStringbranchName;\n/**\n*Release编号\n*/\n@Column(name=”ReleaseId”)\nprivatelongreleaseId;\n/**\n*上一次Release编号\n*/\n@Column(name=”PreviousReleaseId”)\nprivatelongpreviousReleaseId;\n/**\n*操作类型{@linkcom.ctrip.framework.apollo.common.constants.ReleaseOperation}\n*/\n@Column(name=”Operation”)\nprivateintoperation;\n/**\n*操作Context\n*/\n@Column(name=”OperationContext”,nullable=false)\nprivateStringoperationContext;\n}\n

appId+clusterName+namespaceName字段,指向对应的Namespace记录。branchName字段,Branch名字。主干,使用Cluster名字。分支,使用Cluster名字。releaseId字段,Release编号。previousReleaseId字段,上一次Release编号。operation类型,操作类型。在com.ctrip.framework.apollo.common.constants.ReleaseOperation类中,枚举了所有发布相关的操作类型。代码如下:

publicinterfaceReleaseOperation{\nintNORMAL_RELEASE=0;//主干发布\nintROLLBACK=1;//回滚\nintGRAY_RELEASE=2;//灰度发布\nintAPPLY_GRAY_RULES=3;//\nintGRAY_RELEASE_MERGE_TO_MASTER=4;\nintMASTER_NORMAL_RELEASE_MERGE_TO_GRAY=5;\nintMATER_ROLLBACK_MERGE_TO_GRAY=6;\nintABANDON_GRAY_RELEASE=7;\nintGRAY_RELEASE_DELETED_AFTER_MERGE=8;\n}\n

operationContext字段,操作Context。

2.3ReleaseMessage

下一篇文章,详细分享。

3.Portal侧

3.1ReleaseController

在apollo-portal项目中,com.ctrip.framework.apollo.portal.controller.ReleaseController,提供Release的API

在【发布】的界面中,点击【发布】按钮,调用发布配置的API

发布配置

appId,hasReleaseNamespacePermissio(appId,namespaceName)方法,校验是否有发布配置的权限。后续文章,详细分享。com.ctrip.framework.apollo.portal.entity.model.NamespaceReleaseModel,Namespace配置发布Model。代码如下:

publicclassNamespaceReleaseModelimplementsVerifiable{\n/**\n*App编号\n*/\nprivateStringappId;\n/**\n*Env名字\n*/\nprivateStringenv;\n/**\n*Cluster名字\n*/\nprivateStringclusterName;\n/**\n*Namespace名字\n*/\nprivateStringnamespaceName;\n/**\n*发布标题\n*/\nprivateStringreleaseTitle;\n/**\n*发布描述\n*/\nprivateStringreleaseComment;\n/**\n*发布人\n*/\nprivateStringreleasedBy;\n/**\n*是否紧急发布\n*/\nprivatebooleanisEmergencyPublish;\n@Override\npublicbooleanisInvalid(){\nreturnStringUtils.isContainEmpty(appId,env,clusterName,namespaceName,releaseTitle);//校验非空\n}\n}\n

第14行:校验NamespaceReleaseModel非空。第15至19行:设置PathVariable变量到NamespaceReleaseModel中。第20至23行:校验若是紧急发布,但是当前环境未允许该操作,抛出BadRequestException异常。紧急发布功能,可通过设置PortalDB的ServerConfig的”emergencyPublish.supported.envs”配置开启对应的Env们。例如,emergencyPublish.supported.envs=dev。第25行:调用ReleaseServicepublishEvent(event)方法,发布ConfigPublishEvent事件。这部分,我们在后续文章分享。第38行:返回ReleaseDTO对象。

3.2ReleaseService

在apollo-portal项目中,com.ctrip.framework.apollo.portal.service.ReleaseService,提供Release的Service逻辑。

createRelease(appId,env,clusterName,namespaceName,releaseTitle,releaseComment,releaseBy,isEmergencyPublish)方法,调用AdminServiceAPI,发布配置。第19行:【TODO6001】Tracer日志

3.3ReleaseAPI

com.ctrip.framework.apollo.portal.api.ReleaseAPI,实现API抽象类,封装对AdminService的Release模块的API调用。代码如下:

ReleaseAPI

4.AdminService侧

4.1ReleaseController

在apollo-adminservice项目中,com.ctrip.framework.apollo.adminservice.controller.ReleaseController,提供Release的API

publish(namespace,releaseName,releaseComment,operator,isEmergencyPublish)方法,发布Namespace的配置,返回Release对象。第26至33行:获得发布消息的Cluster名字。第27行:调用NamespaceServicesendMessage(Stringmessage,Stringchannel)方法,发送发布消息。详细实现,下一篇文章详细解析。第38行:调用BeanUtilspublish(namespace,releaseName,releaseComment,operator,isEmergencyPublish)方法,发布Namespace的配置。代码如下:

1:privateGsongson=newGson();\n2:\n3:@Autowired\n4:privateReleaseRepositoryreleaseRepository;\n5:@Autowired\n6:privateItemServiceitemService;\n7:@Autowired\n8:privateAuditServiceauditService;\n9:@Autowired\n10:privateNamespaceLockServicenamespaceLockService;\n11:@Autowired\n12:privateNamespaceServicenamespaceService;\n13:@Autowired\n14:privateReleaseHistoryServicereleaseHistoryService;\n15:\n16:@Transactional\n17:publicReleasepublish(Namespacenamespace,StringreleaseName,StringreleaseComment,Stringoperator,booleanisEmergencyPublish){\n18://校验锁定\n19:checkLock(namespace,isEmergencyPublish,operator);\n20://获得Namespace的普通配置Map\n21:Map<String,String>operateNamespaceItems=getNamespaceItems(namespace);\n22://获得父Namespace\n23:NamespaceparentNamespace=namespaceService.findParentNamespace(namespace);\n24://若有父Namespace,则是子Namespace,进行灰度发布\n25://branchrelease\n26:if(parentNamespace!=null){\n27:returnpublishBranchNamespace(parentNamespace,namespace,operateNamespaceItems,releaseName,releaseComment,operator,isEmergencyPublish);\n28:}\n29://获得子Namespace对象\n30:NamespacechildNamespace=namespaceService.findChildNamespace(namespace);\n31://获得上一次,并且有效的Release对象\n32:ReleasepreviousRelease=null;\n33:if(childNamespace!=null){\n34:previousRelease=findLatestActiveRelease(namespace);\n35:}\n36://创建操作Context\n37://masterrelease\n38:Map<String,Object>operationContext=Maps.newHashMap();\n39:operationContext.put(ReleaseOperationContext.IS_EMERGENCY_PUBLISH,isEmergencyPublish);\n40://主干发布\n41:Releaserelease=masterRelease(namespace,releaseName,releaseComment,operateNamespaceItems,operator,ReleaseOperation.NORMAL_RELEASE,operationContext);//是否紧急发布。\n42://若有子Namespace时,自动将主干合并到子Namespace,并进行一次子Namespace的发布\n43://mergetobranchandautorelease\n44:if(childNamespace!=null){\n45:mergeFromMasterAndPublishBranch(namespace,childNamespace,operateNamespaceItems,\n46:releaseName,releaseComment,operator,previousRelease,\n47:release,isEmergencyPublish);\n48:}\n49:returnrelease;\n50:}\n

第19行:调用getNamespaceItems(namespace)方法,获得Namespace的普通配置Map。代码如下:

privateMap<String,String>getNamespaceItems(Namespacenamespace){\n//读取Namespace的Item集合\nList<Item>items=itemService.findItemsWithoutOrdered(namespace.getId());\n//生成普通配置Map。过滤掉注释和空行的配置项\nMap<String,String>configurations=newHashMap<String,String>();\nfor(Itemitem:items){\nif(StringUtils.isEmpty(item.getKey())){\ncontinue;\n}\nconfigurations.put(item.getKey(),item.getValue());\n}\nreturnconfigurations;\n}\n

第23行:调用findChildNamespace(namespace)方法,获得子Namespace对象。详细解析,见《Apollo源码解析——Portal创建灰度》。第31至35行:调用masterRelease(namespace,releaseName,releaseComment,operateNamespaceItems,operator,releaseOperation,operationContext)方法,主干发布配置。创建的Namespace,默认就是主干,而灰度发布使用的是分支。第42至48行:调用masterRelease(namespace,releaseName,releaseComment,operateNamespaceItems,operator,releaseOperation,operationContext)方法,主干发布配置。代码如下:

1:privateReleasemasterRelease(Namespacenamespace,StringreleaseName,StringreleaseComment,\n2:Map<String,String>configurations,Stringoperator,\n3:intreleaseOperation,Map<String,Object>operationContext){\n4://获得最后有效的Release对象\n5:ReleaselastActiveRelease=findLatestActiveRelease(namespace);\n6:longpreviousReleaseId=lastActiveRelease==null?0:lastActiveRelease.getId();\n7://创建Release对象,并保存\n8:Releaserelease=createRelease(namespace,releaseName,releaseComment,configurations,operator);\n9:\n10://创建ReleaseHistory对象,并保存\n11:releaseHistoryService.createReleaseHistory(namespace.getAppId(),namespace.getClusterName(),\n12:namespace.getNamespaceName(),namespace.getClusterName(),\n13:release.getId(),previousReleaseId,releaseOperation,\n14:operationContext,operator);\n15:returnrelease;\n16:}\n

第5行:调用createRelease(namespace,releaseName,releaseComment,configurations,operator)方法,创建Release对象,并保存。第10至14行:调用ReleaseHistoryServicecreateRelease(namespace,releaseName,releaseComment,configurations,operator)方法,创建Release对象,并保存。代码如下:

1:privateReleasecreateRelease(Namespacenamespace,Stringname,Stringcomment,\n2:Map<String,String>configurations,Stringoperator){\n3://创建Release对象\n4:Releaserelease=newRelease();\n5:release.setReleaseKey(ReleaseKeyGenerator.generateReleaseKey(namespace));//【TODO6006】ReleaseKey用途?\n6:release.setDataChangeCreatedTime(newDate());\n7:release.setDataChangeCreatedBy(operator);\n8:release.setDataChangeLastModifiedBy(operator);\n9:release.setName(name);\n10:release.setComment(comment);\n11:release.setAppId(namespace.getAppId());\n12:release.setClusterName(namespace.getClusterName());\n13:release.setNamespaceName(namespace.getNamespaceName());\n14:release.setConfigurations(gson.toJson(configurations));//使用Gson,将配置Map格式化成字符串。\n15://保存Release对象\n16:release=releaseRepository.save(release);\n17://释放NamespaceLock\n18:namespaceLockService.unlock(namespace.getId());\n19://记录Audit到数据库中\n20:auditService.audit(Release.class.getSimpleName(),release.getId(),Audit.OP.INSERT,release.getDataChangeCreatedBy());\n21:returnrelease;\n22:}\n

第4至14行:创建Release对象,并设置对应的属性。第5行:【TODO6006】ReleaseKey用途?第14行:调用`Gsonsave(Release)方法,保存Release对象。第18行:调用NamespaceLockServicecreateReleaseHistory(appId,clusterName,namespaceName,branchName,releaseId,previousReleaseId,operation,operationContext,operator)方法,创建ReleaseHistory对象,并保存。代码如下:

1:privateGsongson=newGson();\n2:\n3:@Autowired\n4:privateReleaseHistoryRepositoryreleaseHistoryRepository;\n5:@Autowired\n6:privateAuditServiceauditService;\n7:\n8:@Transactional\n9:publicReleaseHistorycreateReleaseHistory(StringappId,StringclusterName,StringnamespaceName,StringbranchName,\n10:longreleaseId,longpreviousReleaseId,intoperation,\n11:Map<String,Object>operationContext,Stringoperator){\n12://创建ReleaseHistory对象\n13:ReleaseHistoryreleaseHistory=newReleaseHistory();\n14:releaseHistory.setAppId(appId);\n15:releaseHistory.setClusterName(clusterName);\n16:releaseHistory.setNamespaceName(namespaceName);\n17:releaseHistory.setBranchName(branchName);\n18:releaseHistory.setReleaseId(releaseId);//Release编号\n19:releaseHistory.setPreviousReleaseId(previousReleaseId);//上一个Release编号\n20:releaseHistory.setOperation(operation);\n21:if(operationContext==null){\n22:releaseHistory.setOperationContext(“{}”);//defaultemptyobject\n23:}else{\n24:releaseHistory.setOperationContext(gson.toJson(operationContext));\n25:}\n26:releaseHistory.setDataChangeCreatedTime(newDate());\n27:releaseHistory.setDataChangeCreatedBy(operator);\n28:releaseHistory.setDataChangeLastModifiedBy(operator);\n29://保存ReleaseHistory对象\n30:releaseHistoryRepository.save(releaseHistory);\n31://记录Audit到数据库中\n32:auditService.audit(ReleaseHistory.class.getSimpleName(),releaseHistory.getId(),Audit.OP.INSERT,releaseHistory.getDataChangeCreatedBy());\n33:returnreleaseHistory;\n34:}\n

第12至28行:创建ReleaseHistory对象,并设置对应的属性。第30行:调用ReleaseHistoryRepository#save(ReleaseHistory)方法,保存ReleaseHistory对象。第32行:记录Audit到数据库中。

4.5ReleaseHistoryRepository

com.ctrip.framework.apollo.biz.repository.ReleaseHistoryRepository,继承org.springframework.data.repository.PagingAndSortingRepository接口,提供ReleaseHistory的数据访问给AdminService和ConfigService。代码如下:

publicinterfaceReleaseHistoryRepositoryextendsPagingAndSortingRepository<ReleaseHistory,Long>{\nPage<ReleaseHistory>findByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(StringappId,String\nclusterName,StringnamespaceName,Pageablepageable);\nPage<ReleaseHistory>findByReleaseIdAndOperationOrderByIdDesc(longreleaseId,intoperation,Pageablepageable);\nPage<ReleaseHistory>findByPreviousReleaseIdAndOperationOrderByIdDesc(longpreviousReleaseId,intoperation,Pageablepageable);\n@Modifying\n@Query(“updateReleaseHistorysetisdeleted=1,DataChange_LastModifiedBy=?4whereappId=?1andclusterName=?2andnamespaceName=?3”)\nintbatchDelete(StringappId,StringclusterName,StringnamespaceName,Stringoperator);\n}\n

666.彩蛋

TT终于要到比较干的地方啦。

文章到此结束,如果本次分享的信息发布网站源码分享和网站内信息发布工具的问题解决了您的问题,那么我们由衷的感到高兴!

Published by

风君子

独自遨游何稽首 揭天掀地慰生平