文档章节

微服务管理平台如何防止开发人员的本地环境污染注册中心

杨尚川
 杨尚川
发布于 2018/09/12 22:57
字数 1205
阅读 630
收藏 3

微服务的代码由两部分构成,一部分是根据模型定义自动生成的,这部分代码不可修改,另外一部分代码是留给开发人员实现业务逻辑的,可以修改。

防止开发人员的本地环境污染注册中心最开始的解决方法是,在自动生成的代码中,强制指定了微服务运行的IP,模板文件applicationProperties.ftl中的相关片段如下:

eureka.instance.prefer-ip-address=true
eureka.instance.ip-address=${serviceDeployServerIp}

当服务开发完毕,需要部署到测试环境的时候,需要执行一个发布服务审核流程,发布服务的操作会给服务打一个版本,在发布服务的时候,去掉了如上用于开发环境的强制指定的IP,发布服务脚本文件service_release.sh的部分相关片段如下:

if [[ $1 = '' ]]
then
	echo "没有指定服务名称"
	exit
fi

cat gi-service/gi-service-$1/standard/src/main/resources/application.properties | awk '{if($0!~/eureka.instance.ip-address=/){print $0}}' > gi-service/gi-service-$1/standard/src/main/resources/application-release.properties

mv gi-service/gi-service-$1/standard/src/main/resources/application-release.properties gi-service/gi-service-$1/standard/src/main/resources/application.properties

用这种解决方案,满足了我们很长一段时间的需求,开发人员自己在本地启动服务向注册中心注册服务并不会把自己的本地内网地址注册上去,注册的地址其实是云端的服务的运行地址,不会对网关以及跨服务调用产生影响。

然而,接下来事情发生了变化。

微服务不再是一直固定运行在一台机器上了,开始引入了机器资源池的概念,资源池中的机器用来运行微服务,每一个微服务在启动的时候会自动地从资源池中挑选一台机器来执行,也就是说,微服务不会有固定的运行IP,这样,上面的解决方案就失效了。

该怎么解决这个问题呢?

思路是,改造注册中心,只允许机器资源池中的机器进行服务的注册,其他的未知机器则只能做服务的查找而不能执行注册操作,这样,开发人员的本地服务就不会注册到注册中心中去了。

注册中心本身是一个web应用,服务的注册和查找也是通过http请求进行的,那么改造注册中心的方式可以使用一个过滤器,具体实现代码如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 防止开发人员的本地环境污染注册中心
 * @date 2018-09-11
 * @author 杨尚川
 */
@Service
public class LimitRegisterFilter implements Filter {
    private static final Logger LOGGER = LoggerFactory.getLogger(LimitRegisterFilter.class);

    @Value("${allow.ips.url}")
    private String allowIpsUrl;
    @Value("${refresh.time.ms}")
    private long refreshTimeInMs;
    private String allowIps="127.0.0.1,0:0:0:0:0:0:0:1,";

    private static AtomicLong accessCount = new AtomicLong();
    private static AtomicLong registerCount = new AtomicLong();
    private static AtomicLong rejectRegisterCount = new AtomicLong();
    private static AtomicLong noNeedRegisterCount = new AtomicLong();
    private static AtomicLong nullProjectNameAndServiceNameRegisterCount = new AtomicLong();
    private static Map<String, Long> lastRegisterTimeMap = new ConcurrentHashMap<>();

