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................."); }}
阅读全文
0 0
- 3 微服务实战系列
- 微服务实战系列文章
- 微服务实战系列文章
- 4 微服务实战系列
- 1 微服务实战系列
- 2 微服务实战系列
- 5 微服务实战系列
- 微服务实战系列文章推荐
- 微服务实战
- SpringCloud微服务实战
- 微服务实战系列--Nginx官网发布(转)
- 微服务实战系列--Nginx官网发布(转)
- 微服务实战(转载)
- 微服务实战-端到端流程
- 微服务实战-理论篇
- 微服务实战之微服务介绍
- 微服务实战:从架构到部署
- 微服务实战:使用API Gateway
- 安卓学习笔记---调用相机功能在安卓7.0以上报错android.os.FileUriExposedException
- Linux的ls命令
- ORA-22835:缓冲区对于CLOB到CHAR转换而言太小
- 十个jQuery代码片段助力Web开发
- FFmpeg的Android端调用库ffmpeg-android的简单使用
- 3 微服务实战系列
- 重新铸就Json
- 7-14 天梯地图(30 分)
- windows下的cmd总结
- ElasticSearch5.X嵌套桶(三)
- WebSocket与消息推送
- opencv中伪彩色applyColorMap函数(C++ / Python)
- 杭电ACM OJ 1006 Tick and Tick 厌倦时钟 其实就是简单的数学题
- Linux安装Tomcat环境