Repository: Xlinlin/SpringCloud-Demo
Branch: master
Commit: 5efd083d427f
Files: 742
Total size: 22.6 MB
Directory structure:
gitextract_ftqt9i7b/
├── .gitattributes
├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE.txt
├── README.md
├── SpringBoot-Admin/
│ ├── pom.xml
│ ├── readme.md
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── xiao/
│ │ └── spring/
│ │ └── boot/
│ │ └── admin/
│ │ └── SpringBootAdminApplication.java
│ └── resources/
│ ├── application.yml
│ └── bootstrap.yml
├── SpringBoot-Custom-Elasticsearch-Starter/
│ ├── Custom-Elasticsearch-Starter/
│ │ └── pom.xml
│ ├── Custom-Elasticsearch-Starter-Autoconfig/
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xiao/
│ │ │ └── custom/
│ │ │ └── elasticsearch/
│ │ │ └── start/
│ │ │ └── autoconfig/
│ │ │ ├── ElasticsearchAutoConfiguration.java
│ │ │ └── properties/
│ │ │ ├── ElasticsearchProperties.java
│ │ │ └── HostInfo.java
│ │ └── resources/
│ │ └── META-INF/
│ │ └── spring.factories
│ ├── Custom-Elasticsearch-Starter-Example/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── xiao/
│ │ │ │ └── custom/
│ │ │ │ └── elasticsearch/
│ │ │ │ └── starter/
│ │ │ │ └── example/
│ │ │ │ └── ElasticsearchApplication.java
│ │ │ └── resources/
│ │ │ ├── application.properties
│ │ │ ├── application.yml
│ │ │ ├── bootstrap.yml
│ │ │ └── logback-spring.xml
│ │ └── test/
│ │ └── java/
│ │ └── com/
│ │ └── xiao/
│ │ └── custom/
│ │ └── elasticsearch/
│ │ └── starter/
│ │ └── example/
│ │ └── ElasticsearchApplicationTest.java
│ ├── Readme.md
│ └── pom.xml
├── SpringBoot-Custom-Rest-Starter/
│ ├── Readme.md
│ ├── SpringBoot-Custom-Rest-Autconfigure/
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xiao/
│ │ │ └── custom/
│ │ │ └── rest/
│ │ │ └── starter/
│ │ │ └── autoconfigure/
│ │ │ ├── config/
│ │ │ │ ├── RestTemplateConfiguration.java
│ │ │ │ └── properties/
│ │ │ │ ├── HttpPoolProperties.java
│ │ │ │ └── OkHttpProperties.java
│ │ │ ├── dto/
│ │ │ │ └── Request.java
│ │ │ ├── interceptor/
│ │ │ │ └── RestInterceptor.java
│ │ │ ├── log/
│ │ │ │ ├── annotation/
│ │ │ │ │ ├── RequestLog.java
│ │ │ │ │ └── RequestLogAspect.java
│ │ │ │ ├── dto/
│ │ │ │ │ └── HttpRequestLog.java
│ │ │ │ └── service/
│ │ │ │ └── HttpRequestLogService.java
│ │ │ ├── service/
│ │ │ │ ├── HttpClientService.java
│ │ │ │ └── impl/
│ │ │ │ ├── HttpClientAsyncServiceImpl.java
│ │ │ │ ├── HttpClientServiceImpl.java
│ │ │ │ └── HttpRetryService.java
│ │ │ └── util/
│ │ │ ├── RequestValidatorParamsUtil.java
│ │ │ └── ThreadLocalUtil.java
│ │ └── resources/
│ │ └── META-INF/
│ │ └── spring.factories
│ ├── SpringBoot-Custom-Rest-Example/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── xiao/
│ │ │ │ └── custom/
│ │ │ │ └── rest/
│ │ │ │ └── example/
│ │ │ │ ├── RestExampleApp.java
│ │ │ │ └── log/
│ │ │ │ └── impl/
│ │ │ │ └── HttpLogServiceImpl.java
│ │ │ └── resources/
│ │ │ ├── application.yml
│ │ │ └── logback-spring.xml
│ │ └── test/
│ │ └── java/
│ │ └── com/
│ │ └── xiao/
│ │ └── custom/
│ │ └── rest/
│ │ └── example/
│ │ ├── RestTemplateStarterAppTest.java
│ │ ├── httpclient/
│ │ │ └── HttpClientTest.java
│ │ └── template/
│ │ └── RestTemplateTest.java
│ └── pom.xml
├── SpringBoot-Stock-Demo/
│ ├── doc/
│ │ ├── bootstrap.sh
│ │ ├── stock.sql
│ │ └── stock_demo_jmeter.jmx
│ ├── pom.xml
│ ├── readme.md
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── xiao/
│ │ └── stock/
│ │ └── demo/
│ │ ├── StockDemoApplication.java
│ │ ├── common/
│ │ │ └── StockOptTypeEnum.java
│ │ ├── configure/
│ │ │ ├── DataSourceConfiguration.java
│ │ │ └── RedissonConfiguration.java
│ │ ├── entity/
│ │ │ ├── OrderDemo.java
│ │ │ ├── StockChangeLodDemo.java
│ │ │ └── StockDemo.java
│ │ ├── mapper/
│ │ │ ├── OrderDemoMapper.java
│ │ │ ├── StockChangeLodDemoMapper.java
│ │ │ └── StockDemoMapper.java
│ │ ├── rest/
│ │ │ └── StockRestService.java
│ │ ├── service/
│ │ │ ├── StockDemoService.java
│ │ │ └── impl/
│ │ │ └── StockDemoServiceImpl.java
│ │ └── util/
│ │ ├── OrderNoUtil.java
│ │ └── StockUtil.java
│ └── resources/
│ ├── application.yml
│ ├── com/
│ │ └── xiao/
│ │ └── stock/
│ │ └── demo/
│ │ └── mapper/
│ │ ├── OrderDemoMapper.xml
│ │ ├── StockChangeLodDemoMapper.xml
│ │ └── StockDemoMapper.xml
│ └── logback-spring.xml
├── SpringCloud-Canal/
│ ├── doc/
│ │ └── ServerRunningMonitor源码注解.md
│ ├── pom.xml
│ ├── readme.md
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ ├── alibaba/
│ │ │ └── canal/
│ │ │ └── simple/
│ │ │ └── ClientSample.java
│ │ └── xiao/
│ │ └── springcloud/
│ │ ├── CanalSimpleApplication.java
│ │ ├── canal/
│ │ │ ├── CanalClientService.java
│ │ │ └── CanalConfig.java
│ │ └── disruptor/
│ │ ├── DataEvent.java
│ │ ├── DataEventFactory.java
│ │ ├── DisruptorConsumer.java
│ │ ├── DisruptorExceptionHandler.java
│ │ ├── DisruptorProducer.java
│ │ ├── DisruptorThreadFactory.java
│ │ ├── TableData.java
│ │ └── service/
│ │ ├── DisruptorService.java
│ │ └── impl/
│ │ └── DisruptorServiceImpl.java
│ └── resources/
│ └── application.properties
├── SpringCloud-Common/
│ ├── README.md
│ ├── pom.xml
│ ├── script/
│ │ ├── auto_deploy.sh
│ │ ├── bootstrap.sh
│ │ └── remote_deploy.sh
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── xiao/
│ └── springcloud/
│ └── demo/
│ └── common/
│ ├── SkywalkingService.java
│ ├── cache/
│ │ ├── README.md
│ │ ├── code/
│ │ │ ├── FastJsonCodec.java
│ │ │ └── SerializerObject.java
│ │ ├── conf/
│ │ │ └── RedissonConfig.java
│ │ ├── dto/
│ │ │ └── EntryDto.java
│ │ ├── local/
│ │ │ └── SpringGuavaCacheConfig.java
│ │ └── service/
│ │ ├── CacheService.java
│ │ ├── DistributedService.java
│ │ └── impl/
│ │ ├── CacheServiceRedisImpl.java
│ │ └── DistributedServiceRedissonImpl.java
│ ├── conf/
│ │ └── FeignConfiguration.java
│ ├── disruptor/
│ │ ├── DataEventFactory.java
│ │ ├── DisruptorConsumer.java
│ │ ├── DisruptorExceptionHandler.java
│ │ ├── DisruptorProducer.java
│ │ ├── DisruptorThreadFactory.java
│ │ ├── data/
│ │ │ ├── BasisData.java
│ │ │ ├── DataEvent.java
│ │ │ └── EventEnum.java
│ │ ├── event/
│ │ │ └── ServiceEvent.java
│ │ ├── readme.md
│ │ └── service/
│ │ ├── DisruptorService.java
│ │ └── impl/
│ │ └── DisruptorServiceImpl.java
│ ├── eureka/
│ │ └── LoadBalancerAspect.java
│ ├── exception/
│ │ ├── AbstractServiceException.java
│ │ ├── CommonException.java
│ │ └── CommonExceptionEnum.java
│ ├── forkjoin/
│ │ ├── ForkjoinConfiguration.java
│ │ ├── ForkjoinService.java
│ │ └── task/
│ │ └── ForkjoinTask.java
│ ├── gloab/
│ │ ├── interceptor/
│ │ │ ├── README.md
│ │ │ ├── advice/
│ │ │ │ └── DefaultControllerAdvice.java
│ │ │ ├── config/
│ │ │ │ ├── FastjsonConfig.java
│ │ │ │ └── FeignConfig.java
│ │ │ └── fegin/
│ │ │ ├── CommonFeignErrorDecoder.java
│ │ │ ├── CommonFeignHeaderProcessInterceptor.java
│ │ │ ├── DefaultCommonErrorAttributes.java
│ │ │ ├── FeignBeanFactoryPostProcessor.java
│ │ │ └── HttpContext.java
│ │ └── response/
│ │ ├── ErrorResponseData.java
│ │ ├── ResponseData.java
│ │ └── SuccessResponseData.java
│ ├── logaspect/
│ │ ├── LogAnnotation.java
│ │ ├── LogAspect.java
│ │ ├── LogInfo.java
│ │ ├── LogService.java
│ │ ├── README.md
│ │ └── Slf4jLogService.java
│ ├── sign/
│ │ ├── SignConstants.java
│ │ ├── annotation/
│ │ │ ├── DisposeSign.java
│ │ │ └── DisposeSignService.java
│ │ ├── filter/
│ │ │ └── WrapperRequestFilter.java
│ │ ├── readme.md
│ │ ├── request/
│ │ │ └── BodyReaderHttpServletRequestWrapper.java
│ │ ├── service/
│ │ │ ├── AppManagerService.java
│ │ │ └── impl/
│ │ │ └── AppManagerServiceConfigImpl.java
│ │ └── util/
│ │ ├── AsciiSortUtil.java
│ │ ├── HttpRequestUtils.java
│ │ └── SignUtil.java
│ ├── util/
│ │ ├── CodeFormatConstants.java
│ │ ├── DateUtils.java
│ │ ├── ListPageUtil.java
│ │ ├── StringLengthUtils.java
│ │ ├── encode/
│ │ │ ├── AESEncryption.java
│ │ │ ├── AESType.java
│ │ │ ├── BinaryHelper.java
│ │ │ ├── ByteUtils.java
│ │ │ ├── HMACUtil.java
│ │ │ └── Md5DigestUtil.java
│ │ └── image/
│ │ ├── ImageDHashUtil.java
│ │ └── ImagePHashUtil.java
│ └── validator/
│ ├── CodePrefix.java
│ ├── ParamAspect.java
│ ├── ParamValidator.java
│ ├── ParamVerify.java
│ ├── Validator.java
│ └── VerifyConstants.java
├── SpringCloud-ConfigCenter/
│ ├── Readme.txt
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── xiao/
│ │ └── springcloud/
│ │ └── configure/
│ │ └── ConfigureApplication.java
│ └── resources/
│ └── application.yml
├── SpringCloud-Configure/
│ ├── README.txt
│ ├── consumer/
│ │ ├── springcloud-dev.properties
│ │ ├── springcloud-dev.yml
│ │ ├── springcloud-prod.properties
│ │ ├── springcloud-prod.yml
│ │ ├── springcloud-test.properties
│ │ └── springcloud-test.yml
│ ├── eureka-server/
│ │ ├── eureka-server-dev.properties
│ │ ├── eureka-server-dev.yml
│ │ ├── eureka-server-test.properties
│ │ └── eureka-server-test.yml
│ ├── pom.xml
│ └── redisson/
│ ├── redission-dev.yml
│ ├── redission-sentinel-dev.yml
│ ├── redisson-cloud.yml
│ ├── redisson-cluster-dev.yml
│ └── redisson-cluster-test.yml
├── SpringCloud-Consumer/
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── xiao/
│ │ └── skywalking/
│ │ └── consumer/
│ │ ├── ConsumerApp.java
│ │ ├── common/
│ │ │ ├── CommonConstants.java
│ │ │ ├── CommonException.java
│ │ │ ├── ExceptionEnum.java
│ │ │ ├── advice/
│ │ │ │ ├── GlobalExceptionAdvice.java
│ │ │ │ └── UnifiedReturnAdvice.java
│ │ │ └── response/
│ │ │ ├── ErrorResponseData.java
│ │ │ ├── ResponseData.java
│ │ │ └── SuccessResponseData.java
│ │ ├── controller/
│ │ │ └── FeignContoller.java
│ │ ├── feign/
│ │ │ ├── FeignService.java
│ │ │ └── impl/
│ │ │ ├── FQA
│ │ │ └── FeignServiceImpl.java
│ │ └── ribbon/
│ │ └── RibbonService.java
│ └── resources/
│ ├── application.yml
│ └── bootstrap.yml
├── SpringCloud-Custom-ConfigCenter/
│ ├── README.MD
│ ├── custom-config-client/
│ │ ├── README.md
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xiao/
│ │ │ └── custom/
│ │ │ └── config/
│ │ │ └── client/
│ │ │ ├── configuration/
│ │ │ │ ├── ConfigClientAutoConfiguration.java
│ │ │ │ ├── ConfigClientHealthProperties.java
│ │ │ │ ├── ConfigClientProperties.java
│ │ │ │ ├── ConfigClientStateHolder.java
│ │ │ │ ├── ConfigClientWatch.java
│ │ │ │ ├── ConfigServerHealthIndicator.java
│ │ │ │ ├── ConfigServiceBootstrapConfiguration.java
│ │ │ │ ├── ConfigServicePropertySourceLocator.java
│ │ │ │ ├── DiscoveryClientConfigServiceBootstrapConfiguration.java
│ │ │ │ └── RetryProperties.java
│ │ │ ├── environment/
│ │ │ │ ├── Environment.java
│ │ │ │ └── PropertySource.java
│ │ │ ├── netty/
│ │ │ │ ├── client/
│ │ │ │ │ └── NettyClient.java
│ │ │ │ ├── coder/
│ │ │ │ │ ├── ProtoDecoder.java
│ │ │ │ │ └── ProtoEncoder.java
│ │ │ │ ├── dto/
│ │ │ │ │ ├── CommandEnum.java
│ │ │ │ │ └── Message.java
│ │ │ │ ├── factory/
│ │ │ │ │ ├── CoderFactory.java
│ │ │ │ │ └── NamedThreadFactory.java
│ │ │ │ ├── handler/
│ │ │ │ │ └── ServiceHandler.java
│ │ │ │ └── util/
│ │ │ │ ├── ProtostuffUtil.java
│ │ │ │ └── RemotingUtil.java
│ │ │ └── refresh/
│ │ │ ├── api/
│ │ │ │ └── RefreshController.java
│ │ │ ├── component/
│ │ │ │ └── RefreshBeanConfig.java
│ │ │ └── service/
│ │ │ ├── ConfigRefreshService.java
│ │ │ └── impl/
│ │ │ └── ConfigRefreshServiceImpl.java
│ │ └── resources/
│ │ └── META-INF/
│ │ └── spring.factories
│ ├── custom-config-dependencies/
│ │ └── pom.xml
│ ├── custom-config-pojo/
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xiao/
│ │ │ └── custom/
│ │ │ └── config/
│ │ │ └── pojo/
│ │ │ ├── common/
│ │ │ │ └── BaseQuery.java
│ │ │ ├── dto/
│ │ │ │ ├── ApplicationConfigDto.java
│ │ │ │ ├── ApplicationDto.java
│ │ │ │ ├── ClientHostInfoDto.java
│ │ │ │ ├── ConfigItemDto.java
│ │ │ │ ├── ConfigItemGroupDto.java
│ │ │ │ ├── RegionDto.java
│ │ │ │ └── ServerHostConfigDto.java
│ │ │ ├── entity/
│ │ │ │ ├── Application.java
│ │ │ │ ├── ApplicationConfig.java
│ │ │ │ ├── ApplicationItemGroupRelation.java
│ │ │ │ ├── AuthUser.java
│ │ │ │ ├── ClientApplication.java
│ │ │ │ ├── ClientHostInfo.java
│ │ │ │ ├── ClientInfo.java
│ │ │ │ ├── ConfigItem.java
│ │ │ │ ├── ConfigItemGroup.java
│ │ │ │ ├── ConfigItemGroupRelation.java
│ │ │ │ ├── Region.java
│ │ │ │ ├── Role.java
│ │ │ │ └── ServerHostConfig.java
│ │ │ ├── mapper/
│ │ │ │ ├── ApplicationConfigMapper.java
│ │ │ │ ├── ApplicationItemGroupRelationMapper.java
│ │ │ │ ├── ApplicationMapper.java
│ │ │ │ ├── AuthMapper.java
│ │ │ │ ├── ClientApplicationMapper.java
│ │ │ │ ├── ClientHostInfoMapper.java
│ │ │ │ ├── ConfigItemGroupMapper.java
│ │ │ │ ├── ConfigItemGroupRelationMapper.java
│ │ │ │ ├── ConfigItemMapper.java
│ │ │ │ ├── RegionMapper.java
│ │ │ │ └── ServerHostConfigMapper.java
│ │ │ └── query/
│ │ │ ├── AppQuery.java
│ │ │ ├── ApplicationConfigQuery.java
│ │ │ ├── ClientHostInfoQuery.java
│ │ │ ├── ConfigItemGroupQuery.java
│ │ │ ├── ConfigItemQuery.java
│ │ │ ├── RegionQuery.java
│ │ │ └── ServerHostConfigQuery.java
│ │ └── resources/
│ │ └── com/
│ │ └── xiao/
│ │ └── custom/
│ │ └── config/
│ │ └── pojo/
│ │ └── mapper/
│ │ ├── ApplicationConfigMapper.xml
│ │ ├── ApplicationItemGroupRelationMapper.xml
│ │ ├── ApplicationMapper.xml
│ │ ├── AuthMapper.xml
│ │ ├── ClientApplicationMapper.xml
│ │ ├── ClientHostInfoMapper.xml
│ │ ├── ConfigItemGroupMapper.xml
│ │ ├── ConfigItemGroupRelationMapper.xml
│ │ ├── ConfigItemMapper.xml
│ │ ├── RegionMapper.xml
│ │ └── ServerHostConfigMapper.xml
│ ├── custom-config-server/
│ │ ├── README.md
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xiao/
│ │ │ └── custom/
│ │ │ └── config/
│ │ │ └── server/
│ │ │ ├── ConfigerCenterApplication.java
│ │ │ ├── annotation/
│ │ │ │ └── CustomEnableConfigServer.java
│ │ │ ├── config/
│ │ │ │ └── CustomEnvironmentRepositoryConfiguration.java
│ │ │ ├── controller/
│ │ │ │ └── RefreshController.java
│ │ │ ├── environment/
│ │ │ │ └── CustomEnvironmentRepository.java
│ │ │ ├── manager/
│ │ │ │ ├── ClientManagerService.java
│ │ │ │ ├── SqlConstants.java
│ │ │ │ └── impl/
│ │ │ │ └── ClientManagerServiceDbImpl.java
│ │ │ ├── netty/
│ │ │ │ ├── coder/
│ │ │ │ │ ├── ProtoDecoder.java
│ │ │ │ │ └── ProtoEncoder.java
│ │ │ │ ├── dto/
│ │ │ │ │ ├── CommandEnum.java
│ │ │ │ │ └── Message.java
│ │ │ │ ├── factory/
│ │ │ │ │ ├── CoderFactory.java
│ │ │ │ │ └── NamedThreadFactory.java
│ │ │ │ ├── handler/
│ │ │ │ │ └── ServiceHandler.java
│ │ │ │ ├── manager/
│ │ │ │ │ ├── Connection.java
│ │ │ │ │ └── ConnectionManager.java
│ │ │ │ ├── server/
│ │ │ │ │ └── NettyServer.java
│ │ │ │ └── util/
│ │ │ │ ├── NettyConfig.java
│ │ │ │ ├── NettyEventLoopUtil.java
│ │ │ │ ├── ProtostuffUtil.java
│ │ │ │ └── RemotingUtil.java
│ │ │ └── service/
│ │ │ ├── RefreshService.java
│ │ │ ├── RepositoryService.java
│ │ │ └── impl/
│ │ │ ├── JdbcRepositoryServiceImpl.java
│ │ │ └── RefreshServiceImpl.java
│ │ └── resources/
│ │ └── application.yml
│ ├── custom-config-service/
│ │ ├── README.md
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xiao/
│ │ │ └── custom/
│ │ │ └── config/
│ │ │ └── service/
│ │ │ ├── ConfigServiceApplication.java
│ │ │ ├── api/
│ │ │ │ ├── ApplicationApi.java
│ │ │ │ ├── AuthApi.java
│ │ │ │ ├── ClientHostApi.java
│ │ │ │ ├── ConfigItemApi.java
│ │ │ │ ├── ConfigItemGroupApi.java
│ │ │ │ ├── RegionApi.java
│ │ │ │ └── ServerHostConfigApi.java
│ │ │ ├── feign/
│ │ │ │ └── RefreshFeign.java
│ │ │ └── service/
│ │ │ ├── ApplicationItemGroupRelationService.java
│ │ │ ├── ApplicationService.java
│ │ │ ├── AuthService.java
│ │ │ ├── ClientHostService.java
│ │ │ ├── ConfigItemGroupRelationService.java
│ │ │ ├── ConfigItemGroupService.java
│ │ │ ├── ConfigItemService.java
│ │ │ ├── RegionService.java
│ │ │ ├── ServerHostConfigService.java
│ │ │ └── impl/
│ │ │ ├── ApplicationItemGroupRelationServiceImpl.java
│ │ │ ├── ApplicationServiceImpl.java
│ │ │ ├── AuthServiceImpl.java
│ │ │ ├── ClientHostServiceImpl.java
│ │ │ ├── ConfigItemGroupRelationServiceImpl.java
│ │ │ ├── ConfigItemGroupServiceImpl.java
│ │ │ ├── ConfigItemServiceImpl.java
│ │ │ ├── RegionServiceImpl.java
│ │ │ └── ServerHostConfigServiceImpl.java
│ │ └── resources/
│ │ ├── application.yml
│ │ └── bootstrap.yml
│ ├── custom-config-simple/
│ │ ├── README.md
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xiao/
│ │ │ └── custom/
│ │ │ └── config/
│ │ │ └── simple/
│ │ │ ├── ConfigClientApplication.java
│ │ │ ├── datasource/
│ │ │ │ └── DataSourceConfigure.java
│ │ │ └── demo/
│ │ │ └── ControllerDemo.java
│ │ └── resources/
│ │ ├── bootstrap.yml
│ │ └── logback-spring.xml
│ ├── custom-config-web/
│ │ ├── README.md
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xiao/
│ │ │ └── custom/
│ │ │ └── config/
│ │ │ └── web/
│ │ │ ├── ConfigCenterWebApplication.java
│ │ │ ├── auth/
│ │ │ │ ├── AuthContants.java
│ │ │ │ ├── config/
│ │ │ │ │ ├── HttpSessionConfig.java
│ │ │ │ │ ├── JwtAuthenticationEntryPoint.java
│ │ │ │ │ ├── JwtAuthenticationTokenFilter.java
│ │ │ │ │ ├── RestAccessDeniedHandler.java
│ │ │ │ │ └── WebSecurityConfig.java
│ │ │ │ ├── controller/
│ │ │ │ │ └── AuthController.java
│ │ │ │ ├── entity/
│ │ │ │ │ ├── ResponseUserToken.java
│ │ │ │ │ ├── User.java
│ │ │ │ │ └── UserDetail.java
│ │ │ │ ├── exception/
│ │ │ │ │ ├── CustomException.java
│ │ │ │ │ └── DefaultExceptionHandler.java
│ │ │ │ ├── service/
│ │ │ │ │ ├── AuthUserService.java
│ │ │ │ │ └── impl/
│ │ │ │ │ ├── AuthUserServiceImpl.java
│ │ │ │ │ └── ConfigUserDetailsServiceImpl.java
│ │ │ │ └── util/
│ │ │ │ ├── JwtUtils.java
│ │ │ │ ├── PageResult.java
│ │ │ │ ├── ResultCode.java
│ │ │ │ └── ResultJson.java
│ │ │ ├── commo/
│ │ │ │ └── Constants.java
│ │ │ ├── config/
│ │ │ │ └── AppControllerAdvice.java
│ │ │ ├── controller/
│ │ │ │ ├── ClientInfoController.java
│ │ │ │ ├── ConfigGroupController.java
│ │ │ │ ├── ConfigItemController.java
│ │ │ │ ├── IndexController.java
│ │ │ │ ├── RegionController.java
│ │ │ │ ├── ServerHostConfigController.java
│ │ │ │ └── app/
│ │ │ │ ├── AppManagerController.java
│ │ │ │ └── vo/
│ │ │ │ ├── ApplicationVo.java
│ │ │ │ └── RegionVo.java
│ │ │ ├── dto/
│ │ │ │ └── ServerHostConfigDto.java
│ │ │ ├── exception/
│ │ │ │ └── ExceptionEnum.java
│ │ │ └── feign/
│ │ │ ├── app/
│ │ │ │ └── ApplicationFeign.java
│ │ │ ├── auth/
│ │ │ │ └── AuthFeign.java
│ │ │ ├── client/
│ │ │ │ └── ClientInfoFeign.java
│ │ │ ├── config/
│ │ │ │ ├── ConfigGroupFeign.java
│ │ │ │ └── ConfigItemFeign.java
│ │ │ ├── region/
│ │ │ │ └── RegionFeign.java
│ │ │ └── server/
│ │ │ └── ServerHostConfigFeign.java
│ │ └── resources/
│ │ ├── application.properties
│ │ ├── application.yml
│ │ ├── bootstrap.yml
│ │ └── static/
│ │ ├── mock/
│ │ │ ├── index.js
│ │ │ └── mock.js
│ │ ├── pages/
│ │ │ ├── app/
│ │ │ │ ├── add.html
│ │ │ │ ├── add.js
│ │ │ │ ├── app.html
│ │ │ │ ├── app.js
│ │ │ │ ├── configgroup.html
│ │ │ │ ├── configgroup.js
│ │ │ │ ├── detail.html
│ │ │ │ ├── detail.js
│ │ │ │ ├── refconfiggroup.html
│ │ │ │ └── refconfiggroup.js
│ │ │ ├── client/
│ │ │ │ ├── client.html
│ │ │ │ └── client.js
│ │ │ ├── config/
│ │ │ │ ├── configitem.html
│ │ │ │ └── configitem.js
│ │ │ ├── configgroup/
│ │ │ │ ├── configgroup.html
│ │ │ │ ├── configgroup.js
│ │ │ │ ├── detail.html
│ │ │ │ ├── detail.js
│ │ │ │ ├── refdetail.html
│ │ │ │ └── refdetail.js
│ │ │ ├── error/
│ │ │ │ ├── 401.html
│ │ │ │ ├── 404.html
│ │ │ │ └── 500.html
│ │ │ ├── home/
│ │ │ │ ├── home.html
│ │ │ │ └── home.js
│ │ │ ├── index/
│ │ │ │ ├── compents.js
│ │ │ │ ├── index.css
│ │ │ │ ├── index.html
│ │ │ │ └── index.js
│ │ │ ├── login/
│ │ │ │ └── login.html
│ │ │ ├── region/
│ │ │ │ ├── region.html
│ │ │ │ └── region.js
│ │ │ ├── server/
│ │ │ │ ├── server.js
│ │ │ │ └── serverlist.html
│ │ │ ├── support/
│ │ │ │ ├── code_check.html
│ │ │ │ └── support.html
│ │ │ └── template/
│ │ │ ├── detail.html
│ │ │ ├── detail.js
│ │ │ ├── template.html
│ │ │ └── template.js
│ │ └── plugin/
│ │ ├── common/
│ │ │ ├── common.js
│ │ │ └── env.js
│ │ ├── element/
│ │ │ ├── js/
│ │ │ │ └── index.js
│ │ │ └── styles/
│ │ │ ├── fonts/
│ │ │ │ ├── iconfont.css
│ │ │ │ └── iconfont.js
│ │ │ └── index.css
│ │ ├── iview/
│ │ │ ├── js/
│ │ │ │ └── iview-editor.js
│ │ │ └── styles/
│ │ │ ├── iview-editor.css
│ │ │ └── iview.css
│ │ ├── jquery/
│ │ │ ├── 3.3.1/
│ │ │ │ └── jquery.js
│ │ │ └── jquery.history.js
│ │ └── mock/
│ │ └── mock-min.js
│ ├── custom-starter-config/
│ │ ├── README.md
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ └── resources/
│ │ └── META-INF/
│ │ └── spring.provides
│ ├── doc/
│ │ └── configMysql.sql
│ └── pom.xml
├── SpringCloud-Custom-RestTemplate-Stater/
│ ├── Readme.md
│ ├── SpringCloud-RestTemplate-Stater/
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xiao/
│ │ │ └── springcloud/
│ │ │ └── rest/
│ │ │ └── stater/
│ │ │ └── autoconfig/
│ │ │ ├── common/
│ │ │ │ ├── dto/
│ │ │ │ │ └── Request.java
│ │ │ │ ├── interceptor/
│ │ │ │ │ └── RestInterceptor.java
│ │ │ │ ├── log/
│ │ │ │ │ ├── annotation/
│ │ │ │ │ │ ├── RequestLog.java
│ │ │ │ │ │ └── RequestLogAspect.java
│ │ │ │ │ ├── dto/
│ │ │ │ │ │ └── HttpRequestLog.java
│ │ │ │ │ └── service/
│ │ │ │ │ └── HttpRequestLogService.java
│ │ │ │ ├── service/
│ │ │ │ │ ├── HttpClientService.java
│ │ │ │ │ └── impl/
│ │ │ │ │ └── HttpClientServiceImpl.java
│ │ │ │ └── util/
│ │ │ │ └── ThreadLocalUtil.java
│ │ │ └── config/
│ │ │ ├── RestTemplateConfiguration.java
│ │ │ └── properties/
│ │ │ ├── HttpPoolProperties.java
│ │ │ └── OkHttpProperties.java
│ │ └── resources/
│ │ └── META-INF/
│ │ └── spring.factories
│ ├── SpringCloud-RestTemplate-Stater-Example/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── xiao/
│ │ │ │ └── springcloud/
│ │ │ │ └── rest/
│ │ │ │ ├── RestTemplateStaterApp.java
│ │ │ │ └── log/
│ │ │ │ └── impl/
│ │ │ │ └── HttpLogServiceiImpl.java
│ │ │ └── resources/
│ │ │ ├── application.yml
│ │ │ └── logback-spring.xml
│ │ └── test/
│ │ └── java/
│ │ └── com/
│ │ └── xiao/
│ │ └── springcloud/
│ │ └── rest/
│ │ ├── RestTemplateStarterAppTest.java
│ │ └── httpclient/
│ │ └── HttpClientTest.java
│ └── pom.xml
├── SpringCloud-Demo-Doc/
│ ├── ES/
│ │ ├── ElasticSearch后台操作(使用)手册1.0.docx
│ │ ├── ElasticSearch安装手册1.0.docx
│ │ ├── Elasticsearch IK分词器扩展说明.docx
│ │ └── es索引创建1.1.jmx
│ ├── docker/
│ │ ├── batch_del.sh
│ │ ├── docker-build.sh
│ │ ├── docker-monitor.json
│ │ ├── docker-monitor.yml
│ │ └── docker-swarm-springcloud.md
│ ├── gitlab/
│ │ ├── Gitlab使用手册.docx
│ │ ├── Gitlab安装手册.docx
│ │ ├── gitlab-ci.yml
│ │ ├── sonar_analyze.sh
│ │ └── sonar_preview.sh
│ ├── k8s/
│ │ ├── CronJob.yaml
│ │ ├── DaemonSet.yaml
│ │ ├── Job.yaml
│ │ ├── K8S Linux Centos 7安装.docx
│ │ ├── Pod.yaml
│ │ ├── ReplicaSet.yaml
│ │ ├── ReplicationController.yaml
│ │ ├── k8s-master-bootstrap.sh
│ │ └── k8s-node-bootstrap.sh
│ ├── kafka+elk/
│ │ ├── ELK安装文档.docx
│ │ ├── ELK日志logstash解析JSON嵌套.md
│ │ ├── ElasticSearch安装手册1.0.docx
│ │ ├── Kafka安装指导手册.docx
│ │ ├── elk+springboot+kafka日志跟踪配置.docx
│ │ └── 使用logback-kafka导致服务之间调用多1分钟之坑.md
│ ├── linux/
│ │ └── Linux-netstat命令.md
│ ├── pom.xml
│ ├── rxjava/
│ │ └── RxJavaHelloWorld.MD
│ ├── spring-cloud/
│ │ ├── README.md
│ │ ├── SpringCloud-FQA.md
│ │ ├── 源码解析专栏/
│ │ │ ├── Spring Cloud Config Client加载配置源码分析.md
│ │ │ ├── Spring Cloud Config 是如何实现热更新的
│ │ │ ├── Spring Cloud Netflix Eureka多网卡环境下Eureka服务注册IP选择问题.md
│ │ │ ├── Spring Cloud Netflix Eureka源码导读与原理分析.md
│ │ │ ├── eureka分区的深入讲解.md
│ │ │ └── spring-cloud-feign源码深度解析.md
│ │ └── 链路跟踪/
│ │ └── spring-cloud+skywalking链路跟踪.docx
│ ├── sql/
│ │ └── 行政区域带经纬度.sql
│ ├── 其他个人总结/
│ │ ├── Jmeter分布式压测.docx
│ │ ├── openresty.conf
│ │ ├── stock.lua
│ │ ├── 参数校验注解使用指南.docx
│ │ ├── 微信小程序MQTT协议通信.docx
│ │ └── 聊一聊微服务.pptx
│ └── 持续集成/
│ ├── Gitlab+P3C-PMD(Aliyun)标准化你团队的代码.docx
│ ├── Gitlab安装手册.docx
│ ├── Gitlab的Hooks(钩子)做Push代码检测.docx
│ ├── auto_deploy.sh
│ ├── bootstrap.sh
│ ├── example-gitlab-ci.yml
│ ├── gitlab-hooks/
│ │ ├── checkStyle模板
│ │ ├── pre-receive
│ │ ├── pre-receive(checkstyle版本,使用请更名为pre-receive)
│ │ └── pre-receive(p3c-pmd版,使用请更名为pre-receive)
│ ├── monitor-jstat.sh
│ ├── remote_deploy.sh
│ ├── 代码质量监控体系方案.pptx
│ ├── 代码质量监控平台.docx
│ ├── 代码质量监控平台安装使用手册.docx
│ ├── 持续集成之Jenkins安装和使用指导手册.docx
│ └── 持续集成之Sonarqube安装和使用指导手册.docx
├── SpringCloud-Docker/
│ ├── Dockerfile
│ ├── README.md
│ ├── docker-build.sh
│ ├── pom.xml
│ ├── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xiao/
│ │ │ └── springboot/
│ │ │ └── docker/
│ │ │ ├── OmniZipkinServerApplication.java
│ │ │ └── controller/
│ │ │ └── DockerDemo.java
│ │ └── resources/
│ │ └── application.yml
│ └── zipkin-server-docker.sh
├── SpringCloud-Eureka/
│ ├── pom.xml
│ ├── readme.txt
│ ├── sql/
│ │ └── config.sql
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── xiao/
│ │ └── skywalking/
│ │ └── center/
│ │ └── Application.java
│ └── resources/
│ ├── application.yml
│ └── bootstrap.yml
├── SpringCloud-Gateway/
│ ├── README.txt
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── xiao/
│ │ └── skywalking/
│ │ └── gateway/
│ │ └── zuul/
│ │ └── GatewayApplication.java
│ └── resources/
│ └── application.yml
├── SpringCloud-Hystrix-Demo/
│ ├── README.MD
│ ├── SpringCloud-Hystrix-Consumer/
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xiao/
│ │ │ └── hystrix/
│ │ │ └── demo/
│ │ │ └── consumer/
│ │ │ ├── ConsumerApplication.java
│ │ │ ├── api/
│ │ │ │ └── ConsumerRestService.java
│ │ │ ├── common/
│ │ │ │ └── CacheConstants.java
│ │ │ ├── config/
│ │ │ │ ├── CaffeineCacheConfiguration.java
│ │ │ │ ├── FeignConfiguration.java
│ │ │ │ └── HystrixCacheConfiguration.java
│ │ │ ├── dynamic/
│ │ │ │ ├── DynamicConfigSource.java
│ │ │ │ └── InitHystrixConfiguration.java
│ │ │ ├── feign/
│ │ │ │ ├── ProducerFeign.java
│ │ │ │ └── impl/
│ │ │ │ ├── ProducerFeignFactory.java
│ │ │ │ └── ProducerFeignFallBack.java
│ │ │ └── filter/
│ │ │ └── HystrixCacheFilter.java
│ │ └── resources/
│ │ ├── application.properties
│ │ ├── application.yml
│ │ └── bootstrap.yml
│ ├── SpringCloud-Hystrix-Eureka/
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xiao/
│ │ │ └── hystrix/
│ │ │ └── demo/
│ │ │ └── eureka/
│ │ │ └── EurekaApplication.java
│ │ └── resources/
│ │ └── bootstrap.yml
│ ├── SpringCloud-Hystrix-Producer/
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xiao/
│ │ │ └── hystrix/
│ │ │ └── demo/
│ │ │ └── producer/
│ │ │ ├── ProducerApplication.java
│ │ │ ├── api/
│ │ │ │ └── ProducerRestService.java
│ │ │ └── service/
│ │ │ ├── ProducerService.java
│ │ │ └── impl/
│ │ │ └── ProducerServiceImpl.java
│ │ └── resources/
│ │ ├── application.properties
│ │ ├── application.yml
│ │ └── bootstrap.yml
│ └── pom.xml
├── SpringCloud-Kafka-Elk/
│ ├── README.md
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── xiao/
│ │ └── springcloud/
│ │ └── demo/
│ │ └── kafka/
│ │ └── elk/
│ │ ├── ElkKafkaApplication.java
│ │ └── kafka/
│ │ ├── KafkaProducerTest.java
│ │ └── LogCompent.java
│ └── resources/
│ ├── application.yml
│ ├── bootstrap.yml
│ └── logback-spring.xml
├── SpringCloud-MQTT/
│ ├── Readme.MD
│ ├── doc/
│ │ ├── mqtt.conf
│ │ ├── nginx.conf
│ │ └── wss.conf
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── skywalking/
│ │ └── mqtt/
│ │ ├── ClientCallback.java
│ │ ├── MqttServiceApp.java
│ │ ├── MqttTestClient.java
│ │ ├── MvcController.java
│ │ ├── PurTrustManager.java
│ │ └── TopicProducerTest.java
│ ├── resources/
│ │ └── application.yml
│ └── webapp/
│ └── WEB-INF/
│ ├── js/
│ │ ├── crypto-js.js
│ │ ├── layer/
│ │ │ ├── layer.js
│ │ │ ├── mobile/
│ │ │ │ ├── layer.js
│ │ │ │ └── need/
│ │ │ │ └── layer.css
│ │ │ └── skin/
│ │ │ └── default/
│ │ │ └── layer.css
│ │ └── mqttws31-min.js
│ └── jsp/
│ └── mqtt_client.jsp
├── SpringCloud-Mybatis/
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── xiao/
│ │ └── springcloud/
│ │ └── mybatis/
│ │ └── generator/
│ │ └── plugin/
│ │ └── LombokPlugin.java
│ └── resources/
│ └── generatorConfig.xml
├── SpringCloud-Provider/
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── xiao/
│ │ └── skywalking/
│ │ └── provider/
│ │ ├── ProviderApp.java
│ │ ├── controller/
│ │ │ └── SkywalkingController.java
│ │ └── local/
│ │ └── cache/
│ │ └── SpringGuavaCacheConfig.java
│ └── resources/
│ └── application.yml
├── SpringCloud-Quartz-JobService/
│ ├── README.md
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── xiao/
│ │ └── springcloud/
│ │ └── job/
│ │ ├── JobServiceApplication.java
│ │ ├── config/
│ │ │ ├── JobConfig.java
│ │ │ └── TaskSchedulerFactory.java
│ │ ├── entity/
│ │ │ └── TaskConfigDocument.java
│ │ ├── job/
│ │ │ └── ServiceTaskExecuteJob.java
│ │ ├── quartz/
│ │ │ └── JobManager.java
│ │ └── util/
│ │ └── CronExpUtil.java
│ └── resources/
│ ├── application.yml
│ └── bootstrap.yml
├── SpringCloud-Redisson/
│ ├── README.md
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xiao/
│ │ │ └── spring/
│ │ │ └── cloud/
│ │ │ └── redisson/
│ │ │ ├── SpringDataRedissonApplication.java
│ │ │ └── config/
│ │ │ └── SpringDataRedissonConfig.java
│ │ └── resources/
│ │ ├── application.yml
│ │ └── redisson.yml
│ └── test/
│ └── java/
│ └── com/
│ └── xiao/
│ └── spring/
│ └── cloud/
│ └── redisson/
│ └── SpringDataRedissonApplicationTest.java
├── SpringCloud-SearchService/
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xiao/
│ │ │ └── spring/
│ │ │ └── cloud/
│ │ │ └── search/
│ │ │ ├── SearchApplication.java
│ │ │ ├── dto/
│ │ │ │ ├── ElasticSearchDoc.java
│ │ │ │ ├── PaginationDo.java
│ │ │ │ ├── SearchBrandDo.java
│ │ │ │ ├── SearchCategoryDo.java
│ │ │ │ ├── SearchCommoPropOptionDto.java
│ │ │ │ ├── SearchCommodityPropertyDo.java
│ │ │ │ ├── SearchCommodityResultDo.java
│ │ │ │ ├── SearchLogDo.java
│ │ │ │ ├── SearchMenusDo.java
│ │ │ │ ├── SearchRequestDo.java
│ │ │ │ ├── SearchResultDo.java
│ │ │ │ ├── SearchShopWeightDto.java
│ │ │ │ └── ShopPriceRangeDto.java
│ │ │ ├── es/
│ │ │ │ ├── client/
│ │ │ │ │ └── ElasticSearchClient.java
│ │ │ │ ├── common/
│ │ │ │ │ ├── AnalyzeType.java
│ │ │ │ │ ├── ESConstants.java
│ │ │ │ │ ├── OrderField.java
│ │ │ │ │ └── SearchException.java
│ │ │ │ ├── log/
│ │ │ │ │ ├── ISearchLogService.java
│ │ │ │ │ ├── impl/
│ │ │ │ │ │ └── SearchLogServiceImpl.java
│ │ │ │ │ └── thread/
│ │ │ │ │ ├── BatchSaveSearchLogThread.java
│ │ │ │ │ ├── SearchLogThread.java
│ │ │ │ │ ├── SearchLogThreadPool.java
│ │ │ │ │ └── SearchThreadFactory.java
│ │ │ │ └── service/
│ │ │ │ ├── SearchManagerEsImpl.java
│ │ │ │ └── SearchServiceEsImpl.java
│ │ │ ├── rest/
│ │ │ │ ├── SearchManagerRestService.java
│ │ │ │ └── SearchRestService.java
│ │ │ └── service/
│ │ │ ├── SearchManangerService.java
│ │ │ └── SearchService.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── bootstrap.yml
│ │ └── logback-spring.xml
│ └── test/
│ └── java/
│ └── com/
│ └── xiao/
│ └── springcloud/
│ └── test/
│ ├── SearchApplicationTest.java
│ ├── SearchManagerTest.java
│ ├── SearchTest.java
│ └── cache/
│ └── RedisCacheTest.java
├── SpringCloud-Sentinel/
│ ├── Readme.MD
│ ├── SpringCloud-Sentinel-Consumer/
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xiao/
│ │ │ └── springcloud/
│ │ │ └── sentinel/
│ │ │ └── consumer/
│ │ │ ├── ConsumerApplication.java
│ │ │ ├── api/
│ │ │ │ └── ConsumerRestService.java
│ │ │ ├── config/
│ │ │ │ └── FeignConfiguration.java
│ │ │ └── feign/
│ │ │ ├── ProducerFeign.java
│ │ │ └── fallback/
│ │ │ └── ProducerFeignFallBack.java
│ │ └── resources/
│ │ ├── application.properties
│ │ ├── application.yml
│ │ └── bootstrap.yml
│ ├── SpringCloud-Sentinel-Eureka/
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── com.xiao.springcloud.sentinel.eureka/
│ │ │ └── EurekaApplication.java
│ │ └── resources/
│ │ └── bootstrap.yml
│ ├── SpringCloud-Sentinel-Producer/
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xiao/
│ │ │ └── springcloud/
│ │ │ └── sentinel/
│ │ │ └── producer/
│ │ │ ├── ProducerApplication.java
│ │ │ ├── api/
│ │ │ │ └── ProducerRestService.java
│ │ │ └── service/
│ │ │ ├── ProducerService.java
│ │ │ └── impl/
│ │ │ └── ProducerServiceImpl.java
│ │ └── resources/
│ │ ├── application.properties
│ │ ├── application.yml
│ │ └── bootstrap.yml
│ ├── dashbord/
│ │ └── readme.md
│ └── pom.xml
├── SpringCloud-Sharding-Sphere/
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── purcotton/
│ │ └── sharding/
│ │ └── sphere/
│ │ └── demo/
│ │ ├── SpringBootStarterExample.java
│ │ ├── entity/
│ │ │ ├── Order.java
│ │ │ └── OrderItem.java
│ │ ├── repository/
│ │ │ ├── CommonRepository.java
│ │ │ ├── OrderItemRepository.java
│ │ │ └── OrderRepository.java
│ │ └── service/
│ │ ├── BasisCommonService.java
│ │ ├── CommonService.java
│ │ └── impl/
│ │ └── SpringPojoServiceImpl.java
│ └── resources/
│ ├── META-INF/
│ │ ├── mappers/
│ │ │ ├── OrderItemMapper.xml
│ │ │ └── OrderMapper.xml
│ │ └── mybatis-config.xml
│ ├── application-sharding-databases.properties
│ ├── application.properties
│ └── logback.xml
├── SpringCloud-ZipkinServer/
│ ├── README.md
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── xiao/
│ │ └── springcloud/
│ │ └── zs/
│ │ └── ZipkinServerApplication.java
│ └── resources/
│ ├── application.properties
│ └── application.yml
├── SpringCloud-Zookeeper/
│ ├── pom.xml
│ └── readme.md
└── pom.xml
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
*.js linguist-language=Java
*.css linguist-language=Java
*.html linguist-language=Java
================================================
FILE: .gitignore
================================================
## .gitignore for Grails 1.2 and 1.3
# .gitignore for maven
target/
*.releaseBackup
# web application files
#/web-app/WEB-INF
# IDE support files
/.classpath
/.launch
/.project
/.settings
/*.launch
/*.tmproj
/ivy*
/eclipse
# default HSQL database files for production mode
/prodDb.*
# general HSQL database files
*Db.properties
*Db.script
# logs
/stacktrace.log
/test/reports
/logs
*.log
*.log.*
# project release file
/*.war
# plugin release file
/*.zip
/*.zip.sha1
# older plugin install locations
/plugins
/web-app/plugins
/web-app/WEB-INF/classes
# "temporary" build files
target/
out/
build/
# other
*.iws
#.gitignore for java
*.class
# Package Files #
*.jar
*.war
*.ear
## .gitignore for eclipse
*.pydevproject
.project
.metadata
bin/**
tmp/**
tmp/**/*
*.tmp
*.bak
*.swp
*~.nib
local.properties
.classpath
.settings/
.loadpath
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
## .gitignore for intellij
*.iml
*.ipr
*.iws
.idea/
## .gitignore for linux
.*
!.gitignore
!.gitattributes
!.editorconfig
!.eslintrc
!.travis.yml
*~
## .gitignore for windows
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
## .gitignore for mac os x
.DS_Store
.AppleDouble
.LSOverride
Icon
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
## hack for graddle wrapper
!wrapper/*.jar
!**/wrapper/*.jar
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at 84226733@qq.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: LICENSE.txt
================================================
MIT License
Copyright (c) 2021 Linlin xiao
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# spring-cloud-demo
## 不定期更新与记录在springcloud开发中所遇到的坑以及解决方法
## 说明:由于版本不兼容,很多核心、重点的技术难题都以单独的项目形式提交,并同时更新到csdn,如有需要请关注:https://blog.csdn.net/xiaoll880214?type=blog
初始化添加
1. spring-cloud skywalking demo
2. add mqtt suports
3. add 微信小程序 suports mqtt
4. kafka elk支持
20180809
1. 更新 代码结构
2. 新增git配置中心
3. 新增kafka elk demo配置以及文档
4. 文档结构整理
5. 分离注册中心和配置中心
7. 添加zipkin服务跟踪
20180905
1. 自定义注解实现aop日志
2. 自定义注解实现实体类参数校验
3. 添加mybatis自定生成映射实体类、mapper等
4. 添加全局异常处理
5. 添加fegin自定义数据解析
20180907
1. 添加注解,作为参数校验入口
20180910
1. 解决服务之间调用fegin+hystrix 熔断异常拦截处理
20180914
1. 服务调用之间的rest请求,参数为对象时需要添加@RequestBody注解
``eg:
saveRegionCity(@RequestBody RegionCityDto regionCityDto)``
2. 服务间调用接口的返回值,不能使接口返回,必须要使用实现类返回,fegin客户端获取不到数据返回Null
`eg:
public User getUser(@RequestBody UserQuery query);
User必须为实现类,不能为接口`
3. 添加fastjson解析,解决部分调用对象内包含对象传值为空问题
20180921
1. Doc 目录结构调整
2. 记录[elk+kafka+logback服务之间调用多1分钟时间之坑](https://github.com/Xlinlin/spring-cloud-demo/blob/master/SpringCloud-Demo-Doc/kafka%2Belk/使用logback-kafka导致服务之间调用多1分钟之坑.md)
20180927
1. [elk+logstash+logback解析嵌套json数据](https://github.com/Xlinlin/spring-cloud-demo/blob/master/SpringCloud-Demo-Doc/kafka%2Belk/ELK%E6%97%A5%E5%BF%97logstash%E8%A7%A3%E6%9E%90JSON%E5%B5%8C%E5%A5%97.md)
20180930
1. 添加guava+spring-cache本地缓存实现,[参考入口](https://blog.csdn.net/mafei6827/article/details/80868931)
2. 记录springcloud 1.x版本解决feignclient下requestmapping与springmvc的定义冲突问题,[参考入口](http://blog.didispace.com/spring-cloud-feignclient-problem/?utm_source=tuicool&utm_medium=referral)
20181009
1. 添加ES实现电商[搜索基础服务](https://github.com/Xlinlin/spring-cloud-demo/tree/master/SpringCloud-SearchService)
2. 添加Mockito实现api的junit测试
20181012
1. redisson yml配置加载,支持单机、集群、云托管、sentinel模式
2. 配置文件中添加配置文件即可开启redisson的配置:
`` redisson.fileName: redission-cluster(自定义)``
3. 提供缓存基本服务和分布式服务:
``> CacheService 提供缓存基础服务``
``> DistributedService 提供分布式**可重入公平/非公平锁**、**读写锁**、**闭锁**``
[代码实现参考](https://github.com/Xlinlin/spring-cloud-demo/tree/master/SpringCloud-Common/src/main/java/com/xiao/springcloud/demo/common/cache)
[junit测试参考](https://github.com/Xlinlin/spring-cloud-demo/tree/master/SpringCloud-SearchService/src/test/java/com/xiao/springcloud/test/cache)
20181016
1. redis缓存 redisson客户端添加批处理
20181018
1. [spring-cache+guava 添加本地缓存](https://github.com/Xlinlin/spring-cloud-demo/tree/master/SpringCloud-Common/src/main/java/com/xiao/springcloud/demo/common/cache)
20181022
1. 升级Springboot2.0 详情参考springboot2.0分之
2. 调整common包,可打成jar包
3. 添加启动shell脚本,参考common包script目录下.sh文件
20181027
1. bootstrap.sh 脚本参数简化
20181029
1. 优化bootstrap.sh脚本
2. 添加jenkins构建后自动部署脚本
3. 添加jenkins构建后远程自动部署脚本
[详情](https://github.com/Xlinlin/spring-cloud-demo/tree/master/SpringCloud-Common/script)
20181114
1. Sharding-sphere尝试
20181115
1. bootstrap.sh 脚本添加jvm参数配置,以及停止时旧日志文件的备份
2. 本地和远程自动部署时,不进行原服务包的删除,按时间戳进行备份原来的可执行包
20181127
1. springcloud-config 自定义mysql实现,[详情](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringCloud-Custom-ConfigCenter)
20181210
1. 工程结果整理
2. 添加springboot+quartz自定义实现 任务调度
20190119
1. 新增Redisson集成springdata,使用RedisTemplate,[详情](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringCloud-Redisson)
20190124
1. 新增RedisTemplate 使用pipeline批量操作redis数据
2. 添加常用工具类 AES加解密、MD5等
20190126
1. 改造zipkin链路跟踪实现:SpringCloud Sleuth Stream Zipkin Kafka Elasticsearch 实现简单链路跟踪。
[参考](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-ZipkinServer/README.md)
20190131
1. 自定义配置中心重构,[详情](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringCloud-Custom-ConfigCenter)
2. 新增多条件搜索测试,[详情](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-SearchService/src/test/java/com/xiao/springcloud/test/SearchManagerTest.java)
3. 更多多条件搜索的[参考资料](http://www.scienjus.com/elasticsearch-function-score-query/)
20190201
1. 新增[Docker + SpringBoot + Maven 构建发布到远程仓库 DEMO](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringCloud-Docker)
20190320
1. 新增 ES 权重查询 以及 聚合逻辑,[详情参考](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-SearchService/src/main/java/com/xiao/spring/cloud/search/es/service/SearchServiceEsImpl.java)
2. 新增阿里开源数据同步工具[Canal](https://github.com/alibaba/canal)的简单[Demo使用](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringCloud-Canal)
20190327
1. 将Canal+Disruptor整合到springboot中,提供一套完整的Canal异步框架,在DisruptorServiceImpl服务中实现自己的业务逻辑即可,[更多详见](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringCloud-Canal)
20190402
1. 自定义配置中心,引入Netty监测心跳[详情](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringCloud-Custom-ConfigCenter)
20190405
1. netty实现配置刷新[详情](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringCloud-Custom-ConfigCenter)
20190411
1. 添加maven+jenkins+docker+springboot 构建打包发布部署的jenkins shell脚本
20190402
1. 修复linux系统,客户端异常断开,服务端无感知问题,即在linux上使用kill或ctrl+c 中断服务,无法进入exceptionCaught方法导致无法感知应用下线问题。
更换为channelInactive方法来感知和下线客户端(netty)
20190504
1. [新增mqtt通过nginx代理配置](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringCloud-MQTT)
20190515
1. 新增spring session+ spring security + jwt简单鉴权,[参考入口](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringCloud-Custom-ConfigCenter/custom-config-web)
20190611
1. 记录 fork join demo[详情](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringCloud-Common/src/main/java/com/xiao/springcloud/demo/common/forkjoin)
20190621
1. Disruptor+spring event封装[详情以及使用说明](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringCloud-Common/src/main/java/com/xiao/springcloud/demo/common/disruptor)
20190624
1. [入手Zookeeper](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringCloud-Zookeeper)
20190702
1. 新增Canal启动 [ServerRunningMonitor部分源码注解记录](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-Canal/doc/ServerRunningMonitor%E6%BA%90%E7%A0%81%E6%B3%A8%E8%A7%A3.md),
2. 修复定时任务重新启动时加载启动状态且已过期的任务报错问题:[新增在添加任务是校验表达的合法性](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-Quartz-JobService/src/main/java/com/xiao/springcloud/job/util/CronExpUtil.java)
20190720
1. Springboot-Admin 2.0服务端+Springboot-Admin 1.5.6客户端[集成使用](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringBoot-Admin),掌控你的微服务。
20190801
1. [SpringBoot + SpringCloud + Feign + Sentinel 集成实现接口限流监控](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringCloud-Sentinel)
20190813
1. [SpringCloud + Feign + Hystrix 熔断、线程池的一些坑记录](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringCloud-Hystrix-Demo)
20190909
1. [定制SpringBoot Starter 之Elasticsearch Rest High Level Client Starter](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringBoot-Custom-Elasticsearch-Starter)
20190910
1. [启动脚本添加GC参数和skywalking探针](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-Common/script/bootstrap.sh)
20190929
1. 新增图片比较工具类,比较两张图片是否相同:[DHashUtil](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-Common/src/main/java/com/xiao/springcloud/demo/common/util/image/ImageDHashUtil.java)&[PHashUtil](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-Common/src/main/java/com/xiao/springcloud/demo/common/util/image/ImagePHashUtil.java)
20191019
1. redisson分布式锁库存使用,下单、取消单、出库单之jmeter ifelse程序并发测试--**预告**
20191022
1. [Springboot官方文档-配置新-Tomcat优化](https://docs.spring.io/spring-boot/docs/2.2.1.BUILD-SNAPSHOT/reference/html/appendix-application-properties.html#server-properties)
20191023
1. [Jmeter+Springboot+Redisson分布式锁并发订单操作(下单、取消单、完成单、加库存)](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringBoot-Stock-Demo)
20191105
1. Api对外接口统一返回值,如:{"code":200,"erroMsg":"",data:{}},[参考实现](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-Consumer/src/main/java/com/xiao/skywalking/consumer/common/advice/UnifiedReturnAdvice.java)
20191119
1. Springboot 的Rest请求返回的Response中的HTTP响应行只有:HTTP/1.1 200 {OK},无OK返回导致老的http客户端无法识别,是因为springboot 1.4以上版本将tomcat升级到了8.5.x以后的版本,如果需要支持,需要设置tomcat的版本低于8.5的版本,设置:
```$xslt
8.0.29
```
[参考资料1](http://www.mamicode.com/info-detail-2280850.html);
[参考资料2](https://stackoverflow.com/questions/49610522/spring-boot-return-http-1-1-200-not-http-1-1-200-ok);
20191125
1. [Gitlab+P3C-PMD(ali)标准化你团队的代码.doc](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-Demo-Doc/%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90/Gitlab%2BP3C-PMD(Aliyun)%E6%A0%87%E5%87%86%E5%8C%96%E4%BD%A0%E5%9B%A2%E9%98%9F%E7%9A%84%E4%BB%A3%E7%A0%81.docx)
20191206
1. [SpringCloud RestTemplate 封装stater](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringCloud-Custom-RestTemplate-Stater)支持使用http连接池、okhttp等
20191208
1. [Linux之netstat命令](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-Demo-Doc/linux/Linux-netstat%E5%91%BD%E4%BB%A4.md)-服务自动化发布时以此结果为依据停止服务
20191213
1. [RxJava Hello World](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-Demo-Doc/rxjava/RxJavaHelloWorld.MD),要入手一定要敲代码,敲起来!
20200104
1. Shell脚本+jstat+curl+crontab 监控JVM发短信[脚本](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-Demo-Doc/%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90/monitor-jstat.sh)
20200111
1. [基于Springboot1.5.9+SpringCloud+Zipkin+ELK链路跟踪实现](https://github.com/Xlinlin/spingcloud-zipkin-elk-demo)
20200114
1. String字符GBK和UTF编码格式长度判断以及截取
20200115
1. 经常用到对list进行分页批处理,写了[工具类](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-Common/src/main/java/com/xiao/springcloud/demo/common/util/ListPageUtil.java)一劳永逸
20200117
1. 封装RestTemplate,支持okhttp,httpool,支持同步和异步请求,[ReadMe.MD](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringBoot-Custom-Rest-Starter)
20200224
1. Springboot web应用签名包括工具类,[传送链接](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringCloud-Common/src/main/java/com/xiao/springcloud/demo/common/sign)
20200229
1. K8S Cluster[安装文档](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-Demo-Doc/k8s/K8S%20Linux%20%20Centos%207%E5%AE%89%E8%A3%85.docx)
20200422
1. Sonarqube+Gitlab-CICD构建[代码质量管理平台](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-Demo-Doc/%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90)
20200426
1. 完善代码质量[监控体系](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-Demo-Doc/%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90/%E4%BB%A3%E7%A0%81%E8%B4%A8%E9%87%8F%E7%9B%91%E6%8E%A7%E4%BD%93%E7%B3%BB%E6%96%B9%E6%A1%88.pptx)
20200508
1. SpringCloud Gateway + nacos实现灰度, + ribbon实现全链路版本请求,[详情](https://github.com/Xlinlin/SpringCloud-Gateway-Canary)
20200527
1. [添加docker-build脚本,执行脚本构建镜像并推送到私服仓库](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-Docker/docker-build.sh)
20200528
1. swagger2 创建[api文档](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-Demo-Doc/swagger%E7%94%9F%E6%88%90html%E6%96%87%E4%BB%B6.pdf)整理
2. [docker-swarm集群监控文档](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringCloud-Demo-Doc/docker)整理
20200529
1. 补充swarm集群部署springcloud项目,[详细文档](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-Demo-Doc/docker/docker-swarm-springcloud.md)
20200814
1. 新gitlab使用代码规则校验说明,[详情参考](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringCloud-Demo-Doc/%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90/gitlab-hooks/pre-receive)
20201002
1. 说明:本仓库的代码以springboot 1.0版本,工作中已使用2.0以上版本,很多新的分享会单独创建git项目
2. 基于nacos或eureka实现 服务级别的灰度,支持网关、feign自由插件[详情参考](https://github.com/Xlinlin/canary)
20211012
1. [Springboot Admin(SBA) + Nacos + Arthas 搭建你的在线性能分析和问题定位工具-服务端改造篇](https://blog.csdn.net/xiaoll880214/article/details/120191476?spm=1001.2014.3001.5501)
2. [Springboot Admin(SBA) + Nacos + Arthas 搭建你的在线性能分析和问题定位工具-客户端改造篇](https://blog.csdn.net/xiaoll880214/article/details/120295070?spm=1001.2014.3001.5501)
================================================
FILE: SpringBoot-Admin/pom.xml
================================================
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.0.RELEASE
Springboot-Admin 2.0版本和SpringBoot 1.5.X版本结合使用
SpringBoot-Admin
UTF-8
UTF-8
1.8
1.8
1.8
2.1.0
de.codecentric
spring-boot-admin-starter-server
${spring-boot-admin.version}
de.codecentric
spring-boot-admin-server-ui
${spring-boot-admin.version}
org.jolokia
jolokia-core
org.projectlombok
lombok
org.springframework.boot
spring-boot-starter-web
org.slf4j
slf4j-log4j12
SpringBoot-Admin
${project.build.directory}/classes
src/main/resources
true
**/*.xml
**/*.yml
**/*.properties
META-INF/**
org.springframework.boot
spring-boot-maven-plugin
build-info
================================================
FILE: SpringBoot-Admin/readme.md
================================================
Springboot-Admin 2.0服务端+Springboot-Admin 1.5.6客户端集成使用监控服务
原参考的[github地址](https://github.com/p555iii/spring-boot-admin1.5to2.0)
服务端配置参考[地址](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringBoot-Admin)
客户端配置参考[地址](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringCloud-Provider)
================================================
FILE: SpringBoot-Admin/src/main/java/com/xiao/spring/boot/admin/SpringBootAdminApplication.java
================================================
package com.xiao.spring.boot.admin;
import de.codecentric.boot.admin.server.config.EnableAdminServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
/**
* [简要描述]: springboot-admin 集成eureka 监控服务
* [详细描述]:
*
* @author llxiao
* @version 1.0, 2019/7/30 09:47
* @since JDK 1.8
*/
@Configuration
@SpringBootApplication
@EnableAdminServer
public class SpringBootAdminApplication
{
public static void main(String[] args)
{
SpringApplication.run(SpringBootAdminApplication.class, args);
}
}
================================================
FILE: SpringBoot-Admin/src/main/resources/application.yml
================================================
info:
groupId: @project.groupId@
artifactId: @project.artifactId@
version: @project.version@
###################
# 邮件通知配置
##################
#spring:
#mail:
#host: smtphm.qiye.163.com
#username: # 用户名
#password: # 密码
#boot:
#admin:
#notify:
#mail:
#from: # 发件人
#to: # 收件人
#enabled: true
================================================
FILE: SpringBoot-Admin/src/main/resources/bootstrap.yml
================================================
server:
port: 8080
spring:
application:
name: omni-service-admin
security:
user:
name: "admin"
password: "admin@123"
#eureka:
#client:
#fetch-registry: true
#service-url:
#defaultZone: http://192.168.206.201:8888/eureka
#register-with-eureka: true
#registry-fetch-interval-seconds: 30
#instance:
#hostname: localhost
#prefer-ip-address: true
#lease-renewal-interval-in-seconds: 10
#lease-expiration-duration-in-seconds: 30
#health-check-url-path: /actuator/health
#metadata-map:
#user.name: ${spring.security.user.name}
#user.password: ${spring.security.user.password}
#info:
#name: @project.name@
#groupId: @project.groupId@
#artifactId: @project.artifactId@
#version: @project.version@
#management:
#endpoints:
#web:
#exposure:
#include: "*"
#base-path: /
#endpoint:
#health:
#show-details: ALWAYS
================================================
FILE: SpringBoot-Custom-Elasticsearch-Starter/Custom-Elasticsearch-Starter/pom.xml
================================================
SpringBoot-Custom-Elasticsearch-Starter
com.xiao.skywalking.demo
0.0.1-SNAPSHOT
4.0.0
Custom-Elasticsearch-Starter
Custom ElasticSearch High Level Rest Client Stater AutoConfigure
com.xiao.skywalking.demo
Custom-Elasticsearch-Starter-Autoconfig
${project.version}
================================================
FILE: SpringBoot-Custom-Elasticsearch-Starter/Custom-Elasticsearch-Starter-Autoconfig/pom.xml
================================================
SpringBoot-Custom-Elasticsearch-Starter
com.xiao.skywalking.demo
0.0.1-SNAPSHOT
4.0.0
Custom-Elasticsearch-Starter-Autoconfig
Custom ElasticSearch High Level Rest Client Stater AutoConfigure
6.8.17
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-autoconfigure
org.springframework.boot
spring-boot-configuration-processor
true
org.projectlombok
lombok
1.16.18
true
org.elasticsearch
elasticsearch
${elasticsearch.version}
org.elasticsearch.client
elasticsearch-rest-client
${elasticsearch.version}
org.elasticsearch.client
elasticsearch-rest-high-level-client
${elasticsearch.version}
org.apache.commons
commons-lang3
3.4
com.alibaba
fastjson
1.2.83
================================================
FILE: SpringBoot-Custom-Elasticsearch-Starter/Custom-Elasticsearch-Starter-Autoconfig/src/main/java/com/xiao/custom/elasticsearch/start/autoconfig/ElasticsearchAutoConfiguration.java
================================================
package com.xiao.custom.elasticsearch.start.autoconfig;
import com.alibaba.fastjson.JSONObject;
import com.xiao.custom.elasticsearch.start.autoconfig.properties.ElasticsearchProperties;
import com.xiao.custom.elasticsearch.start.autoconfig.properties.HostInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.CollectionUtils;
import java.util.List;
/**
* [简要描述]: 自动装配
* [详细描述]:
* EnableAutoConfiguration 利用SpringFactoriesLoader机制加载所有的AutoConfiguration类 META-INF/spring.factories
*
* @author llxiao
* @version 1.0, 2019/8/28 10:28
* @since JDK 1.8
*/
@Configuration
@EnableConfigurationProperties(ElasticsearchProperties.class)
@Slf4j
public class ElasticsearchAutoConfiguration implements DisposableBean
{
private RestHighLevelClient restHighLevelClient;
@Bean
@ConditionalOnMissingBean
public RestHighLevelClient restHighLevelClient(ElasticsearchProperties elasticsearchProperties)
{
if (log.isDebugEnabled())
{
log.debug("初始化Elasticsearch Rest High Level Client....");
}
List hosts = elasticsearchProperties.getHosts();
if (CollectionUtils.isEmpty(hosts))
{
throw new RuntimeException("Elasticsearch host配置为空,请检查:spring.elasticsearch.rest.hosts的配置是否正确");
}
if (log.isDebugEnabled())
{
log.debug("Elasticsearch host: {}", JSONObject.toJSONString(hosts));
}
HttpHost[] httpHosts = new HttpHost[hosts.size()];
int i = 0;
for (HostInfo host : hosts)
{
httpHosts[i++] = new HttpHost(host.getHostname(), host.getPort(), host.getSchema());
}
RestClientBuilder restClientBuilder = RestClient.builder(httpHosts);
restClientBuilder.setMaxRetryTimeoutMillis(elasticsearchProperties.getMaxRetryTimeout());
// 请求参数设置
restClientBuilder.setRequestConfigCallback(requestConfigBuilder ->
{
requestConfigBuilder.setConnectTimeout(elasticsearchProperties.getConnectTimeout());
requestConfigBuilder.setSocketTimeout(elasticsearchProperties.getSocketTimeout());
requestConfigBuilder.setConnectionRequestTimeout(elasticsearchProperties.getRequestTimeout());
return requestConfigBuilder;
});
//异步 httpclient 连接参数配置
restClientBuilder.setHttpClientConfigCallback(httpClientBuilder ->
{
httpClientBuilder.setMaxConnTotal(elasticsearchProperties.getMaxConnect());
httpClientBuilder.setMaxConnPerRoute(elasticsearchProperties.getMaxConnectRoute());
// httpClientBuilder.setThreadFactory();
// SSL 配置
// httpClientBuilder.setSSLContext()
// 请求队列头部拦截 ,request response HttpResponseInterceptor HttpRequestInterceptor
// httpClientBuilder.addInterceptorFirst();
// 请求队列尾部拦截 ,request response
// httpClientBuilder.addInterceptorLast();
// 鉴权设置
if (StringUtils.isNotBlank(elasticsearchProperties.getUsername()) && StringUtils
.isNotBlank(elasticsearchProperties.getPassword()))
{
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider
.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(elasticsearchProperties
.getUsername(), elasticsearchProperties.getPassword()));
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
}
return httpClientBuilder;
});
if (log.isDebugEnabled())
{
log.debug("初始化Elasticsearch Rest High Level Client 成功!");
}
restHighLevelClient = new RestHighLevelClient(restClientBuilder);
return restHighLevelClient;
}
/**
* Invoked by a BeanFactory on destruction of a singleton.
*
* @exception Exception in case of shutdown errors.
* Exceptions will get logged but not rethrown to allow
* other beans to release their resources too.
*/
@Override
public void destroy() throws Exception
{
restHighLevelClient.close();
}
}
================================================
FILE: SpringBoot-Custom-Elasticsearch-Starter/Custom-Elasticsearch-Starter-Autoconfig/src/main/java/com/xiao/custom/elasticsearch/start/autoconfig/properties/ElasticsearchProperties.java
================================================
package com.xiao.custom.elasticsearch.start.autoconfig.properties;
import lombok.Data;
import org.elasticsearch.client.RestClientBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import java.util.List;
/**
* [简要描述]: Elasticsearch 配置类
* [详细描述]:
*
* @author llxiao
* @version 1.0, 2019/8/28 10:18
* @since JDK 1.8
*/
@Data
@ConfigurationProperties(prefix = ElasticsearchProperties.ELASTIC_SEARCH_PREFIX)
public class ElasticsearchProperties
{
public static final String ELASTIC_SEARCH_PREFIX = "spring.elasticsearch.rest";
/**
* 集群名称
*/
private String clusterName;
/**
* 节点信息
*/
@NestedConfigurationProperty
private List hosts;
/**
* 鉴权使用
*/
private String username;
private String password;
/**
* 高亮前缀
*/
private String highlightPre = "";
/**
* 高亮后缀
*/
private String highlightPost = "";
/**
* 连接超时时间
*/
private int connectTimeout = RestClientBuilder.DEFAULT_CONNECT_TIMEOUT_MILLIS;
/**
* socket超时时间
*/
private int socketTimeout = RestClientBuilder.DEFAULT_SOCKET_TIMEOUT_MILLIS;
/**
* 请求超时时间
*/
private int requestTimeout = RestClientBuilder.DEFAULT_CONNECT_TIMEOUT_MILLIS;
/**
* 最大连接数
*/
private int maxConnect = RestClientBuilder.DEFAULT_MAX_CONN_TOTAL;
/**
* 单主机并发最大数
*/
private int maxConnectRoute = RestClientBuilder.DEFAULT_MAX_CONN_PER_ROUTE;
/**
* 重试最大超时时间
*/
private int maxRetryTimeout = RestClientBuilder.DEFAULT_MAX_RETRY_TIMEOUT_MILLIS;
}
================================================
FILE: SpringBoot-Custom-Elasticsearch-Starter/Custom-Elasticsearch-Starter-Autoconfig/src/main/java/com/xiao/custom/elasticsearch/start/autoconfig/properties/HostInfo.java
================================================
package com.xiao.custom.elasticsearch.start.autoconfig.properties;
import lombok.Data;
/**
* [简要描述]:
* [详细描述]:
*
* @author llxiao
* @version 1.0, 2019/8/28 10:22
* @since JDK 1.8
*/
@Data
public class HostInfo
{
private String hostname;
private int port;
private String schema;
}
================================================
FILE: SpringBoot-Custom-Elasticsearch-Starter/Custom-Elasticsearch-Starter-Autoconfig/src/main/resources/META-INF/spring.factories
================================================
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.xiao.custom.elasticsearch.start.autoconfig.ElasticsearchAutoConfiguration
================================================
FILE: SpringBoot-Custom-Elasticsearch-Starter/Custom-Elasticsearch-Starter-Example/pom.xml
================================================
SpringBoot-Custom-Elasticsearch-Starter
com.xiao.skywalking.demo
0.0.1-SNAPSHOT
4.0.0
Custom-Elasticsearch-Starter-Example
Custom ElasticSearch High Level Rest Client Stater Example
6.3.2
2.17.1
org.springframework.boot
spring-boot-starter-web
org.slf4j
slf4j-log4j12
org.apache.logging.log4j
log4j-api
${log4j2.version}
org.apache.logging.log4j
log4j-core
${log4j2.version}
org.springframework.boot
spring-boot-starter-test
test
com.xiao.skywalking.demo
Custom-Elasticsearch-Starter
${project.version}
${project.artifactId}
${project.build.directory}/classes
src/main/resources
true
**/*.xml
**/*.yml
**/*.properties
META-INF/**
org.springframework.boot
spring-boot-maven-plugin
================================================
FILE: SpringBoot-Custom-Elasticsearch-Starter/Custom-Elasticsearch-Starter-Example/src/main/java/com/xiao/custom/elasticsearch/starter/example/ElasticsearchApplication.java
================================================
package com.xiao.custom.elasticsearch.starter.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* [简要描述]:
* [详细描述]:
*
* @author llxiao
* @version 1.0, 2019/8/28 13:54
* @since JDK 1.8
*/
@SpringBootApplication
public class ElasticsearchApplication
{
public static void main(String[] args)
{
SpringApplication.run(ElasticsearchApplication.class, args);
}
}
================================================
FILE: SpringBoot-Custom-Elasticsearch-Starter/Custom-Elasticsearch-Starter-Example/src/main/resources/application.properties
================================================
#spring.elasticsearch.rest.clusterName=omni-dev-es
#spring.elasticsearch.rest.hosts[0].hostname=192.168.206.210
#spring.elasticsearch.rest.hosts[0].port=9200
#spring.elasticsearch.rest.hosts[0].schema=http
#spring.elasticsearch.rest.connectTimeout=1000
#spring.elasticsearch.rest.socketTimeout=30000
#spring.elasticsearch.rest.requestTimeout=500
#spring.elasticsearch.rest.maxConnect=30
#spring.elasticsearch.rest.maxConnectRoute=10
#spring.elasticsearch.rest.maxRetryTimeout=30000
================================================
FILE: SpringBoot-Custom-Elasticsearch-Starter/Custom-Elasticsearch-Starter-Example/src/main/resources/application.yml
================================================
spring:
elasticsearch:
rest:
clusterName: omni-dev-es
hosts:
-
hostname: 192.168.206.210
port: 9200
schema: http
#-
#hostname: 192.168.206.212
#port: 9200
#schema: http
#username: you username
#password: you passwd
# 连接超时时间,单位ms,默认1S
connectTimeout: 1000
# socket超时时间,单位ms,默认30S
socketTimeout: 30000
# 请求超时时间,单位ms,默认500ms
requestTimeout: 500
# 单机最大连接数,默认30个
maxConnect: 30
# 单机最大并发数,默认10个
maxConnectRoute: 10
# 最大重试时间,默认30S
maxRetryTimeout: 30000
# 高亮前缀
#highlightPre:
# 高亮后缀
#highlightPost:
================================================
FILE: SpringBoot-Custom-Elasticsearch-Starter/Custom-Elasticsearch-Starter-Example/src/main/resources/bootstrap.yml
================================================
================================================
FILE: SpringBoot-Custom-Elasticsearch-Starter/Custom-Elasticsearch-Starter-Example/src/main/resources/logback-spring.xml
================================================
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
================================================
FILE: SpringBoot-Custom-Elasticsearch-Starter/Custom-Elasticsearch-Starter-Example/src/test/java/com/xiao/custom/elasticsearch/starter/example/ElasticsearchApplicationTest.java
================================================
package com.xiao.custom.elasticsearch.starter.example;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xiao.custom.elasticsearch.start.autoconfig.properties.ElasticsearchProperties;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.message.BasicHeader;
import org.apache.http.nio.entity.NStringEntity;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.DocWriteRequest;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.close.CloseIndexRequest;
import org.elasticsearch.action.admin.indices.close.CloseIndexResponse;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
import org.elasticsearch.action.admin.indices.open.OpenIndexResponse;
import org.elasticsearch.action.bulk.*;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedRequest;
import org.elasticsearch.action.support.replication.ReplicationResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.ByteSizeUnit;
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.get.GetResult;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* [简要描述]:
* [详细描述]:
*
* @author llxiao
* @version 1.0, 2019/8/28 13:56
* @since JDK 1.8
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class ElasticsearchApplicationTest
{
@Autowired
private RestHighLevelClient restHighLevelClient;
@Autowired
private ElasticsearchProperties elasticsearchProperties;
/**
* 搜索
* https://www.elastic.co/guide/en/elasticsearch/client/java-rest/6.2/java-rest-high-search.html
*/
@Test
public void testQuery()
{
SearchRequest request = new SearchRequest("10000");
// request.searchType("10000");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.termQuery("commodityNo", "3439538790"));
searchSourceBuilder.from(0);
searchSourceBuilder.size(10);
searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
// 高亮设置
HighlightBuilder highlightBuilder = new HighlightBuilder().field("*").requireFieldMatch(false);
if (StringUtils.isNoneEmpty(elasticsearchProperties.getHighlightPre()))
{
highlightBuilder.preTags(elasticsearchProperties.getHighlightPre());
highlightBuilder.postTags(elasticsearchProperties.getHighlightPost());
highlightBuilder.field("subTitle");
highlightBuilder.field("title");
}
searchSourceBuilder.highlighter(highlightBuilder);
request.source(searchSourceBuilder);
try
{
final SearchResponse response = restHighLevelClient.search(request);
response.getHits().forEach(document -> System.out.println(document.getSourceAsString()));
}
catch (IOException e)
{
e.printStackTrace();
}
}
/**
* 批处理:
* 增删改查
*/
public void testBulkRequest()
{
BulkRequest request = new BulkRequest();
request.add(new DeleteRequest("posts", "doc", "3"));
request.add(new UpdateRequest("posts", "doc", "2").doc(XContentType.JSON, "other", "test"));
request.add(new IndexRequest("posts", "doc", "4").source(XContentType.JSON, "field", "baz"));
try
{
// restHighLevelClient.bulkAsync(request, ActionListener);
BulkResponse bulkResponse = restHighLevelClient.bulk(request);
if (bulkResponse.hasFailures())
{
for (BulkItemResponse bulkItemResponse : bulkResponse)
{
if (bulkItemResponse.isFailed())
{
BulkItemResponse.Failure failure = bulkItemResponse.getFailure();
System.out.println("处理失败:" + failure.getId() + '-' + failure.getMessage());
}
}
}
for (BulkItemResponse bulkItemResponse : bulkResponse)
{
DocWriteResponse itemResponse = bulkItemResponse.getResponse();
if (bulkItemResponse.getOpType() == DocWriteRequest.OpType.INDEX
|| bulkItemResponse.getOpType() == DocWriteRequest.OpType.CREATE)
{
IndexResponse indexResponse = (IndexResponse) itemResponse;
// 创建
}
else if (bulkItemResponse.getOpType() == DocWriteRequest.OpType.UPDATE)
{
UpdateResponse updateResponse = (UpdateResponse) itemResponse;
// 更新
}
else if (bulkItemResponse.getOpType() == DocWriteRequest.OpType.DELETE)
{
DeleteResponse deleteResponse = (DeleteResponse) itemResponse;
// 删除
}
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
/**
* BulkProcessor通过提供一个实用程序类简化了批量API的使用,该实用程序类允许在将索引/更新/删除操作添加到处理器时透明地执行这些操作。
*/
@Test
public void testBulkProcessor()
{
BulkProcessor.Listener listener = new BulkProcessor.Listener()
{
@Override
public void beforeBulk(long executionId, BulkRequest request)
{
int numberOfActions = request.numberOfActions();
System.out.println("当前BulkProcessor中执行的操作数:" + numberOfActions);
}
@Override
public void afterBulk(long executionId, BulkRequest request, BulkResponse response)
{
if (response.hasFailures())
{
System.out.println("当前BulkProcessor执行出现异常:" + response.buildFailureMessage());
}
else
{
System.out.println("执行成功!");
}
}
@Override
public void afterBulk(long executionId, BulkRequest request, Throwable failure)
{
System.out.println("当前请求发生的错误消息:" + failure.getMessage());
failure.printStackTrace();
}
};
BulkProcessor.Builder builder = BulkProcessor.builder(restHighLevelClient::bulkAsync, listener);
//根据当前添加的操作数设置刷新新批量请求的时间 defaults to 1000
builder.setBulkActions(500);
// 根据当前添加的操作的大小设置刷新新批量请求的时间 defaults to 5Mb
builder.setBulkSize(new ByteSizeValue(1L, ByteSizeUnit.MB));
// 设置允许执行的并发请求数 默认0仅允许一个
builder.setConcurrentRequests(0);
// 设置刷新间隔,如果间隔通过,则刷新任何挂起的BulkRequest
builder.setFlushInterval(TimeValue.timeValueSeconds(10L));
// 回退策略 等待1秒,最多重试3
builder.setBackoffPolicy(BackoffPolicy.constantBackoff(TimeValue.timeValueSeconds(1L), 3));
BulkProcessor bulkProcessor = builder.build();
IndexRequest one = new IndexRequest("posts", "doc", "1").
source(XContentType.JSON, "title", "In which order are my Elasticsearch queries executed?");
IndexRequest two = new IndexRequest("posts", "doc", "2")
.source(XContentType.JSON, "title", "Current status and upcoming changes in Elasticsearch");
IndexRequest three = new IndexRequest("posts", "doc", "3")
.source(XContentType.JSON, "title", "The Future of Federated Search in Elasticsearch");
bulkProcessor.add(one);
bulkProcessor.add(two);
bulkProcessor.add(three);
try
{
// 执行并等待,直到超时
boolean terminated = bulkProcessor.awaitClose(30L, TimeUnit.SECONDS);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
// 关闭
bulkProcessor.close();
}
/**
* 插入数据 index api
*/
@Test
public void testInsert() throws IOException
{
String index = "101010";
String type = index;
String id = UUID.randomUUID().toString();
IndexRequest indexRequest = new IndexRequest(index, type, id);
// 可以设置版本号,但可能出现版本冲突异常 ElasticsearchException e.status() == RestStatus.CONFLICT
indexRequest.version(1);
// or
// IndexRequest indexRequest = new IndexRequest();
// indexRequest.index(index);
// indexRequest.type(type);
// indexRequest.id(id);
// As json String
String jsonString = "{" + "\"user\":\"kimchy\"," + "\"postDate\":\"2013-01-30\","
+ "\"message\":\"trying out Elasticsearch\"" + "}";
indexRequest.source(jsonString, XContentType.JSON);
// As map
Map jsonMap = new HashMap<>();
jsonMap.put("user", "kimchy");
jsonMap.put("postDate", new Date());
jsonMap.put("message", "trying out Elasticsearch");
indexRequest.source(jsonMap);
// As XContentBuilder
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
{
builder.field("user", "kimchy");
builder.field("postDate", new Date());
builder.field("message", "trying out Elasticsearch");
}
builder.endObject();
indexRequest.source(builder);
// As source
indexRequest.source("user", "kimchy", "postDate", new Date(), "message", "trying out Elasticsearch");
// index or indexAsync
IndexResponse indexResponse = restHighLevelClient.index(indexRequest);
index = indexResponse.getIndex();
type = indexResponse.getType();
id = indexResponse.getId();
long version = indexResponse.getVersion();
if (indexResponse.getResult() == DocWriteResponse.Result.CREATED)
{
System.out.println("ES数据已经成功创建");
}
else if (indexResponse.getResult() == DocWriteResponse.Result.UPDATED)
{
System.out.println("ES数据已经成功覆盖");
}
ReplicationResponse.ShardInfo shardInfo = indexResponse.getShardInfo();
if (shardInfo.getTotal() != shardInfo.getSuccessful())
{
System.out.println("数据创建成功,但成功的shard数量小于总shard数量");
}
if (shardInfo.getFailed() > 0)
{
for (ReplicationResponse.ShardInfo.Failure failure : shardInfo.getFailures())
{
System.out.println("失败原:" + failure.reason());
}
}
}
/**
* 文档ID 查找
*/
@Test
public void testGetIndex()
{
GetRequest getRequest = new GetRequest("101010", "101010", "101010");
// 设置版本
// getRequest.version(2);
//禁用获取 _source字段
getRequest.fetchSourceContext(new FetchSourceContext(false));
try
{
// get or getAsync(request,ActionListener)
GetResponse getResponse = restHighLevelClient.get(getRequest);
String index = getResponse.getIndex();
String type = getResponse.getType();
String id = getResponse.getId();
if (getResponse.isExists())
{
long version = getResponse.getVersion();
String sourceAsString = getResponse.getSourceAsString();
Map sourceAsMap = getResponse.getSourceAsMap();
byte[] sourceAsBytes = getResponse.getSourceAsBytes();
}
else
{
System.out.println("不存在的数据!");
}
}
catch (ElasticsearchException e)
{
if (e.status() == RestStatus.NOT_FOUND)
{
System.out.println("索引不存在");
}
else if (e.status() == RestStatus.CONFLICT)
{
System.out.println("版本冲突!");
}
else
{
System.out.println("其他为未知异常:" + e.status().name());
}
}
catch (IOException e)
{
System.out.println("IO 异常");
}
}
/**
* 文档ID 更新
* 1. 脚本更新
* 2. 文档更新:部分字段更新和不存在直接插入更新
*/
@Test
public void testUpdate() throws IOException
{
UpdateRequest request = new UpdateRequest("101010", "101010", "101010");
// ############## 更新文档数据部分字段使用 .doc ,如果不确定存在则直接插入使用 .upsert方法
// As JSON String
String jsonString = "{" + "\"updated\":\"2017-01-01\"," + "\"reason\":\"daily update\"" + "}";
request.doc(jsonString, XContentType.JSON);
// request.upsert(jsonString,XContentType.JSON);
// As Map
Map jsonMap = new HashMap<>();
jsonMap.put("updated", new Date());
jsonMap.put("reason", "daily update");
request.doc(jsonMap);
// request.upsert(jsonMap);
// AS XContentBuilder
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
{
builder.field("updated", new Date());
builder.field("reason", "daily update");
}
builder.endObject();
request.doc(builder);
request.upsert(builder);
// 设置版本
// request.version(2);
//指示如果部分文档尚不存在,则必须将其用作upsert文档。
// request.docAsUpsert(true);
try
{
//同步 update 异步 updateAsync(request,ActionListener)
UpdateResponse updateResponse = restHighLevelClient.update(request);
String index = updateResponse.getIndex();
String type = updateResponse.getType();
String id = updateResponse.getId();
long version = updateResponse.getVersion();
if (updateResponse.getResult() == DocWriteResponse.Result.CREATED)
{
System.out.println("文档不存在,创建成功!");
}
else if (updateResponse.getResult() == DocWriteResponse.Result.UPDATED)
{
System.out.println("文档更新成功!");
}
else if (updateResponse.getResult() == DocWriteResponse.Result.DELETED)
{
System.out.println("文档删除成功!");
}
else if (updateResponse.getResult() == DocWriteResponse.Result.NOOP)
{
System.out.println("没有对文档做任何更新操作!");
}
// 获取更新后的数据
GetResult result = updateResponse.getGetResult();
if (result.isExists())
{
String sourceAsString = result.sourceAsString();
Map sourceAsMap = result.sourceAsMap();
byte[] sourceAsBytes = result.source();
}
else
{
System.out.println("文档不存在!");
}
// 分片更新失败
ReplicationResponse.ShardInfo shardInfo = updateResponse.getShardInfo();
if (shardInfo.getFailed() > 0)
{
for (ReplicationResponse.ShardInfo.Failure failure : shardInfo.getFailures())
{
System.out.println("分片ID:" + failure.fullShardId() + "更新失败:" + failure.reason());
}
}
}
catch (ElasticsearchException e)
{
// 可能有索引没找到,版本异常,参考RestStatus
System.out.println("更新异常:" + e.status().name());
}
}
/**
* 文档ID 删除
*/
@Test
public void testDel()
{
DeleteRequest request = new DeleteRequest("101010", "101010", "101010");
try
{
DeleteResponse deleteResponse = restHighLevelClient.delete(request);
if (deleteResponse.getResult() == DocWriteResponse.Result.NOT_FOUND)
{
System.out.println("删除的文档不存在");
}
ReplicationResponse.ShardInfo shardInfo = deleteResponse.getShardInfo();
if (shardInfo.getTotal() != shardInfo.getSuccessful())
{
System.out.println("删除成功了,但是删除的数量与分片的数量不符合!");
}
if (shardInfo.getFailed() > 0)
{
for (ReplicationResponse.ShardInfo.Failure failure : shardInfo.getFailures())
{
System.out.println("分片ID:" + failure.fullShardId() + "更新失败:" + failure.reason());
}
}
}
catch (ElasticsearchException e)
{
System.out.println("删除异常:" + e.status().name());
}
catch (IOException e)
{
e.printStackTrace();
}
}
/**
* 创建索引
*
* @exception IOException
*/
@Test
public void testCreateIndex() throws IOException
{
CreateIndexRequest request = new CreateIndexRequest("101010");
// setting设置
request.settings(Settings.builder().put("index.number_of_shards", 3).put("index.number_of_replicas", 2));
// mapping
// request.mapping();
request.alias(new Alias("101010_alias"));
// 其他可选参数
setOptionParams(request);
// 同步创建
final CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request);
// 所有节点是否已确认请求
if (createIndexResponse.isAcknowledged())
{
System.out.println("创建成功!");
}
// 是否在超时前为索引中的每个碎片启动了所需数量的分片副本
if (createIndexResponse.isShardsAcknowledged())
{
System.out.println("分片副本都已创建成功");
}
// 异步创建
// restHighLevelClient.indices().createAsync(request, new ActionListener()
// {
// @Override
// public void onResponse(CreateIndexResponse clearIndicesCacheResponse)
// {
// System.out.println("所有节点是否已确认请求: " + createIndexResponse.isAcknowledged());
// System.out.println("分片副本都已创建成功:" + createIndexResponse.isShardsAcknowledged());
// }
//
// @Override
// public void onFailure(Exception e)
// {
// System.out.println("请求出现异常,异常信息:" + e.getMessage());
// e.printStackTrace();
// }
// });
}
/**
* 所以是否存在
*/
@Test
public void testIndexExist()
{
GetIndexRequest request = new GetIndexRequest();
request.indices("10001");
try
{
System.out.println(restHighLevelClient.indices().exists(request));
}
catch (IOException e)
{
e.printStackTrace();
}
}
/**
* 删除索引
*/
@Test
public void testDelIndex()
{
DeleteIndexRequest request = new DeleteIndexRequest("101010");
// 其他可选参数
setOptionParams(request);
// 同步
try
{
DeleteIndexResponse response = restHighLevelClient.indices().delete(request);
System.out.println("所有节点已确认:" + response.isAcknowledged());
}
catch (ElasticsearchException exception)
{
if (exception.status() == RestStatus.NOT_FOUND)
{
System.out.println("索引未找到!");
}
}
catch (IOException e)
{
e.printStackTrace();
}
// 异步
// restHighLevelClient.indices().deleteAsync(request, new ActionListener()
// {
// @Override
// public void onResponse(DeleteIndexResponse deleteIndexResponse)
// {
// System.out.println("所有节点已确认:" + deleteIndexResponse.isAcknowledged());
// }
//
// @Override
// public void onFailure(Exception e)
// {
// if (e instanceof ElasticsearchException)
// {
// ElasticsearchException exception = (ElasticsearchException) e;
// if (exception.status() == RestStatus.NOT_FOUND)
// {
// System.out.println("索引未找到!");
// }
// }
// System.out.println("删除出现位置异常!");
// }
// });
}
/**
* 打开和关闭索引
*/
@Test
public void testOpenAndCloseIndex() throws IOException
{
OpenIndexRequest openIndexRequest = new OpenIndexRequest("index");
setOptionParams(openIndexRequest);
// 同步 open or 异步 openAsync
OpenIndexResponse openIndexResponse = restHighLevelClient.indices().open(openIndexRequest);
System.out.println("所有节点已确认:" + openIndexResponse.isAcknowledged());
System.out.println("所有副本分片已确认:" + openIndexResponse.isShardsAcknowledged());
CloseIndexRequest closeIndexRequest = new CloseIndexRequest("index");
setOptionParams(closeIndexRequest);
// 同步 close or 异步 closeAsync
CloseIndexResponse closeIndexResponse = restHighLevelClient.indices().close(closeIndexRequest);
System.out.println("所有节点已确认:" + closeIndexResponse.isAcknowledged());
}
private void setOptionParams(AcknowledgedRequest request)
{
// 其他可选参数
request.timeout(TimeValue.timeValueMinutes(2));
// request.timeout("2m");
// master node
// request.masterNodeTimeout(TimeValue.timeValueMinutes(1));
// request.masterNodeTimeout("1m");
// 创建索引API返回响应之前等待的活动分片副本数
// request.waitForActiveShards(2);
// request.waitForActiveShards(ActiveShardCount.DEFAULT);
}
/**
* [简要描述]:使用LowLevelClient 执行analyzer操作
* [详细描述]:
*
* llxiao 2019/9/17 - 10:45
**/
@Test
public void testAnalysis()
{
RestClient lowLevelClient = restHighLevelClient.getLowLevelClient();
JSONObject entity = new JSONObject();
entity.put("analyzer", "ik_max_word");
entity.put("text", "我是中国人");
HttpEntity httpEntity = new NStringEntity(JSONObject.toJSONString(entity), ContentType.APPLICATION_JSON);
Map params = Collections.emptyMap();
Header[] defaultHeaders = new Header[] { new BasicHeader("header", "value") };
try
{
Response response = lowLevelClient.performRequest("POST", "_analyze", params, httpEntity, defaultHeaders);
JSONObject tokens = JSONObject.parseObject(EntityUtils.toString(response.getEntity()));
JSONArray arrays = tokens.getJSONArray("tokens");
String[] result = new String[arrays.size()];
for (int i = 0; i < arrays.size(); i++)
{
JSONObject obj = JSONObject.parseObject(arrays.getString(i));
result[i] = obj.getString("token");
}
System.out.println(Arrays.toString(result));
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
================================================
FILE: SpringBoot-Custom-Elasticsearch-Starter/Readme.md
================================================
**定制SpringBoot Starter 之Elasticsearch Rest High Level Client Starter**
**1. 自定义SpringBoot Starter 三要素:**
>1.1.pom :
```$xslt
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-autoconfigure
org.springframework.boot
spring-boot-configuration-processor
true
```
>1.2. 注解使用
```$xslt
@Data
@ConfigurationProperties(prefix = ElasticsearchProperties.ELASTIC_SEARCH_PREFIX)
public class ElasticsearchProperties{}
@Configuration
@EnableConfigurationProperties(ElasticsearchProperties.class)
public class ElasticsearchAutoConfiguration{}
```
>1.3 EnableAutoConfiguration 利用SpringFactoriesLoader机制加载所有的AutoConfiguration类 META-INF/spring.factories
```$xslt
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.xiao.custom.elasticsearch.start.autoconfig.ElasticsearchAutoConfiguration
```
Elasticsearch高级客户端打包集成为Springboot Starter包,详情参考[Example工程](https://github.com/Xlinlin/SpringCloud-Demo/tree/master/SpringBoot-Custom-Elasticsearch-Starter/Custom-Elasticsearch-Starter-Example)
**2. Custom ElasticSearch High Level Rest Client Starter使用说明:**
>2.1Pom引入
```$xslt
6.3.2
com.purcotton.omni
omni-common-elasticsearch-starter
${project.version}
org.elasticsearch
elasticsearch
${elasticsearch.version}
org.elasticsearch.client
elasticsearch-rest-client
${elasticsearch.version}
org.elasticsearch.client
elasticsearch-rest-high-level-client
${elasticsearch.version}
```
>2.2配置文件:
```$xslt
spring:
elasticsearch:
rest:
clusterName: omni-dev-es
hosts:
-
hostname: 192.168.206.210
port: 9200
schema: http
#-
#hostname: 192.168.206.212
#port: 9200
#schema: http
#username: you username
#password: you passwd
# 连接超时时间,单位ms,默认1S
connectTimeout: 1000
# socket超时时间,单位ms,默认30S
socketTimeout: 30000
# 请求超时时间,单位ms,默认500ms
requestTimeout: 500
# 单机最大连接数,默认30个
maxConnect: 30
# 单机最大并发数,默认10个
maxConnectRoute: 10
# 最大重试时间,默认30S
maxRetryTimeout: 30000
```
>2.3代码引用:
```$xslt
@Autowired
private RestHighLevelClient restHighLevelClient;
```
>2.4ElasticSearch High Level Rest Client 增删改Demo:
[ElasticsearchApplicationTest](https://github.com/Xlinlin/SpringCloud-Demo/blob/master/SpringBoot-Custom-Elasticsearch-Starter/Custom-Elasticsearch-Starter-Example/src/test/java/com/xiao/custom/elasticsearch/starter/example/ElasticsearchApplicationTest.java)
```$xslt
// 创建索引
ElasticsearchApplicationTest.testCreateIndex()
// 索引是否存在
ElasticsearchApplicationTest.testIndexExist()
// 删除索引
ElasticsearchApplicationTest.testDelIndex()
// 打开和关闭索引
ElasticsearchApplicationTest.testOpenAndCloseIndex()
// 添加文档
ElasticsearchApplicationTest.testInsert()
// 主键ID获取文档
ElasticsearchApplicationTest.testGetIndex()
// 更新文档
ElasticsearchApplicationTest.testUpdate()
// 搜索
ElasticsearchApplicationTest.testQuery()
// 删除文档
ElasticsearchApplicationTest.testDel()
// 批处理1
ElasticsearchApplicationTest.testBulkRequest()
// 批处理2
ElasticsearchApplicationTest.testBulkProcessor()
```
================================================
FILE: SpringBoot-Custom-Elasticsearch-Starter/pom.xml
================================================
org.springframework.boot
spring-boot-starter-parent
1.5.9.RELEASE
4.0.0
com.xiao.skywalking.demo
SpringBoot-Custom-Elasticsearch-Starter
0.0.1-SNAPSHOT
Custom ElasticSearch High Level Rest Client Stater
pom
Custom-Elasticsearch-Starter-Autoconfig
Custom-Elasticsearch-Starter
Custom-Elasticsearch-Starter-Example
================================================
FILE: SpringBoot-Custom-Rest-Starter/Readme.md
================================================
Springboot Rest Template配置
1. 支持Ok Http和Http连接池模式,内嵌包装成HttClientService服务,并提供完成的请求日志处理
2. 引入pom:
```$xslt
com.xiao.skywalking.demo
SpringBoot-Custom-Rest-Autconfigure
0.0.1-SNAPSHOT
```
3. Ok Http使用:
```$xslt
rest:
# okhttp 配置
okhttp:
enable: true
connection-timeout: 12000
read-timeout: 30000
write-timeout: 12000
```
4. Http pool使用:
```$xslt
rest
# http pool
pool:
enable: true
max-total: 20
default-max-per-route: 2
validate-after-inactivity: 2000
connect-timeout: 10000
connection-request-timeout: 10000
socket-timeout: 10000
```
5. 同步异步使用:
```$xslt
rest:
http:
service:
sync: true
async: false
```
6. 使用方式,推荐使用HttpClientService,因为提供了完整的日志记录:
```$xslt
// 使用包装http client
@Autowired
private HttpClientService httpClientService;
// 使用 resttemplate
@Autowired
private RestTemplate restTemplate;
```
7. HttpClientService日志处理,实现HttpRequestLogService接口
```$xslt
public class HttpLogServiceImpl implements HttpRequestLogService
{
/**
* [简要描述]:保存日志信息
* [详细描述]:
*
* @param requestLog :
* llxiao 2019/4/24 - 14:42
**/
@Override
public void saveRequestLog(HttpRequestLog requestLog)
{
// 日志输出
log.info("Example log : {}", JSONObject.toJSONString(requestLog));
}
}
```
================================================
FILE: SpringBoot-Custom-Rest-Starter/SpringBoot-Custom-Rest-Autconfigure/pom.xml
================================================
SpringBoot-Custom-Rest-Starter
com.xiao.skywalking.demo
0.0.1-SNAPSHOT
4.0.0
SpringBoot-Custom-Rest-Autconfigure
org.springframework.boot
spring-boot-starter-web
org.slf4j
slf4j-log4j12
org.springframework.retry
spring-retry
org.aspectj
aspectjweaver
org.springframework.cloud
spring-cloud-context
compile
1.3.4.RELEASE
org.springframework.boot
spring-boot-starter-aop
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-autoconfigure
org.springframework.boot
spring-boot-configuration-processor
true
org.projectlombok
lombok
1.16.18
true
org.apache.commons
commons-lang3
3.4
commons-collections
commons-collections
3.2.2
com.squareup.okhttp3
okhttp
3.11.0
org.apache.httpcomponents
httpclient
4.5.13
com.alibaba
fastjson
1.2.83
================================================
FILE: SpringBoot-Custom-Rest-Starter/SpringBoot-Custom-Rest-Autconfigure/src/main/java/com/xiao/custom/rest/starter/autoconfigure/config/RestTemplateConfiguration.java
================================================
package com.xiao.custom.rest.starter.autoconfigure.config;
import com.xiao.custom.rest.starter.autoconfigure.config.properties.HttpPoolProperties;
import com.xiao.custom.rest.starter.autoconfigure.config.properties.OkHttpProperties;
import com.xiao.custom.rest.starter.autoconfigure.interceptor.RestInterceptor;
import com.xiao.custom.rest.starter.autoconfigure.service.HttpClientService;
import com.xiao.custom.rest.starter.autoconfigure.service.impl.HttpClientAsyncServiceImpl;
import com.xiao.custom.rest.starter.autoconfigure.service.impl.HttpClientServiceImpl;
import com.xiao.custom.rest.starter.autoconfigure.service.impl.HttpRetryService;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
/**
* [简要描述]: 初始化rest template
* [详细描述]: 开启重试
*
* @author llxiao
* @version 1.0, 2019/11/30 10:43
* @since JDK 1.8
*/
@Configuration
@ComponentScan("com.xiao.custom.rest.starter.autoconfigure")
@Slf4j
@EnableRetry
public class RestTemplateConfiguration
{
/**
* [简要描述]:okHttp支持
* [详细描述]:
*
* @param okHttpProperties :
* @return org.springframework.http.client.ClientHttpRequestFactory
* xiaolinlin 2020/1/16 - 18:43
**/
@Bean
@ConditionalOnProperty(value = "rest.okhttp.enable", havingValue = "true")
@ConditionalOnMissingBean(ClientHttpRequestFactory.class)
public ClientHttpRequestFactory okHttpHttpRequestFactory(OkHttpProperties okHttpProperties)
{
log.info("Init request factory for okHttp!");
OkHttp3ClientHttpRequestFactory clientHttpRequestFactory = new OkHttp3ClientHttpRequestFactory(buildOkHttpsClient()
.build());
//连接超时
clientHttpRequestFactory.setConnectTimeout(okHttpProperties.getConnectionTimeout());
//读超时
clientHttpRequestFactory.setReadTimeout(okHttpProperties.getReadTimeout());
//写超时
clientHttpRequestFactory.setWriteTimeout(okHttpProperties.getWriteTimeout());
return clientHttpRequestFactory;
}
/**
* [简要描述]:http pool支持
* [详细描述]:
*
* @param httpPoolProperties :
* @return org.springframework.http.client.ClientHttpRequestFactory
* xiaolinlin 2020/1/16 - 18:43
**/
@Bean
@ConditionalOnProperty(value = "rest.pool.enable", havingValue = "true")
@ConditionalOnMissingBean(ClientHttpRequestFactory.class)
public ClientHttpRequestFactory httpPoolRequestFactory(HttpPoolProperties httpPoolProperties)
{
log.info("Init request factory for http pool");
SSLConnectionSocketFactory socketFactory = null;
SSLContext sslContext = buildSslContext();
if (null != sslContext)
{
socketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
}
else
{
socketFactory = SSLConnectionSocketFactory.getSocketFactory();
}
Registry registry = RegistryBuilder.create()
.register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", socketFactory)
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
connectionManager.setMaxTotal(httpPoolProperties.getMaxTotal());
connectionManager.setDefaultMaxPerRoute(httpPoolProperties.getDefaultMaxPerRoute());
connectionManager.setValidateAfterInactivity(httpPoolProperties.getValidateAfterInactivity());
RequestConfig requestConfig = RequestConfig.custom()
//服务器返回数据(response)的时间,超过抛出read timeout
.setSocketTimeout(httpPoolProperties.getSocketTimeout())
//连接上服务器(握手成功)的时间,超出抛出connect timeout
.setConnectTimeout(httpPoolProperties.getConnectTimeout())
//从连接池中获取连接的超时时间,超时间未拿到可用连接,会抛出org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
.setConnectionRequestTimeout(httpPoolProperties.getConnectionRequestTimeout()).build();
return new HttpComponentsClientHttpRequestFactory(HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig).setConnectionManager(connectionManager).build());
}
/**
* [简要描述]:RestTemplate
* [详细描述]:
*
* @param clientHttpRequestFactory :
* @return org.springframework.web.client.RestTemplate
* xiaolinlin 2020/1/16 - 18:47
**/
@Bean
@ConditionalOnMissingBean(RestTemplate.class)
public RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory)
{
log.info("Init rest template!");
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
restTemplate.setRequestFactory(clientHttpRequestFactory);
restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
// 拦截
restTemplate.setInterceptors(Collections.singletonList(new RestInterceptor()));
return restTemplate;
}
/**
* [简要描述]:集成重试机制
* [详细描述]:
*
* @param restTemplate :
* @return com.purcotton.omni.rest.stater.common.service.impl.HttpRetryService
* xiaolinlin 2020/1/16 - 18:47
**/
@Bean
public HttpRetryService retryService(RestTemplate restTemplate)
{
log.info("Init http retry support!");
return new HttpRetryService(restTemplate);
}
/**
* [简要描述]:http 同步服务
* [详细描述]:
*
* @param retryService: 支持重试请求
* @return com.purcotton.omni.rest.stater.common.service.HttpClientService
* xiaolinlin 2020/1/16 - 18:48
**/
@Bean
@ConditionalOnProperty(value = "rest.http.service.sync", havingValue = "true")
@ConditionalOnMissingBean(HttpClientService.class)
public HttpClientService httpClientService(HttpRetryService retryService)
{
log.info("Use sync http client service!");
return new HttpClientServiceImpl(retryService);
}
/**
* [简要描述]:http 异步服务
* [详细描述]:
*
* @param retryService : 支持重试请求
* @return com.purcotton.omni.rest.stater.common.service.HttpClientService
* xiaolinlin 2020/1/16 - 18:48
**/
@Bean
@ConditionalOnProperty(value = "rest.http.service.async", havingValue = "true")
@ConditionalOnMissingBean(HttpClientService.class)
public HttpClientService asyncHttpClientService(HttpRetryService retryService)
{
log.info("User async http client service!");
return new HttpClientAsyncServiceImpl(retryService);
}
/**
* [简要描述]:okhttp3 跳过https验证
* [详细描述]:
*
* @return okhttp3.OkHttpClient.Builder
* xiaolinlin 2020/1/4 - 10:31
**/
private OkHttpClient.Builder buildOkHttpsClient()
{
OkHttpClient.Builder builder = new OkHttpClient.Builder();
TrustManager[] trustAllCerts = buildTrustManagers();
SSLContext sslContext = buildSslContext();
if (null != sslContext && null != trustAllCerts)
{
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
}
builder.hostnameVerifier((hostname, session) -> true);
return builder;
}
private SSLContext buildSslContext()
{
TrustManager[] trustAllCerts = buildTrustManagers();
SSLContext sslContext = null;
try
{
sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
}
catch (NoSuchAlgorithmException | KeyManagementException e)
{
log.error("Init SSLContext error :\n", e);
}
return sslContext;
}
private TrustManager[] buildTrustManagers()
{
return new TrustManager[] { new X509TrustManager()
{
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType)
{
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType)
{
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers()
{
return new java.security.cert.X509Certificate[] {};
}
}
};
}
}
================================================
FILE: SpringBoot-Custom-Rest-Starter/SpringBoot-Custom-Rest-Autconfigure/src/main/java/com/xiao/custom/rest/starter/autoconfigure/config/properties/HttpPoolProperties.java
================================================
package com.xiao.custom.rest.starter.autoconfigure.config.properties;
import lombok.Data;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* [简要描述]: http 连接池参数配置
* [详细描述]:
*
* @author llxiao
* @version 1.0, 2019/11/30 11:09
* @since JDK 1.8
*/
@Component
@ConfigurationProperties(prefix = "rest.pool")
@ConditionalOnProperty(value = "rest.pool.enable", havingValue = "true")
@Data
public class HttpPoolProperties
{
private boolean enable;
/**
* 最大连接数
*/
private Integer maxTotal = 20;
/**
* 最大路由数
*/
private Integer defaultMaxPerRoute = 2;
/**
* 连接超时时间
*/
private Integer connectTimeout = 5000;
/**
* 请求超时时间
*/
private Integer connectionRequestTimeout = 1000;
/**
* socket超时时间
*/
private Integer socketTimeout = 6500;
/**
* 校验时间
*/
private Integer validateAfterInactivity = 2000;
}
================================================
FILE: SpringBoot-Custom-Rest-Starter/SpringBoot-Custom-Rest-Autconfigure/src/main/java/com/xiao/custom/rest/starter/autoconfigure/config/properties/OkHttpProperties.java
================================================
package com.xiao.custom.rest.starter.autoconfigure.config.properties;
import lombok.Data;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* [简要描述]: ok http 参数配置
* [详细描述]:
*
* @author llxiao
* @version 1.0, 2019/11/30 11:09
* @since JDK 1.8
*/
@Component
@ConfigurationProperties("rest.okhttp")
@ConditionalOnProperty(value = "rest.okhttp.enable", havingValue = "true")
@Data
public class OkHttpProperties
{
private boolean enable;
/**
* 连接超时时间
*/
private int connectionTimeout = 12000;
/**
* 读超时时间
*/
private int readTimeout = 300000;
/**
* 写超时时间
*/
private int writeTimeout = 120000;
}
================================================
FILE: SpringBoot-Custom-Rest-Starter/SpringBoot-Custom-Rest-Autconfigure/src/main/java/com/xiao/custom/rest/starter/autoconfigure/dto/Request.java
================================================
package com.xiao.custom.rest.starter.autoconfigure.dto;
import lombok.Data;
import org.springframework.http.HttpHeaders;
/**
* [简要描述]:
* [详细描述]:
*
* @author llxiao
* @version 1.0, 2019/4/24 13:48
* @since JDK 1.8
*/
@Data
public class Request
{
public static final int POST = 0;
public static final int JSON = 1;
/**
* 请求uri
*/
private String uri;
/**
* 返回值类型
*/
private Class responseType;
/**
* 请求参数
*/
private Object params;
/**
* 执行方式:0普通请求,1.JSON请求
*/
private int method;
/**
* 执行请求的ID,用于重复请求更新操作
*/
private Long requestId;
/**
* 自定义请求头
*/
private HttpHeaders headers;
/**
* url变量
*/
private Object uriVariables;
}
================================================
FILE: SpringBoot-Custom-Rest-Starter/SpringBoot-Custom-Rest-Autconfigure/src/main/java/com/xiao/custom/rest/starter/autoconfigure/interceptor/RestInterceptor.java
================================================
package com.xiao.custom.rest.starter.autoconfigure.interceptor;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xiao.custom.rest.starter.autoconfigure.log.dto.HttpRequestLog;
import com.xiao.custom.rest.starter.autoconfigure.util.ThreadLocalUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.support.HttpRequestWrapper;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* [简要描述]: restTemplate 拦截
* [详细描述]:
*
* @author llxiao
* @version 1.0, 2019/4/23 16:47
* @since JDK 1.8
*/
@Slf4j
public class RestInterceptor implements ClientHttpRequestInterceptor
{
/**
* Intercept the given request, and return a response. The given {@link ClientHttpRequestExecution} allows
* the interceptor to pass on the request and response to the next entity in the chain.
*
* A typical implementation of this method would follow the following pattern:
*
* - Examine the {@linkplain HttpRequest request} and body
* - Optionally {@linkplain HttpRequestWrapper wrap} the request to filter HTTP attributes.
* - Optionally modify the body of the request.
* - Either
*
* - execute the request using {@link ClientHttpRequestExecution#execute(HttpRequest, byte[])},
* or
* - do not execute the request to block the execution altogether.
*
* - Optionally wrap the response to filter HTTP attributes.
*
*
* @param request the request, containing method, URI, and headers
* @param body the body of the request
* @param execution the request execution
* @return the response
* @exception IOException in case of I/O errors
*/
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException
{
HttpRequestLog requestLog = (HttpRequestLog) ThreadLocalUtil.get(HttpRequestLog.REQUEST_LOG);
if (null != requestLog)
{
requestLog.setMethod(request.getMethod().name());
requestLog.setHeader(JSONObject.toJSONString(this.filtrationHeaders(request.getHeaders())));
requestLog.setRequestTime(new Timestamp(System.currentTimeMillis()));
}
ClientHttpResponse response = execution.execute(request, body);
if (null != requestLog)
{
requestLog.setHttpStatus(response.getStatusCode().value());
requestLog.setResponseTime(new Timestamp(System.currentTimeMillis()));
}
return response;
}
private Map filtrationHeaders(HttpHeaders httpHeaders)
{
Map logMap = null;
if (null != httpHeaders)
{
logMap = new HashMap<>();
Map> headerMap = (Map>) JSONArray.toJSON(httpHeaders);
for (Map.Entry> stringListEntry : headerMap.entrySet())
{
if (StringUtils.isNotBlank(stringListEntry.getKey()) && CollectionUtils
.isNotEmpty(stringListEntry.getValue()))
{
logMap.put(stringListEntry.getKey(), stringListEntry.getValue());
}
}
}
return logMap;
}
}
================================================
FILE: SpringBoot-Custom-Rest-Starter/SpringBoot-Custom-Rest-Autconfigure/src/main/java/com/xiao/custom/rest/starter/autoconfigure/log/annotation/RequestLog.java
================================================
package com.xiao.custom.rest.starter.autoconfigure.log.annotation;
import java.lang.annotation.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* [简要描述]: 请HTTP求日志注解
* [详细描述]:
* Retention 注解会在class字节码文件中存在,在运行时可以通过反射获取到
* Inherited 说明子类可以继承父类中的该注解
* Target 既可以在方法上,也可以在类上
* Documented说明该注解将被包含在javadoc中
*
* @author llxiao
* @version 1.0, 2019/4/24 11:40
* @since JDK 1.8
*/
@Retention(RUNTIME)
@Inherited
@Target(ElementType.METHOD)
@Documented
public @interface RequestLog
{
String value() default "";
}
================================================
FILE: SpringBoot-Custom-Rest-Starter/SpringBoot-Custom-Rest-Autconfigure/src/main/java/com/xiao/custom/rest/starter/autoconfigure/log/annotation/RequestLogAspect.java
================================================
package com.xiao.custom.rest.starter.autoconfigure.log.annotation;
import com.alibaba.fastjson.JSONObject;
import com.xiao.custom.rest.starter.autoconfigure.dto.Request;
import com.xiao.custom.rest.starter.autoconfigure.log.dto.HttpRequestLog;
import com.xiao.custom.rest.starter.autoconfigure.log.service.HttpRequestLogService;
import com.xiao.custom.rest.starter.autoconfigure.util.ThreadLocalUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.sql.Timestamp;
/**
* [简要描述]: 请求日志切面
* [详细描述]:
*
* @author llxiao
* @version 1.0, 2019/4/24 11:39
* @since JDK 1.8
*/
@Aspect
@Component
@Slf4j
public class RequestLogAspect
{
/**
* 请求响应最大长度
*/
private static final int MAX_PARAMS_LENGTH = 256;
/**
* 日志服务
*/
@Autowired(required = false)
private HttpRequestLogService httpRequestLogService;
/**
* [简要描述]:定义一个annotation切入点
* [详细描述]:切入点
* llxiao 2018/9/2 - 17:02
**/
@Pointcut("@annotation(com.xiao.custom.rest.starter.autoconfigure.log.annotation.RequestLog)")
public void logAnnotatison()
{
}
/**
* [简要描述]:around 切面强化
* [详细描述]:
*
* @param joinPoint :
* @return Object
* llxiao 2019/11/27 - 19:10
**/
@Around("logAnnotatison()")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable
{
HttpRequestLog requestLog = null;
Object retrunobj = null;
Object[] args = joinPoint.getArgs();
if (args.length > 0)
{
Object params = args[0];
if (params instanceof Request)
{
Request request = (Request) params;
Long requestId = request.getRequestId();
if (null == requestId)
{
requestLog = new HttpRequestLog();
requestLog.setCreateTime(new Timestamp(System.currentTimeMillis()));
requestLog.setRequest(subParams(JSONObject.toJSONString(request)));
requestLog.setUri(request.getUri());
requestLog.setParams(subParams(JSONObject.toJSONString(request.getParams())));
requestLog.setResponseType(request.getResponseType().getName());
}
ThreadLocalUtil.put(HttpRequestLog.REQUEST_LOG, requestLog);
}
}
try
{
retrunobj = joinPoint.proceed(args);
if (null != requestLog)
{
requestLog.setResponse(subParams(JSONObject.toJSONString(retrunobj)));
}
}
catch (Throwable e)
{
if (null != requestLog)
{
requestLog.setErrorMsg(e.getMessage());
}
log.error("Http 请求执行错误: ", e);
throw e;
}
finally
{
//删除当前线程保存数据,防止内存溢出
ThreadLocalUtil.remove();
if (null != httpRequestLogService)
{
httpRequestLogService.saveRequestLog(requestLog);
}
// else
// {
// log.info("Http 执行日志:{}", JSONObject.toJSONString(requestLog));
// }
}
return retrunobj;
}
/**
* [简要描述]:参数截取,参数太长超过2000直接用*号代替
* [详细描述]:
*
* @param toJsonString :
* @return java.lang.String
* llxiao 2019/8/8 - 11:43
**/
private String subParams(String toJsonString)
{
String params = "";
if (StringUtils.isNotEmpty(toJsonString))
{
if (toJsonString.length() > MAX_PARAMS_LENGTH)
{
params = toJsonString.substring(0, MAX_PARAMS_LENGTH);
}
else
{
params = toJsonString;
}
}
return params;
}
}
================================================
FILE: SpringBoot-Custom-Rest-Starter/SpringBoot-Custom-Rest-Autconfigure/src/main/java/com/xiao/custom/rest/starter/autoconfigure/log/dto/HttpRequestLog.java
================================================
package com.xiao.custom.rest.starter.autoconfigure.log.dto;
import lombok.Data;
import java.sql.Timestamp;
/**
* [简要描述]: 请求日志,以此来做请求补偿,请求日志记录等等
* [详细描述]:
*
* @author llxiao
* @version 1.0, 2019/4/24 10:00
* @since JDK 1.8
*/
@Data
public class HttpRequestLog
{
public static final String REQUEST_LOG = "HttpRequestLog";
/**
* 主键ID
*/
private Long id;
/**
* 请求Url
*/
private String uri;
/**
* 请求方式
*/
private String method;
/**
* JSON键值对header
*/
private String header;
/**
* 请求参数,JSON数据
*/
private String params;
/**
* 响应参数,JSON数据
*/
private String response;
/**
* 响应参数需要转换的类型
*/
private String responseType;
/**
* http状态
*/
private int httpStatus;
/**
* 请求最终状态
*/
private int status;
/**
* 尝试次数
*/
private int tryNum;
/**
* 整个请求request-JSON串
*/
private String request;
/**
* 错误消息
*/
private String errorMsg;
/**
* 请求时间
*/
private Timestamp requestTime;
/**
* 响应时间
*/
private Timestamp responseTime;
private Timestamp createTime;
private Timestamp updateTime;
}
================================================
FILE: SpringBoot-Custom-Rest-Starter/SpringBoot-Custom-Rest-Autconfigure/src/main/java/com/xiao/custom/rest/starter/autoconfigure/log/service/HttpRequestLogService.java
================================================
package com.xiao.custom.rest.starter.autoconfigure.log.service;
import com.xiao.custom.rest.starter.autoconfigure.log.dto.HttpRequestLog;
/**
* [简要描述]: http 请求日志记录
* [详细描述]:
*
* @author llxiao
* @version 1.0, 2019/11/29 15:33
* @since JDK 1.8
*/
public interface HttpRequestLogService
{
/**
* [简要描述]:保存日志信息
* [详细描述]:
*
* @param requestLog :
* llxiao 2019/4/24 - 14:42
**/
void saveRequestLog(HttpRequestLog requestLog);
}
================================================
FILE: SpringBoot-Custom-Rest-Starter/SpringBoot-Custom-Rest-Autconfigure/src/main/java/com/xiao/custom/rest/starter/autoconfigure/service/HttpClientService.java
================================================
package com.xiao.custom.rest.starter.autoconfigure.service;
import com.xiao.custom.rest.starter.autoconfigure.dto.Request;
import org.springframework.http.ResponseEntity;
/**
* [简要描述]: http服务
* [详细描述]:
*
* @author llxiao
* @version 1.0, 2019/4/23 19:53
* @since JDK 1.8
*/
public interface HttpClientService
{
/**
* [简要描述]:发起post请求
* [详细描述]:
*
* @param request : 请求参数
* @return T
* llxiao 2019/4/23 - 19:56
**/
T doForObject(Request request);
T doRequest(Request request);
T getForObject(Request request);
/**
* [简要描述]:formdata 获取请求Response
* [详细描述]:
*
* @param request :
* @return org.springframework.http.ResponseEntity
* llxiao 2019/8/26 - 16:33
**/
ResponseEntity postFormData(Request request);
}
================================================
FILE: SpringBoot-Custom-Rest-Starter/SpringBoot-Custom-Rest-Autconfigure/src/main/java/com/xiao/custom/rest/starter/autoconfigure/service/impl/HttpClientAsyncServiceImpl.java
================================================
package com.xiao.custom.rest.starter.autoconfigure.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.xiao.custom.rest.starter.autoconfigure.dto.Request;
import com.xiao.custom.rest.starter.autoconfigure.log.annotation.RequestLog;
import com.xiao.custom.rest.starter.autoconfigure.service.HttpClientService;
import com.xiao.custom.rest.starter.autoconfigure.util.RequestValidatorParamsUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
/**
* [简要描述]: CompletableFuture实现http异步服务
* [详细描述]:
*
* @author llxiao
* @version 1.0, 2019/4/24 09:25
* @since JDK 1.8
*/
@Slf4j
public class HttpClientAsyncServiceImpl implements HttpClientService
{
private HttpClientServiceImpl httpClientService;
public HttpClientAsyncServiceImpl(HttpRetryService retryService)
{
this.httpClientService = new HttpClientServiceImpl(retryService);
}
/**
* [简要描述]: 发起post请求
* [详细描述]: @Retryable默认重试 等待2000ms 3次
*
* @param request : 请求参数
* @return T
* llxiao 2019/4/23 - 19:56
**/
@Override
@RequestLog
public T doForObject(Request request)
{
if (RequestValidatorParamsUtil.validateParams(request))
{
return null;
}
CompletableFuture tCompletableFuture = CompletableFuture
.supplyAsync(() -> httpClientService.doForObject(request));
return futureResult(tCompletableFuture, request);
}
@Override
@RequestLog
public T doRequest(Request request)
{
if (RequestValidatorParamsUtil.validateParams(request))
{
return null;
}
CompletableFuture tCompletableFuture = CompletableFuture
.supplyAsync(() -> httpClientService.doRequest(request));
return futureResult(tCompletableFuture, request);
}
/**
* [简要描述]: 不进行encode编码的get请求
* [详细描述]: 请求参数中的url必须进行手动encode编码
*
* @param request : 请求参数
* @return T
* mjye 2019/10/23 - 11:32
**/
@Override
@RequestLog
public T getForObject(Request request)
{
if (RequestValidatorParamsUtil.validateParams(request))
{
return null;
}
CompletableFuture future = CompletableFuture.supplyAsync(() -> httpClientService.doForObject(request));
return futureResult(future, request);
}
/**
* [简要描述]:formdata 获取请求Response
* [详细描述]:
*
* @param request :
* @return org.springframework.http.ResponseEntity
* llxiao 2019/8/26 - 16:33
**/
@Override
@RequestLog
public ResponseEntity postFormData(Request request)
{
if (RequestValidatorParamsUtil.validateParams(request))
{
return null;
}
CompletableFuture> completableFuture = CompletableFuture
.supplyAsync(() -> httpClientService.postFormData(request));
return futureResult(completableFuture, request);
}
private T futureResult(CompletableFuture tCompletableFuture, Request request)
{
try
{
return tCompletableFuture.get();
}
catch (InterruptedException e)
{
log.error("请求参数:{}", JSONObject.toJSONString(request));
log.error("Http异步请求线程中断:", e);
}
catch (ExecutionException e)
{
log.error("请求参数:{}", JSONObject.toJSONString(request));
log.error("Http异步请求异常:", e);
}
return null;
}
}
================================================
FILE: SpringBoot-Custom-Rest-Starter/SpringBoot-Custom-Rest-Autconfigure/src/main/java/com/xiao/custom/rest/starter/autoconfigure/service/impl/HttpClientServiceImpl.java
================================================
package com.xiao.custom.rest.starter.autoconfigure.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.xiao.custom.rest.starter.autoconfigure.dto.Request;
import com.xiao.custom.rest.starter.autoconfigure.log.annotation.RequestLog;
import com.xiao.custom.rest.starter.autoconfigure.service.HttpClientService;
import com.xiao.custom.rest.starter.autoconfigure.util.RequestValidatorParamsUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.util.Map;
/**
* [简要描述]: http同步阻塞服务
* [详细描述]:
*
* @author llxiao
* @version 1.0, 2019/4/24 09:25
* @since JDK 1.8
*/
@Slf4j
public class HttpClientServiceImpl implements HttpClientService
{
private static final String JSON_UTF_8 = "application/json; charset=UTF-8";
private static final String HEADER_ACCEPT = "Accept";
/**
* 带重试机制
*/
private HttpRetryService retryService;
public HttpClientServiceImpl(HttpRetryService retryService)
{
this.retryService = retryService;
}
/**
* [简要描述]: 发起post请求
* [详细描述]: @Retryable默认重试 等待1000ms 3次
*
* @param request : 请求参数
* @return T
* llxiao 2019/4/23 - 19:56
**/
@Override
@RequestLog
public T doForObject(Request request)
{
if (RequestValidatorParamsUtil.validateParams(request))
{
return null;
}
T entity = null;
if (null != request)
{
String uri = request.getUri();
Object params = request.getParams();
int method = request.getMethod();
Class responseType = request.getResponseType();
HttpHeaders headers = request.getHeaders();
Object uriVariables = request.getUriVariables();
if (Request.POST == method)
{
entity = retryService.postForObject(uri, params, responseType, uriVariables);
}
else if (Request.JSON == method)
{
if (null == headers)
{
headers = new HttpHeaders();
}
headers.setContentType(MediaType.parseMediaType(JSON_UTF_8));
headers.add(HEADER_ACCEPT, MediaType.APPLICATION_JSON.toString());
HttpEntity formEntity = new HttpEntity<>(JSONObject.toJSONString(params), headers);
entity = retryService.postForObject(uri, formEntity, responseType, uriVariables);
}
else
{
log.error("当期请求暂不支持的操作,请求参数:{}", JSONObject.toJSONString(request));
}
}
return entity;
}
/**
* [简要描述]:普通HTTP请求
* [详细描述]:喆道对接在使用
*
* @param request :
* @return T
* xiaolinlin 2020/1/16 - 18:38
**/
@Override
@RequestLog
public T doRequest(Request request)
{
if (RequestValidatorParamsUtil.validateParams(request))
{
return null;
}
return retryService.doRequest(request.getUri(), request.getParams(), request.getResponseType());
}
/**
* [简要描述]: 不进行encode编码的get请求
* [详细描述]: 请求参数中的url必须进行手动encode编码
*
* @param request : 请求参数
* @return T
* mjye 2019/10/23 - 11:32
**/
@Override
@RequestLog
public T getForObject(Request request)
{
if (RequestValidatorParamsUtil.validateParams(request))
{
return null;
}
return retryService.getForObject(request.getUri(), request.getHeaders(), request.getResponseType());
}
/**
* [简要描述]:formdata 获取请求Response
* [详细描述]:
*
* @param request :
* @return org.springframework.http.ResponseEntity
* llxiao 2019/8/26 - 16:33
**/
@Override
@RequestLog
public ResponseEntity postFormData(Request request)
{
if (RequestValidatorParamsUtil.validateParams(request))
{
return null;
}
HttpEntity httpEntity = null;
Object params = request.getParams();
// 请求参数为httpEntity直接发送请求
if (params instanceof HttpEntity)
{
httpEntity = (HttpEntity) params;
}
// 需要重新组装 HttpEntity
else if (params instanceof MultiValueMap)
{
MultiValueMap multiValueMap = (MultiValueMap) params;
httpEntity = new HttpEntity<>(multiValueMap, request.getHeaders());
}
else if (params instanceof Map)
{
Map parmasMap = (Map) params;
MultiValueMap multiValueMap = new LinkedMultiValueMap<>();
for (Map.Entry entry : parmasMap.entrySet())
{
multiValueMap.add(entry.getKey(), entry.getValue());
}
httpEntity = new HttpEntity<>(multiValueMap, request.getHeaders());
}
else
{
log.error("当亲请求暂不支持的操作请求,请求数据:{}", JSONObject.toJSONString(request));
}
if (null != httpEntity)
{
return retryService.postFormData(httpEntity, request.getUri(), request.getResponseType());
}
else
{
log.error("请求异常,无法识别请求数据,请求数据:{}", JSONObject.toJSONString(request));
return null;
}
}
}
================================================
FILE: SpringBoot-Custom-Rest-Starter/SpringBoot-Custom-Rest-Autconfigure/src/main/java/com/xiao/custom/rest/starter/autoconfigure/service/impl/HttpRetryService.java
================================================
package com.xiao.custom.rest.starter.autoconfigure.service.impl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.SocketTimeoutException;
/**
* [简要描述]: 重试不能使用接口实现类
* [详细描述]:
* 重试机制注解说明(https://blog.csdn.net/u011116672/article/details/77823867):
* EnableRetry注解:
* 能否重试,proxyTargetClass属性为true时(默认false),使用CGLIB代理
*
* Retryable注解:注解需要被重试的方法
* include 指定处理的异常类。默认为空
* exclude指定不需要处理的异常。默认为空
* vaue指定要重试的异常。默认为空
* maxAttempts 最大重试次数。默认3次
* backoff 重试等待策略。默认使用@Backoff注解
*
* Backoff注解:重试回退策略(立即重试还是等待一会再重试)
* 不设置参数时,默认使用FixedBackOffPolicy,重试等待1000ms
* 只设置delay()属性时,使用FixedBackOffPolicy,重试等待指定的毫秒数
* 当设置delay()和maxDealy()属性时,重试等待在这两个值之间均态分布
* 使用delay(),maxDealy()和multiplier()属性时,使用ExponentialBackOffPolicy
* 当设置multiplier()属性不等于0时,同时也设置了random()属性时,使用ExponentialRandomBackOffPolicy
*
* Recover注解: 用于方法。
* 用于@Retryable失败时的“兜底”处理方法。 @Recover注释的方法必须要与@Retryable注解的方法“签名”保持一致,第一入参为要重试的异常,其他参数与@Retryable保持一致,返回值也要一样,否则无法执行!
*
* CircuitBreaker注解:用于方法,实现熔断模式。
* include 指定处理的异常类。默认为空
* exclude指定不需要处理的异常。默认为空
* vaue指定要重试的异常。默认为空
* maxAttempts 最大重试次数。默认3次
* openTimeout 配置熔断器打开的超时时间,默认5s,当超过openTimeout之后熔断器电路变成半打开状态(只要有一次重试成功,则闭合电路)
* resetTimeout 配置熔断器重新闭合的超时时间,默认20s,超过这个时间断路器关闭
*
* @author xiaolinlin
* @version 1.0, 2020/1/16 17:45
* @since JDK 1.8
*/
public class HttpRetryService
{
private RestTemplate restTemplate;
public HttpRetryService(RestTemplate restTemplate)
{
this.restTemplate = restTemplate;
}
/**
* [简要描述]:发起post请求
* [详细描述]:
*
* @param uri :
* @param params :
* @param responseType :
* @param uriVariables :
* @return T
* xiaolinlin 2020/1/16 - 18:20
**/
@Retryable(value = SocketTimeoutException.class, maxAttempts = 3, backoff = @Backoff(delay = 2000, multiplier = 1.5))
public T postForObject(String uri, Object params, Class responseType, Object uriVariables)
{
if (StringUtils.isNotBlank(uri) && null != params && null != responseType)
{
ResponseEntity responseEntity = restTemplate.postForEntity(uri, params, responseType, uriVariables);
return null == responseEntity ? null : responseEntity.getBody();
}
return null;
}
/**
* [简要描述]:formdata 获取请求Response
* [详细描述]:
*
* @param httpEntity :
* @param uri :
* @param responseType :
* @return org.springframework.http.ResponseEntity
* xiaolinlin 2020/1/16 - 18:27
**/
@Retryable(value = SocketTimeoutException.class, maxAttempts = 3, backoff = @Backoff(delay = 2000, multiplier = 1.5))
public ResponseEntity postFormData(HttpEntity httpEntity, String uri, Class responseType)
{
if (null != httpEntity && StringUtils.isNotBlank(uri) && null != responseType)
{
return restTemplate.postForEntity(uri, httpEntity, responseType);
}
return null;
}
/**
* [简要描述]:不进行encode编码的get请求
* [详细描述]:
*
* @param uri :
* @param headers :
* @param responseType :
* @return T
* xiaolinlin 2020/1/16 - 18:33
**/
@Retryable(value = SocketTimeoutException.class, maxAttempts = 3, backoff = @Backoff(delay = 2000, multiplier = 1.5))
public T getForObject(String uri, HttpHeaders headers, Class responseType)
{
if (StringUtils.isNotBlank(uri) && null != responseType)
{
HttpEntity requestEntity = null;
if (null != headers)
{
requestEntity = new HttpEntity<>(null, headers);
}
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(uri);
ResponseEntity resEntity = restTemplate
.exchange(builder.build(true).toUri(), HttpMethod.GET, requestEntity, responseType);
return resEntity.getBody();
}
return null;
}
/**
* [简要描述]:普通post请求
* [详细描述]:
*
* @param uri :
* @param params :
* @param responseType :
* @return T
* xiaolinlin 2020/1/16 - 18:37
**/
@Retryable(value = SocketTimeoutException.class, maxAttempts = 3, backoff = @Backoff(delay = 2000, multiplier = 1.5))
public T doRequest(String uri, Object params, Class responseType)
{
if (StringUtils.isNotBlank(uri) && null != params && null != responseType)
{
return (T) restTemplate.postForObject(uri, params, responseType);
}
return null;
}
}
================================================
FILE: SpringBoot-Custom-Rest-Starter/SpringBoot-Custom-Rest-Autconfigure/src/main/java/com/xiao/custom/rest/starter/autoconfigure/util/RequestValidatorParamsUtil.java
================================================
package com.xiao.custom.rest.starter.autoconfigure.util;
import com.alibaba.fastjson.JSONObject;
import com.xiao.custom.rest.starter.autoconfigure.dto.Request;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
/**
* [简要描述]:
* [详细描述]:
*
* @author xiaolinlin
* @version 1.0, 2020/1/16 15:47
* @since JDK 1.8
*/
@Slf4j
public class RequestValidatorParamsUtil
{
public static boolean validateParams(Request request)
{
if (null == request || StringUtils.isEmpty(request.getUri()) || null == request.getResponseType())
{
log.error("请求参数不能为空:{}", null == request ? "Request is null!" : JSONObject.toJSONString(request));
return true;
}
return false;
}
}
================================================
FILE: SpringBoot-Custom-Rest-Starter/SpringBoot-Custom-Rest-Autconfigure/src/main/java/com/xiao/custom/rest/starter/autoconfigure/util/ThreadLocalUtil.java
================================================
package com.xiao.custom.rest.starter.autoconfigure.util;
import java.util.HashMap;
import java.util.Map;
/**
* [简要描述]: ThreadLocalUtil
* [详细描述]:
*
* @author llxiao
* @version 1.0, 2019/4/24 09:25
* @since JDK 1.8
*/
public class ThreadLocalUtil
{
private static final ThreadLocal