3 微服务实战系列

来源:互联网 发布:淘宝怎么去同款 编辑:程序博客网 时间:2024/05/22 06:27

1 环境配置

注册中心(服务治理:注册与发现nginx反向代理)111.231.112.151:81  111.231.112.151:8001                    123.207.218.250:8001服务网关    111.231.112.151:80  111.231.112.151:8002                    123.207.218.250:8002

2 配置

  • 2.1 配置线程池/任务执行器

SpringBoot默认单线程执行,需要多线程异步执行,需要@EnableAsync并设置ThreadPoolTaskExecutor,仅仅开启@EnableAsync 1.4.X版本仍是单线程执行,1.5.X会报错提醒设置线程池!

//可以设置执行任务的线程池的数量。默认是单线程。@Beanpublic ThreadPoolTaskScheduler getDefaultThreadPoolScheduler(){ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();threadPoolTaskScheduler.setPoolSize(2);threadPoolTaskScheduler.setThreadNamePrefix("scheduleMoon");return threadPoolTaskScheduler;}//spingBoot默认单线程执行@Bean    public ThreadPoolTaskExecutor createThreadPoolTaskExecutor() {        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();        threadPoolTaskExecutor.setCorePoolSize(10);        threadPoolTaskExecutor.setMaxPoolSize(20);        return threadPoolTaskExecutor;    }
  • 2.2 配置跨域
