parent
							
								
									6125625b87
								
							
						
					
					
						commit
						202267b686
					
				| @ -0,0 +1,31 @@ | ||||
| # 预发布灰度发布 | ||||
| 
 | ||||
| 从1.14.0开始支持预发布、灰度发布,可登陆`SOP-Admin`,然后选择`服务列表`进行操作。 | ||||
| 
 | ||||
| ## 使用预发布 | ||||
| 
 | ||||
| 假设网关工程在阿里云负载均衡有两台服务器,域名分别为: | ||||
| 
 | ||||
| |域名|说明| | ||||
| |:---- |:----  | | ||||
| |open1.domain.com  |网关服务器1  | | ||||
| |openpre.domain.com | 网关服务器2,作为预发布请求入口| | ||||
| 
 | ||||
| 线上域名为`open.domain.com`,请求网关`http://open.domain.com/api`会负载均衡到这两台服务器 | ||||
| 
 | ||||
| 在网关工程打开`com.gitee.sop.gateway.loadbalancer.EnvironmentServerChooser`类,修改`PRE_DOMAIN`变量 | ||||
| 
 | ||||
| ```java | ||||
| /** | ||||
|  * 预发布机器域名 | ||||
|  */ | ||||
| private static final String PRE_DOMAIN = "openpre.domain.com"; | ||||
| ``` | ||||
| 
 | ||||
| 网关工程打包发布到阿里云 | ||||
| 
 | ||||
| 登录SOP-Admin,在服务列表中点击预发布,然后接口的请求地址变成:`http://openpre.domain.com/api` | ||||
| 
 | ||||
| ## 使用灰度发布 | ||||
| 
 | ||||
| 灰度发布可允许指定的用户进行访问,其它用户则走正常流程。 | ||||
| @ -0,0 +1,12 @@ | ||||
| use sop; | ||||
| 
 | ||||