    @PostConstruct
    public void init(){
        LOGGER.info("初始化过滤器, 获取允许的IP地址的路径: {}", allowIpsUrl);
        try {
            HttpURLConnection conn = (HttpURLConnection) new URL(allowIpsUrl).openConnection();
            conn.setRequestMethod("GET");
            conn.setDoOutput(true);
            StringBuilder result = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
                String line = null;
                while ((line = reader.readLine()) != null) {
                    result.append(line).append("\n");
                }
            }
            allowIps += result.toString();
        }catch (Exception e){
            LOGGER.error("获取允许的IP地址失败", e);
        }
        LOGGER.info("初始化过滤器, 允许的IP地址: {}", allowIps);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        LOGGER.info("初始化过滤器");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain filterChain) throws IOException, ServletException {
        accessCount.incrementAndGet();
        HttpServletRequest req = (HttpServletRequest)request;
        String uri = req.getRequestURI();
        String method = req.getMethod();
        String ip = request.getRemoteAddr();
        LOGGER.debug("访问信息, ip: {}, method: {}, uri: {}", ip, method, uri);
        if (!"GET".equals(method.toUpperCase())
                && (uri.contains("eureka/apps") || uri.contains("eureka/v2/apps"))) {
            LOGGER.debug("注册服务, ip: {}, method: {}, uri: {}", ip, method, uri);
            String[] attr = uri.split("/");
            String projectNameAndServiceName = null;
            if(uri.contains("eureka/apps") && attr.length>3){
                projectNameAndServiceName = attr[3];
            }
            if(uri.contains("eureka/v2/apps") && attr.length>4){
                projectNameAndServiceName = attr[4];
            }
            registerCount.incrementAndGet();
            if (allowIps == null || !allowIps.contains(ip)) {
                LOGGER.warn("不允许的IP注册: {}, 总的访问次数: {}, 注册次数: {}, 无需注册次数: {}, 拒绝注册次数: {}, 获取不到项目名称和服务名称数: {}", ip, accessCount.get(), registerCount.get(), noNeedRegisterCount.get(), rejectRegisterCount.get(), nullProjectNameAndServiceNameRegisterCount.get());
                rejectRegisterCount.incrementAndGet();
                return;
            }
            if(projectNameAndServiceName==null){
                nullProjectNameAndServiceNameRegisterCount.incrementAndGet();
                return;
            }
            String key = ip+"_"+projectNameAndServiceName+"_"+method;
            Long lastRegisterTime = lastRegisterTimeMap.get(key);
            if(lastRegisterTime != null){
                if((System.currentTimeMillis()-lastRegisterTime) < refreshTimeInMs){
                    noNeedRegisterCount.incrementAndGet();
                    return;
                }
            }
            lastRegisterTimeMap.put(key, System.currentTimeMillis());
        }
        filterChain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        LOGGER.info("销毁过滤器");
    }

    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        LimitRegisterFilter limitRegisterFilter = new LimitRegisterFilter();
        limitRegisterFilter.allowIps = allowIps;
        limitRegisterFilter.refreshTimeInMs = refreshTimeInMs;
        LOGGER.info("注册过滤器ServerRequestAuthFilter, 允许的IP地址: {}", allowIps);
        FilterRegistrationBean registration = new FilterRegistrationBean(limitRegisterFilter);
        registration.addUrlPatterns("/*");
        return registration;
    }
}

 

 

我的开源项目主页 

 

© 著作权归作者所有

杨尚川

杨尚川

粉丝 1103
博文 220
码字总数 1624053
作品 12
东城
架构师
私信 提问
Spring Cloud全家桶主要组件及简要介绍

一、微服务简介 微服务是最近的一两年的时间里是很火的一个概念。感觉不学习一下都快跟不上时代的步伐了,下边做一下简单的总结和介绍。 何为微服务?简而言之,微服务架构风格这种开发方法,...

小致Daddy
06/04
110
0
Choerodon 的微服务之路(四):深入理解微服务配置中心

本文是Choerodon 的微服务系列推文第四篇,上一篇《Choerodon的微服务之路(三):服务注册与发现》介绍了Choerodon的注册中心,并通过代码的形式介绍了 在Choerodon微服务框架中是如何来实现...

Choerodon
01/02
41
0
spring cloud - 概述

什么是微服务? 微服务没有一个标准统一的概念,个人理解为:微服务是一种可以让软件职责单一、松耦合、自包含、可以独立运行和部署的架构思想。 关键思想就是:拆分、单一、独立、组件化。把...

明理萝
2018/09/12
10
1
Choerodon 的微服务之路(三):服务注册与发现

本文是 Choerodon 的微服务之路系列推文第三篇。在上一篇《Choerodon的微服务之路(二):微服务网关》中,介绍了Choerodon 在搭建微服务网关时考虑的一些问题以及两种常见的微服务网关模式,...

Choerodon
2018/11/20
559
0
Spring Cloud微服务实战---1.7.Eureka的微服务注册与管理

在微服务架构中,服务的注册与管理是一项特别重要的基础功能。因为在微服务架构下,随着业务的发展,微服务的数量会越来越多,而且微服务集群规模、微服务的位置、微服务的命名等,都需要维护...

最老程序员闫涛
2018/12/11
0
0

没有更多内容

加载失败,请刷新页面

加载更多

自建redis笔记

自建redis笔记 最近在linux安装了一下redis,特做一些笔记! 本文先单节点启动redis,然后再进行持久化配置,在次基础上,再分享搭建主从模式的配置以及Sentinel 哨兵模式及集群的搭建 单节点...

北极之北
刚刚
0
0
vue+element之多表单验证

方法一:利用promise var p1=new Promise(function(resolve, reject) { this.$refs[form1].validate((valid) => { if(valid){ ......

沉迷代码我爱学习
2分钟前
0
0
golang 1.13 errors 包 新函数介绍

引 这次 errors 包算重量级更新。很有更能把以前的一些设计模式给推到。下面聊下用法。 error 装包 以前返回一个错误,想要保存 error 链,还要定义结构体保存以前的 error 信息。感兴趣看下...

guonaihong
11分钟前
1
0
并发编程之线程池

一、线程池 1、什么是线程池 Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序 都可以使用线程池。在开发过程中,合理地使用线程池能够带来3个好处。 第一:降...

codeobj
14分钟前
1
0
知识点总结思维导图模板分享,良心安利,建议收藏

思维导图经常被用在学习中,对大脑思维进行发散,对知识进行记忆。使用思维导图可以让知识更加简单更有层次。下面是利用思维导图所绘制的几款知识点总结思维导图模板,大家可以进行进行参考使...

干货趣分享
17分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部