private CorsConfiguration buildConfig() {          CorsConfiguration corsConfiguration = new CorsConfiguration();          corsConfiguration.addAllowedOrigin("*");          corsConfiguration.addAllowedHeader("*");          corsConfiguration.addAllowedMethod("*");          return corsConfiguration;      }      /**      * 跨域过滤器      * @return      */      @Bean      public CorsFilter corsFilter() {          UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();          source.registerCorsConfiguration("/**", buildConfig()); // 4          return new CorsFilter(source);      }  
  • 2.3 配置文件上传
@Bean     public MultipartConfigElement multipartConfigElement() {         MultipartConfigFactory factory = new MultipartConfigFactory();        //// 设置文件大小限制 ,超了,页面会抛出异常信息,这时候就需要进行异常信息的处理了;        factory.setMaxFileSize("400MB"); //KB,MB        /// 设置总上传数据总大小        factory.setMaxRequestSize("400MB");         //Sets the directory location where files will be stored.        //factory.setLocation("路径地址");        return factory.createMultipartConfig();     }  
  • 2.4 配置mybatis
/** * MyBatis 配置 * */@Configuration@MapperScan(basePackages = "com.ttd.mapper")public class MyBatisConfiguration {    private static final Logger logger = LoggerFactory.getLogger(MyBatisConfiguration.class);    @Bean    public PageHelper pageHelper(DataSource dataSource) {        logger.info("注册MyBatis分页插件PageHelper");        PageHelper pageHelper = new PageHelper();        Properties p = new Properties();        p.setProperty("offsetAsPageNum", "true");        p.setProperty("rowBoundsWithCount", "true");        p.setProperty("reasonable", "true");        pageHelper.setProperties(p);        return pageHelper;    }}

3 多数据源实战

  • 3.1 配置application
# \u4e3b\u6570\u636e\u6e90\uff0c\u9ed8\u8ba4\u7684spring.datasource.type=com.alibaba.druid.pool.DruidDataSourcespring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.url=jdbc:mysql://*****/*****spring.datasource.username=rootspring.datasource.password=****# \u66f4\u591a\u6570\u636e\u6e90custom.datasource.names=ds1,ds2custom.datasource.ds1.type=com.alibaba.druid.pool.DruidDataSourcecustom.datasource.ds1.driver-class-name=com.mysql.jdbc.Drivercustom.datasource.ds1.url=jdbc:mysql://*****/*****custom.datasource.ds1.username=rootcustom.datasource.ds1.password=*****custom.datasource.ds2.type=com.alibaba.druid.pool.DruidDataSourcecustom.datasource.ds2.driver-class-name=com.mysql.jdbc.Drivercustom.datasource.ds2.url=jdbc:mysql://*****/*****custom.datasource.ds2.username=rootcustom.datasource.ds2.password=******# 下面为连接池的补充设置,应用到上面所有数据源中spring.datasource.initialSize=5spring.datasource.minIdle=5spring.datasource.maxActive=20# 配置获取连接等待超时的时间spring.datasource.maxWait=60000# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒spring.datasource.timeBetweenEvictionRunsMillis=60000# 配置一个连接在池中最小生存的时间,单位是毫秒spring.datasource.minEvictableIdleTimeMillis=300000spring.datasource.validationQuery=SELECT 1 FROM DUALspring.datasource.testWhileIdle=truespring.datasource.testOnBorrow=falsespring.datasource.testOnReturn=false# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙spring.datasource.filters=stat,wall,log4jspring.datasource.logSlowSql=true
  • 3.2 注册定义数据源bean
定义bean实现ImportBeanDefinitionRegistrar并在程序入口导入@Import(DynamicDataSourceRegister.class)@Override    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {        System.out.println("####加载数据源####"+ defaultDataSource);        Map<Object, Object> targetDataSources = new HashMap<Object, Object>();        // 将主数据源添加到更多数据源中        targetDataSources.put("dataSource", defaultDataSource);        DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");        // 添加更多数据源        targetDataSources.putAll(customDataSources);        for (String key : customDataSources.keySet()) {            DynamicDataSourceContextHolder.dataSourceIds.add(key);        }        // 创建DynamicDataSource        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();        beanDefinition.setBeanClass(DynamicDataSource.class);        beanDefinition.setSynthetic(true);        MutablePropertyValues mpv = beanDefinition.getPropertyValues();        mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);        mpv.addPropertyValue("targetDataSources", targetDataSources);        registry.registerBeanDefinition("dataSource", beanDefinition);        logger.info("#############################Dynamic DataSource Registry########################");    }
  • 3.3 多数据源aop切换
@Before("@annotation(ds)")    public void changeDataSource(JoinPoint point, TargetDataSource ds) throws Throwable {        String dsId = ds.name();        if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {            logger.error("数据源[{}]不存在,使用默认数据源 > {}", ds.name(), point.getSignature());        } else {            logger.debug("Use DataSource : {} > {}", ds.name(), point.getSignature());            DynamicDataSourceContextHolder.setDataSourceType(ds.name());        }    }    @After("@annotation(ds)")    public void restoreDataSource(JoinPoint point, TargetDataSource ds) {        logger.debug("Revert DataSource : {} > {}", ds.name(), point.getSignature());        DynamicDataSourceContextHolder.clearDataSourceType();    }
  • 3.4 druid数据源监控
/**     * 注册一个StatViewServlet     * #####此配置需要在入口程序添加@ServletComponentScan     * @WebServlet(urlPatterns = "/druid/*",      *      initParams={     *              @WebInitParam(name="allow",value="127.0.0.1"),// IP白名单 (没有配置或者为空,则允许所有访问)     *              @WebInitParam(name="deny",value="192.168.16.111"),// IP黑名单 (存在共同时,deny优先于allow)     *              @WebInitParam(name="loginUsername",value="wolf"),// 用户名     *              @WebInitParam(name="loginPassword",value="wolf"),// 密码     *              @WebInitParam(name="resetEnable",value="false")// 禁用HTML页面上的“Reset All”功能     *      })     *  public class DruidStatViewServlet extends StatViewServlet {     *       *  }     * @return     */    @Bean    public ServletRegistrationBean DruidStatViewServle(){        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(),"/druid/*");        //添加初始化参数:initParams        //白名单://        servletRegistrationBean.addInitParameter("allow","127.0.0.1");        //IP黑名单 (存在共同时,deny优先于allow) : 如果满足deny的话提示:Sorry, you are not permitted to view this page.//        servletRegistrationBean.addInitParameter("deny","192.168.1.73");        //登录查看信息的账号密码.        servletRegistrationBean.addInitParameter("loginUsername", "wolf");        servletRegistrationBean.addInitParameter("loginPassword", "wolf");        //是否能够重置数据.        servletRegistrationBean.addInitParameter("resetEnable", "false");        return servletRegistrationBean;    }    /**     * 注册一个:filterRegistrationBean     * @WebFilter(filterName = "druidWebStatFilter", urlPatterns = "/*",     *  initParams = { @WebInitParam(name = "exclusions", value = "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid2/*") // 忽略资源     *  })     *  public class DruidStatFilter extends WebStatFilter {     *       *  }     * @return     */    @Bean    public FilterRegistrationBean druidStatFilter(){        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());        //添加过滤规则.        filterRegistrationBean.addUrlPatterns("/*");        //添加不需要忽略的格式信息.        filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid2/*");        return filterRegistrationBean;}

这里写图片描述
这里写图片描述

4 redis实战

  • 4.1 redis配置
   /**     * 生成key的策略     *     * @return     */    @Bean    public KeyGenerator keyGenerator() {        logger.info("########################redis 初始化配置!!!#################");        return new KeyGenerator() {            @Override            public Object generate(Object target, Method method, Object... params) {                StringBuilder sb = new StringBuilder();                sb.append(target.getClass().getName());                sb.append(method.getName());                for (Object obj : params) {                    sb.append(obj.toString());                }                return sb.toString();            }        };    }    /**     * 管理缓存     *     * @param redisTemplate     * @return     */    @SuppressWarnings("rawtypes")    @Bean    public CacheManager cacheManager(RedisTemplate redisTemplate) {        RedisCacheManager rcm = new RedisCacheManager(redisTemplate);        //设置缓存过期时间        // rcm.setDefaultExpiration(60);//秒        //设置value的过期时间        Map<String,Long> map=new HashMap();        map.put("test",60L);        rcm.setExpires(map);        return rcm;    }    /**     * RedisTemplate配置     * @param factory     * @return     */    @SuppressWarnings("rawtypes")    @Bean    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {        StringRedisTemplate template = new StringRedisTemplate(factory);        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);        ObjectMapper om = new ObjectMapper();        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);        jackson2JsonRedisSerializer.setObjectMapper(om);        template.setValueSerializer(jackson2JsonRedisSerializer);        template.afterPropertiesSet();        return template;}
  • 4.2 redis utils操作
@Test    public void testRedis() {        /*redisUtil.set("testKey", "spring:session:expirations:1499333280000");        System.out.println(redisUtil.get("testKey"));        System.out.println(redisUtil.exists("testKey"));*/    }
  • 4.3 redis spring业务缓存
@Service("cacheForemanHouseService")@CacheConfig(cacheNames={"cacheForemanHouse"})public class CacheForemanHouseService extends BaseService<CacheForemanHouse, Long> {}

5 安全控制

  • 5.1 request入参解密
 */@WebFilter(filterName = "webAPPFilter", urlPatterns ={ "/foremanInfo/*", "/houseInfo/*", "/orderInfo/*", "/userInfo/*"}, initParams={@WebInitParam(name="excluds", value="/druid/*")})public class WebAPPFilter implements Filter {    protected final Logger logger = LoggerFactory.getLogger(getClass());    private final String iv = DigestUtils.sha256Hex("Totodi!@#").substring(0, 16);    private String[] excluds = null;     @Override    public void destroy() {        excluds = null;    }    private void handleParamter(String queryString, Map<String, String[]> retParams) {        String[] params = queryString.split("&");        for (int i = 0; i < params.length; i++) {            int splitIndex = params[i].indexOf("=");            if (splitIndex == -1) {                continue;            }            String key = params[i].substring(0, splitIndex);            if (splitIndex < params[i].length()) {                String value = params[i].substring(splitIndex + 1);                retParams.put(key, new String[] {value});            }        }    }    @Override    public void doFilter(ServletRequest request, ServletResponse response,  FilterChain chain) throws IOException, ServletException {        HttpServletResponse res = (HttpServletResponse) response;        HttpServletRequest req = (HttpServletRequest) request;        //调试不需要进行加密        if (StringUtils.equalsIgnoreCase(req.getParameter("skipEncryption"), "true") || StringUtils.contains(req.getQueryString(), "skipEncryption")            || StringUtils.contains(req.getRequestURI(), "favicon.ico")) {            chain.doFilter(req, res);            return;        }        // 解决跨域访问        res.addHeader("Access-Control-Allow-Origin", "*");        res.addHeader("Access-Control-Allow-Methods", "GET");        res.addHeader("Access-Control-Allow-Methods", "POST");        res.addHeader("Access-Control-Allow-Headers", "x-requested-with,content-type");        // 判断是否OPTIONS请求        if (req.getMethod().equals("OPTIONS")) {            res.setStatus(HttpStatus.OK.value());            return;        } else {            try {                 Map<String, String[]> retParams = Maps.newHashMap();                 //request key读写                 Map<String, String[]> params = new HashMap<String,String[]>(request.getParameterMap());                 for (Entry<String, String[]> item : params.entrySet()) {                     logger.info(">>>拦截前参数:"+item.getValue()[0]);                     String newVal = AESUtils.decrypt(item.getValue()[0], iv);                     logger.info(">>>拦截后参数:"+newVal);                        handleParamter(newVal, retParams);                 }                  //requet body流读写                /* byte[] buf = new byte[1024];                 while (req.getInputStream().readLine(buf, 0, 1024) > 0) {                     String tem = new String(buf);                     logger.info(">>>拦截前参数:" + tem);                     String newVal = AESUtils.decrypt(tem, iv);                     logger.info(">>>拦截后参数:" + newVal);                      handleParamter(newVal, retParams);                }*/                req = new  ParameterRequestWrapper((HttpServletRequest)req, retParams);                chain.doFilter(req, res);            } catch (Exception e) {                e.printStackTrace();            }        }    }    @Override    public void init(FilterConfig config) throws ServletException {        String vals = config.getInitParameter("excluds");        excluds = vals.split(",");    }}
  • 5.2 response解密

/**     * 输出文本     * @param txt     * @param contextType     */    protected void write(String txt, String contextType) {        try {            if(Strings.isNullOrEmpty(txt)){                return;            }            getResponse().setContentType(contextType);            if (StringUtils.isNotBlank(getRequest().getParameter("skipEncryption")) && getRequest().getParameter("skipEncryption").equals("true")) {                LOGGER.info("response skipEncryption:" + txt);                StreamUtil.writeData(txt.getBytes("UTF-8"), getResponse().getOutputStream());            } else {                LOGGER.info("response 明文:" + txt);                String encryString = AESUtils.aesEncipherString(AESUtils.KEY, AESUtils.IV, txt);                LOGGER.info("response 密文:" + encryString);                StreamUtil.writeData(encryString.getBytes("UTF-8"), getResponse().getOutputStream());            }        } catch (Exception ex) {            throw new RuntimeException(ex);        }}
  • 5.3 配置全局拦截器

@Override    public void addInterceptors(InterceptorRegistry registry) {        // addPathPatterns 用于添加拦截规则        // excludePathPatterns 用户排除拦截        registry.addInterceptor(new GlobalInterceptor()).addPathPatterns("/**").excludePathPatterns("/common/**");//      registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**");        super.addInterceptors(registry);    }
  • 5.4 移动端请求生成唯一hash
//移动设备:mac、os、version、timestamp全局hash校验private void refreHash(ServletContextUtil context, boolean refree) {        HttpServletRequest request = context.getRequest();        String mac = context.getParameter("mac");        String os = context.getParameter("os");        String version = context.getParameter("version");        String hashKey = EncryptUtils.encodeMD532(mac + os + version, null);        BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());         RedisUtil redisUtil = (RedisUtil) factory.getBean("redisUtil");        if (refree) {            if (redisUtil.exists(hashKey)) {                 redisUtil.remove(hashKey);                logger.info("|----------释放hash-----------");            }        } else {            redisUtil.set(hashKey, true);            logger.info("|----------存储hash-----------");        }    }@Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        ServletContextUtil context = ServletContextUtil.getContext();        // 设置本地线程变量        context.setRequest(request);        context.setResponse(response);        HandlerMethod method = (HandlerMethod) handler;        Object[] params = new Object[] { request.getRequestURI(), request.getParameterMap() };        logger.info("request:请求地址:{}-请求参数-{}", params);        boolean ret = authentication(method.getMethodAnnotation(Authentication.class), context);        if (ret) {            refreHash(context, false);        }        return ret;    }
  • 5.5 释放hash
    如支付/订单发起申请,由于网络或者被攻击或者延迟未响应,提示客户端请求处理中,同一设备处理挂起操作。
@Override    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {        ServletContextUtil context = ServletContextUtil.getContext();        refreHash(context, true);    }

6 监听器

  • 6.1 spring application context
public class ApplicationStartUpListener implements ApplicationListener<ApplicationStartingEvent>{    @Override    public void onApplicationEvent(ApplicationStartingEvent event) {        PropertyUtil.loadAllProperties();        System.out.println(">>>>>>>>>>>>>>>>>>>>>>ApplicationStartUpListener EXEC");    }}
  • 6.2 servlet context
@WebListenerpublic class MyServletContextListener implements ServletContextListener {    @Override    public void contextInitialized(ServletContextEvent sce) {        System.out.println("ServletContex初始化");        System.out.println(sce.getServletContext().getServerInfo());    }    @Override    public void contextDestroyed(ServletContextEvent sce) {        System.out.println("ServletContex销毁");    }}

7 请求aop日志切割

@Aspect@Componentpublic class LogOrderAspect {    private static final Logger logger = LoggerFactory.getLogger(LogOrderAspect.class);    @Resource private LogOrderOperationService logOrderService;    @Before("@annotation(orderAspect)")    public void gennerateLog(JoinPoint point, LogOrderAnnotation orderAspect) throws Throwable {        logger.info(">>>>>>订单操作开始aspect");        ServletContextUtil context = ServletContextUtil.getContext();        HttpServletRequest request = context.getRequest();        LogOrderOperation log = new LogOrderOperation();        log.setAction(orderAspect.action());        log.setUserId(LongUtils.parseLong(request.getParameter("userId")));        Map<String, String[]> params = Maps.newHashMap(request.getParameterMap());        params.put("url", new String[]{request.getRequestURI()});        log.setParams(JSON.toJSONString(params));        log.setCreated(new Date());        logOrderService.insertEntry(log);    }    @After("@annotation(orderAspect)")    public void endLog(JoinPoint point, LogOrderAnnotation orderAspect) {        logger.info(">>>>>>订单操作执行完aspect");    }    @AfterReturning("@annotation(orderAspect)")    public void returnLog(JoinPoint point, LogOrderAnnotation orderAspect) {        logger.info(">>>>>>订单操作执行完aspect,return.................");    }}
原创粉丝点击