| CREATE TABLE `config_gray_userkey` ( | ||||
|   `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, | ||||
|   `instance_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'serviceId', | ||||
|   `user_key_content` text COMMENT '用户key,多个用引文逗号隔开', | ||||
|   `name_version_content` text COMMENT '需要灰度的接口,goods.get=1.2,order.list=1.2', | ||||
|   `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP, | ||||
|   `gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | ||||
|   PRIMARY KEY (`id`), | ||||
|   UNIQUE KEY `uk_instanceid` (`instance_id`) USING BTREE | ||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='灰度发布用户key'; | ||||
| @ -1,12 +1,16 @@ | ||||
| package com.gitee.sop.adminserver; | ||||
| 
 | ||||
| import com.gitee.fastmybatis.core.FastmybatisConfig; | ||||
| import org.springframework.boot.SpringApplication; | ||||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | ||||
| 
 | ||||
| import java.util.Arrays; | ||||
| 
 | ||||
| @SpringBootApplication | ||||
| public class SopAdminServerApplication { | ||||
| 
 | ||||
| 	public static void main(String[] args) { | ||||
| 		FastmybatisConfig.defaultIgnoreUpdateColumns = Arrays.asList("gmt_create", "gmt_modified"); | ||||
| 		SpringApplication.run(SopAdminServerApplication.class, args); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,26 @@ | ||||
| package com.gitee.sop.adminserver.api.service.param; | ||||
| 
 | ||||
| import com.gitee.easyopen.doc.annotation.ApiDocField; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| 
 | ||||
| import javax.validation.constraints.NotBlank; | ||||
| 
 | ||||
| /** | ||||
|  * @author tanghc | ||||
|  */ | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| @Data | ||||
| public class ServiceInstanceGrayParam extends ServiceInstanceParam { | ||||
| 
 | ||||
|     @ApiDocField(description = "灰度发布用户,多个用英文逗号隔开") | ||||
|     @NotBlank(message = "灰度发布用户不能为空") | ||||
|     private String userKeyContent; | ||||
| 
 | ||||
|     @ApiDocField(description = "灰度发布接口名版本号如:order.get1.0=1.2,多个用英文逗号隔开") | ||||
|     @NotBlank(message = "灰度发布接口名版本号不能为空") | ||||
|     private String nameVersionContent; | ||||
| 
 | ||||
|     @ApiDocField(description = "是否仅更新灰度用户") | ||||
|     private Boolean onlyUpdateGrayUserkey; | ||||
| } | ||||
| @ -0,0 +1,12 @@ | ||||
| package com.gitee.sop.adminserver.bean; | ||||
| 
 | ||||
| import lombok.Data; | ||||
| 
 | ||||
| /** | ||||
|  * @author tanghc | ||||
|  */ | ||||
| @Data | ||||
| public class UserKeyDefinition { | ||||
|     private String serviceId; | ||||
|     private String data; | ||||
| } | ||||
| @ -0,0 +1,60 @@ | ||||
| package com.gitee.sop.adminserver.common; | ||||
| 
 | ||||
| /** | ||||
|  * @author tanghc | ||||
|  */ | ||||
| public enum ChannelOperation { | ||||
|     /** | ||||
|      * 限流推送路由配置-修改 | ||||
|      */ | ||||
|     LIMIT_CONFIG_UPDATE("update"), | ||||
| 
 | ||||
|     /** | ||||
|      * 路由信息更新 | ||||
|      */ | ||||
|     ROUTE_CONFIG_UPDATE("update"), | ||||
| 
 | ||||
|     /** | ||||
|      * isv信息修改 | ||||
|      */ | ||||
|     ISV_INFO_UPDATE("update"), | ||||
| 
 | ||||
|     /** | ||||
|      * 黑名单消息类型:添加 | ||||
|      */ | ||||
|     BLACKLIST_ADD("add"), | ||||
|     /** | ||||
|      * 黑名单消息类型:删除 | ||||
|      */ | ||||
|     BLACKLIST_DELETE("delete"), | ||||
| 
 | ||||
|     /** | ||||
|      * 路由权限配置更新 | ||||
|      */ | ||||
|     ROUTE_PERMISSION_UPDATE("update"), | ||||
|     /** | ||||
|      * 路由权限加载 | ||||
|      */ | ||||
|     ROUTE_PERMISSION_RELOAD("reload"), | ||||
| 
 | ||||
|     /** | ||||
|      * 灰度发布用户key设置 | ||||
|      */ | ||||
|     GRAY_USER_KEY_SET("set"), | ||||
|     /** | ||||
|      * 灰度发布用户key清除 | ||||
|      */ | ||||
|     GRAY_USER_KEY_CLEAR("clear"), | ||||
| 
 | ||||
|     ; | ||||
| 
 | ||||
|     private String operation; | ||||
| 
 | ||||
|     ChannelOperation(String operation) { | ||||
|         this.operation = operation; | ||||
|     } | ||||
| 
 | ||||
|     public String getOperation() { | ||||
|         return operation; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,42 @@ | ||||
| package com.gitee.sop.adminserver.entity; | ||||
| 
 | ||||
| import lombok.Data; | ||||
| 
 | ||||
| import javax.persistence.Column; | ||||
| import javax.persistence.GeneratedValue; | ||||
| import javax.persistence.GenerationType; | ||||
| import javax.persistence.Id; | ||||
| import javax.persistence.Table; | ||||
| import java.util.Date; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * 表名:config_gray_userkey | ||||
|  * 备注:灰度发布用户key | ||||
|  * | ||||
|  * @author tanghc | ||||
|  */ | ||||
| @Table(name = "config_gray_userkey") | ||||
| @Data | ||||
| public class ConfigGrayUserkey { | ||||
|     @Id | ||||
|     @Column(name = "id") | ||||
|     @GeneratedValue(strategy = GenerationType.IDENTITY) | ||||
|     /**  数据库字段:id */ | ||||
|     private Long id; | ||||
| 
 | ||||
|     /** instanceId, 数据库字段:instance_id */ | ||||
|     private String instanceId; | ||||
| 
 | ||||
|     /** 用户key,多个用引文逗号隔开, 数据库字段:user_key_content */ | ||||
|     private String userKeyContent; | ||||
| 
 | ||||
|     /** 需要灰度的接口,goods.get=1.2,order.list=1.2, 数据库字段:name_version_content */ | ||||
|     private String nameVersionContent; | ||||
| 
 | ||||
|     /**  数据库字段:gmt_create */ | ||||
|     private Date gmtCreate; | ||||
| 
 | ||||
|     /**  数据库字段:gmt_modified */ | ||||
|     private Date gmtModified; | ||||
| } | ||||
| @ -0,0 +1,12 @@ | ||||
| package com.gitee.sop.adminserver.mapper; | ||||
| 
 | ||||
| import com.gitee.fastmybatis.core.mapper.CrudMapper; | ||||
| 
 | ||||
| import com.gitee.sop.adminserver.entity.ConfigGrayUserkey; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * @author tanghc | ||||
|  */ | ||||
| public interface ConfigGrayUserkeyMapper extends CrudMapper<ConfigGrayUserkey, Long> { | ||||
| } | ||||
| @ -0,0 +1,30 @@ | ||||
| package com.gitee.sop.gatewaycommon.bean; | ||||
| 
 | ||||
| import org.springframework.beans.BeansException; | ||||
| import org.springframework.context.ApplicationContext; | ||||
| import org.springframework.context.ApplicationContextAware; | ||||
| 
 | ||||
| /** | ||||
|  * @author tanghc | ||||
|  */ | ||||
| public class SpringContext implements ApplicationContextAware { | ||||
| 
 | ||||
|     private static ApplicationContext ctx; | ||||
| 
 | ||||
|     @Override | ||||
|     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { | ||||
|         ctx = applicationContext; | ||||
|     } | ||||
| 
 | ||||
|     public static <T> T getBean(Class<T> clazz) { | ||||
|         return ctx.getBean(clazz); | ||||
|     } | ||||
| 
 | ||||
|     public static Object getBean(String beanName) { | ||||
|         return ctx.getBean(beanName); | ||||
|     } | ||||
| 
 | ||||
|     public static ApplicationContext getApplicationContext() { | ||||
|         return ctx; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,12 @@ | ||||
| package com.gitee.sop.gatewaycommon.bean; | ||||
| 
 | ||||
| import lombok.Data; | ||||
| 
 | ||||
| /** | ||||
|  * @author tanghc | ||||
|  */ | ||||
| @Data | ||||
| public class UserKeyDefinition { | ||||
|     private String serviceId; | ||||
|     private String data; | ||||
| } | ||||
| @ -0,0 +1,42 @@ | ||||
| package com.gitee.sop.gateway.entity; | ||||
| 
 | ||||
| import lombok.Data; | ||||
| 
 | ||||
| import javax.persistence.Column; | ||||
| import javax.persistence.GeneratedValue; | ||||
| import javax.persistence.GenerationType; | ||||
| import javax.persistence.Id; | ||||
| import javax.persistence.Table; | ||||
| import java.util.Date; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * 表名:config_gray_userkey | ||||
|  * 备注:灰度发布用户key | ||||
|  * | ||||
|  * @author tanghc | ||||
|  */ | ||||
| @Table(name = "config_gray_userkey") | ||||
| @Data | ||||
| public class ConfigGrayUserkey { | ||||
|     @Id | ||||
|     @Column(name = "id") | ||||
|     @GeneratedValue(strategy = GenerationType.IDENTITY) | ||||
|     /**  数据库字段:id */ | ||||
|     private Long id; | ||||
| 
 | ||||
|     /** instanceId, 数据库字段:instance_id */ | ||||
|     private String instanceId; | ||||
| 
 | ||||
|     /** 用户key,多个用引文逗号隔开, 数据库字段:user_key_content */ | ||||
|     private String userKeyContent; | ||||
| 
 | ||||
|     /** 需要灰度的接口,goods.get=1.2,order.list=1.2, 数据库字段:name_version_content */ | ||||
|     private String nameVersionContent; | ||||
| 
 | ||||
|     /**  数据库字段:gmt_create */ | ||||
|     private Date gmtCreate; | ||||
| 
 | ||||
|     /**  数据库字段:gmt_modified */ | ||||
|     private Date gmtModified; | ||||
| } | ||||
| @ -0,0 +1,116 @@ | ||||
| package com.gitee.sop.gateway.loadbalancer; | ||||
| 
 | ||||
| import com.gitee.sop.gateway.manager.UserKeyManager; | ||||
| import com.gitee.sop.gatewaycommon.bean.SpringContext; | ||||
| import com.gitee.sop.gatewaycommon.param.ApiParam; | ||||
| import com.gitee.sop.gatewaycommon.param.Param; | ||||
| import com.gitee.sop.gatewaycommon.param.ParamNames; | ||||
| import com.gitee.sop.gatewaycommon.zuul.ZuulContext; | ||||
| import com.gitee.sop.gatewaycommon.zuul.loadbalancer.BaseServerChooser; | ||||
| import com.netflix.loadbalancer.Server; | ||||
| import com.netflix.niws.loadbalancer.DiscoveryEnabledServer; | ||||
| import com.netflix.zuul.context.RequestContext; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| 
 | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| /** | ||||
|  * 预发布、灰度环境选择,参考自:https://segmentfault.com/a/1190000017412946
 | ||||
|  * | ||||
|  * @author tanghc | ||||
|  */ | ||||
| public class EnvironmentServerChooser extends BaseServerChooser { | ||||
| 
 | ||||
|     private static final String MEDATA_KEY_ENV = "env"; | ||||
|     private static final String ENV_PRE_VALUE = "pre"; | ||||
|     private static final String ENV_GRAY_VALUE = "gray"; | ||||
|     /** | ||||
|      * 预发布机器域名 | ||||
|      */ | ||||
|     private static final String PRE_DOMAIN = "localhost"; | ||||
| 
 | ||||
|     @Override | ||||
|     protected boolean match(Server server) { | ||||
|         // eureka存储的metadata
 | ||||
|         Map<String, String> metadata = ((DiscoveryEnabledServer) server).getInstanceInfo().getMetadata(); | ||||
|         String env = metadata.get(MEDATA_KEY_ENV); | ||||
|         return StringUtils.isNotBlank(env); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 这里判断客户端能否访问,可以根据ip地址,域名,header内容来决定是否可以访问预发布环境 | ||||
|      * | ||||
|      * @param server  服务器实例 | ||||
|      * @param request request | ||||
|      * @return | ||||
|      */ | ||||
|     @Override | ||||
|     protected boolean canVisit(Server server, HttpServletRequest request) { | ||||
|         // eureka存储的metadata
 | ||||
|         Map<String, String> metadata = ((DiscoveryEnabledServer) server).getInstanceInfo().getMetadata(); | ||||
|         String env = metadata.get(MEDATA_KEY_ENV); | ||||
|         boolean canVisit; | ||||
|         switch (env) { | ||||
|             case ENV_PRE_VALUE: | ||||
|                 canVisit = canVisitPre(server, request); | ||||
|                 break; | ||||
|             case ENV_GRAY_VALUE: | ||||
|                 canVisit = canVisitGray(server, request); | ||||
|                 break; | ||||
|             default: | ||||
|                 canVisit = false; | ||||
|         } | ||||
|         return canVisit; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 通过判断hostname来确定是否是预发布请求,可修改此方法实现自己想要的 | ||||
|      * | ||||
|      * @param request request | ||||
|      * @return 返回true:可以进入到预发环境 | ||||
|      */ | ||||
|     protected boolean canVisitPre(Server server, HttpServletRequest request) { | ||||
|         String serverName = request.getServerName(); | ||||
|         return PRE_DOMAIN.equals(serverName); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 能否进入灰度环境,可修改此方法实现自己想要的 | ||||
|      * | ||||
|      * @param request request | ||||
|      * @return 返回true:可以进入到预发环境 | ||||
|      */ | ||||
|     protected boolean canVisitGray(Server server, HttpServletRequest request) { | ||||
|         ApiParam apiParam = ZuulContext.getApiParam(); | ||||
|         UserKeyManager userKeyManager = SpringContext.getBean(UserKeyManager.class); | ||||
|         boolean canVisit = false; | ||||
|         if (this.isGrayUser(apiParam, userKeyManager, server, request)) { | ||||
|             // 指定灰度版本号
 | ||||
|             String instanceId = server.getId(); | ||||
|             String newVersion = userKeyManager.getVersion(instanceId, apiParam.fetchNameVersion()); | ||||
|             if (newVersion != null) { | ||||
|                 RequestContext.getCurrentContext().getZuulRequestHeaders().put(ParamNames.HEADER_VERSION_NAME, newVersion); | ||||
|                 canVisit = true; | ||||
|             } | ||||
|         } | ||||
|         return canVisit; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * 是否是灰度用户 | ||||
|      * | ||||
|      * @param param          接口参数 | ||||
|      * @param userKeyManager userKey管理 | ||||
|      * @param server   服务器实例 | ||||
|      * @param request        request | ||||
|      * @return true:是 | ||||
|      */ | ||||
|     protected boolean isGrayUser(Param param, UserKeyManager userKeyManager, Server server, HttpServletRequest request) { | ||||
|         String instanceId = server.getId(); | ||||
|         // 这里的灰度用户为appKey,包含此appKey则为灰度用户,允许访问
 | ||||
|         String appKey = param.fetchAppKey(); | ||||
|         return userKeyManager.containsKey(instanceId, appKey); | ||||
|     } | ||||
| } | ||||
| @ -1,55 +0,0 @@ | ||||
| package com.gitee.sop.gateway.loadbalancer; | ||||
| 
 | ||||
| import com.gitee.sop.gatewaycommon.zuul.loadbalancer.BaseServerChooser; | ||||
| import com.netflix.loadbalancer.Server; | ||||
| import com.netflix.niws.loadbalancer.DiscoveryEnabledServer; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| 
 | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
| import java.util.Map; | ||||
| import java.util.Objects; | ||||
| 
 | ||||
| /** | ||||
|  * 预发布环境选择,参考自:https://segmentfault.com/a/1190000017412946
 | ||||
|  * | ||||
|  * @author tanghc | ||||
|  */ | ||||
| public class PreEnvironmentServerChooser extends BaseServerChooser { | ||||
| 
 | ||||
|     private static final String MEDATA_KEY_ENV = "env"; | ||||
|     private static final String ENV_PRE_VALUE = "pre"; | ||||
| 
 | ||||
|     private static final String HOST_NAME = "localhost"; | ||||
| 
 | ||||
|     @Override | ||||
|     protected boolean match(Server server) { | ||||
|         // eureka存储的metadata
 | ||||
|         Map<String, String> metadata = ((DiscoveryEnabledServer) server).getInstanceInfo().getMetadata(); | ||||
|         String env = metadata.get(MEDATA_KEY_ENV); | ||||
|         return StringUtils.isNotBlank(env); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 这里判断客户端能否访问,可以根据ip地址,域名,header内容来决定是否可以访问预发布环境 | ||||
|      * @param server  服务器实例 | ||||
|      * @param request request | ||||
|      * @return | ||||
|      */ | ||||
|     @Override | ||||
|     protected boolean canVisit(Server server, HttpServletRequest request) { | ||||
|         // eureka存储的metadata
 | ||||
|         Map<String, String> metadata = ((DiscoveryEnabledServer) server).getInstanceInfo().getMetadata(); | ||||
|         String env = metadata.get(MEDATA_KEY_ENV); | ||||
|         return Objects.equals(ENV_PRE_VALUE, env) && canClientVisit(request); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 通过判断hostname来确定是否是预发布请求,如果需要通过其它条件判断,修改此方法 | ||||
|      * @param request request | ||||
|      * @return 返回true:可以进入到预发环境 | ||||
|      */ | ||||
|     protected boolean canClientVisit(HttpServletRequest request) { | ||||
|         String serverName = request.getServerName(); | ||||
|         return HOST_NAME.equals(serverName); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,33 @@ | ||||
| package com.gitee.sop.gateway.loadbalancer; | ||||
| 
 | ||||
| import lombok.Data; | ||||
| 
 | ||||
| import java.util.Map; | ||||
| import java.util.Set; | ||||
| 
 | ||||
| /** | ||||
|  * @author tanghc | ||||
|  */ | ||||
| @Data | ||||
| public class ServiceGrayConfig { | ||||
|     /** | ||||
|      * 用户id | ||||
|      */ | ||||
|     private Set<String> userKeys; | ||||
| 
 | ||||
|     /** 存放接口隐射关系,key:nameversion,value:newVersion */ | ||||
|     private Map<String, String> grayNameVersion; | ||||
| 
 | ||||
|     public boolean containsKey(Object userKey) { | ||||
|         return userKeys.contains(String.valueOf(userKey)); | ||||
|     } | ||||
| 
 | ||||
|     public String getVersion(String name) { | ||||
|         return grayNameVersion.get(name); | ||||
|     } | ||||
| 
 | ||||
|     public void clear() { | ||||
|         this.userKeys.clear(); | ||||
|         this.grayNameVersion.clear(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,132 @@ | ||||
| package com.gitee.sop.gateway.manager; | ||||
| 
 | ||||
| import com.alibaba.fastjson.JSON; | ||||
| import com.gitee.sop.gateway.entity.ConfigGrayUserkey; | ||||
| import com.gitee.sop.gateway.loadbalancer.ServiceGrayConfig; | ||||
| import com.gitee.sop.gateway.mapper.ConfigGrayUserkeyMapper; | ||||
| import com.gitee.sop.gatewaycommon.bean.ChannelMsg; | ||||
| import com.gitee.sop.gatewaycommon.bean.UserKeyDefinition; | ||||
| import com.gitee.sop.gatewaycommon.manager.ZookeeperContext; | ||||
| import com.google.common.collect.Maps; | ||||
| import com.google.common.collect.Sets; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.core.env.Environment; | ||||
| import org.springframework.stereotype.Service; | ||||
| 
 | ||||
| import javax.annotation.PostConstruct; | ||||
| import java.util.Arrays; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.stream.Collectors; | ||||
| import java.util.stream.Stream; | ||||
| 
 | ||||
| /** | ||||
|  * 存放用户key,这里放在本机内容,如果灰度发布保存的用户id数量偏多,可放在redis中 | ||||
|  * | ||||
|  * @author tanghc | ||||
|  */ | ||||
| @Service | ||||
| @Slf4j | ||||
| public class UserKeyManager { | ||||
| 
 | ||||
|     /** | ||||
|      * KEY:instanceId | ||||
|      */ | ||||
|     private Map<String, ServiceGrayConfig> serviceUserKeyMap = Maps.newConcurrentMap(); | ||||
| 
 | ||||
|     @Autowired | ||||
|     private Environment environment; | ||||
| 
 | ||||
|     @Autowired | ||||
|     private ConfigGrayUserkeyMapper configGrayUserkeyMapper; | ||||
| 
 | ||||
|     public boolean containsKey(String serviceId, Object userKey) { | ||||
|         if (serviceId == null || userKey == null) { | ||||
|             return false; | ||||
|         } | ||||
|         return this.getServiceGrayConfig(serviceId).containsKey(userKey); | ||||
|     } | ||||
| 
 | ||||
|     public String getVersion(String serviceId, String nameVersion) { | ||||
|         if (serviceId == null || nameVersion == null) { | ||||
|             return null; | ||||
|         } | ||||
|         return this.getServiceGrayConfig(serviceId).getVersion(nameVersion); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 设置用户key | ||||
|      * | ||||
|      * @param configGrayUserkey 灰度配置 | ||||
|      */ | ||||
|     public void setServiceGrayConfig(String serviceId, ConfigGrayUserkey configGrayUserkey) { | ||||
|         if (configGrayUserkey == null) { | ||||
|             return; | ||||
|         } | ||||
|         this.clear(serviceId); | ||||
|         String userKeyData = configGrayUserkey.getUserKeyContent(); | ||||
|         String nameVersionContent = configGrayUserkey.getNameVersionContent(); | ||||
|         String[] userKeys = StringUtils.split(userKeyData, ','); | ||||
|         String[] nameVersionList = StringUtils.split(nameVersionContent, ','); | ||||
|         log.info("添加userKey,userKeys.length:{}, nameVersionList:{}", userKeys.length, Arrays.toString(nameVersionList)); | ||||
| 
 | ||||
|         List<String> list = Stream.of(userKeys).collect(Collectors.toList()); | ||||
|         ServiceGrayConfig serviceGrayConfig = getServiceGrayConfig(serviceId); | ||||
|         serviceGrayConfig.getUserKeys().addAll(list); | ||||
| 
 | ||||
|         Map<String, String> grayNameVersion = serviceGrayConfig.getGrayNameVersion(); | ||||
|         for (String nameVersion : nameVersionList) { | ||||
|             String[] nameVersionInfo = StringUtils.split(nameVersion, '='); | ||||
|             String name = nameVersionInfo[0]; | ||||
|             String version = nameVersionInfo[1]; | ||||
|             grayNameVersion.put(name, version); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 清空用户key | ||||
|      */ | ||||
|     public void clear(String serviceId) { | ||||
|         getServiceGrayConfig(serviceId).clear(); | ||||
|     } | ||||
| 
 | ||||
|     public ServiceGrayConfig getServiceGrayConfig(String serviceId) { | ||||
|         ServiceGrayConfig serviceGrayConfig = serviceUserKeyMap.get(serviceId); | ||||
|         if (serviceGrayConfig == null) { | ||||
|             serviceGrayConfig = new ServiceGrayConfig(); | ||||
|             serviceGrayConfig.setUserKeys(Sets.newConcurrentHashSet()); | ||||
|             serviceGrayConfig.setGrayNameVersion(Maps.newConcurrentMap()); | ||||
|             serviceUserKeyMap.put(serviceId, serviceGrayConfig); | ||||
|         } | ||||
|         return serviceGrayConfig; | ||||
|     } | ||||
| 
 | ||||
|     @PostConstruct | ||||
|     protected void after() throws Exception { | ||||
|         ZookeeperContext.setEnvironment(environment); | ||||
|         String isvChannelPath = ZookeeperContext.getUserKeyChannelPath(); | ||||
|         ZookeeperContext.listenPath(isvChannelPath, nodeCache -> { | ||||
|             String nodeData = new String(nodeCache.getCurrentData().getData()); | ||||
|             ChannelMsg channelMsg = JSON.parseObject(nodeData, ChannelMsg.class); | ||||
|             String data = channelMsg.getData(); | ||||
|             UserKeyDefinition userKeyDefinition = JSON.parseObject(data, UserKeyDefinition.class); | ||||
|             String serviceId = userKeyDefinition.getServiceId(); | ||||
|             switch (channelMsg.getOperation()) { | ||||
|                 case "set": | ||||
|                     ConfigGrayUserkey configGrayUserkey = configGrayUserkeyMapper.getByColumn("service_id", serviceId); | ||||
|                     this.setServiceGrayConfig(serviceId, configGrayUserkey); | ||||
|                     break; | ||||
|                 case "clear": | ||||
|                     clear(serviceId); | ||||
|                     break; | ||||
|                 default: | ||||
|                     log.error("userKey消息,错误的消息指令,nodeData:{}", nodeData); | ||||
| 
 | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,11 @@ | ||||
| package com.gitee.sop.gateway.mapper; | ||||
| 
 | ||||
| import com.gitee.fastmybatis.core.mapper.CrudMapper; | ||||
| import com.gitee.sop.gateway.entity.ConfigGrayUserkey; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * @author tanghc | ||||
|  */ | ||||
| public interface ConfigGrayUserkeyMapper extends CrudMapper<ConfigGrayUserkey, Long> { | ||||
| } | ||||
					Loading…
					
					
				
		Reference in new issue