SpringCloud练习
入职培训阶段SpringCloud练习将系统拆分为服务注册中心、网关服务、用户服务、图片服务、积分服务和公共模块使用Eureka 完成服务注册与发现Spring Cloud Gateway 统一入口OpenFeign 完成服务之间的远程调用Resilience4j实现熔断降级限流seata实现分布式事务。项目仅关注微服务架构的练习不再展示业务代码。后续考虑将eureka和Resilience4j替换为nacos和sentinel。项目目录。配置两个photo服务用来测试负载均衡每个服务包含基本的增删改查其中user服务会调用photo服务来查询用户名下所有图片和最近三次上传的图片新增用户也会对应新增一条积分记录。photo服务中有一个点赞功能调用score服务给对应的用户增加积分。commen模块定义一些公共实体和统一返回响应。eureka-server注册中心核心依赖!--Web--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency!--EurekaClient--dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-server/artifactId/dependency启动类需加上EnableEurekaServer注解SpringBootApplicationEnableEurekaServerpublicclassEurekaServerApplication{publicstaticvoidmain(String[]args){SpringApplication.run(EurekaServerApplication.class,args);}}gateway网关核心依赖与配置uri: lb://user-servicelb表示开启网关负载均衡后边是服务名。dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-gateway/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependencyserver:port:8081spring:application:name:gateway cloud:gateway:routes:-id:user-service uri:lb://user-service predicates:-Path/user/** - id: photo-service uri: lb://photo-service predicates: - Path/photo/** - id: score-service uri: lb://score-service predicates: - Path/score/** eureka: client: service-url: defaultZone: http://localhost:8761/eureka/user服务核心依赖启动类需加上EnableFeignClients注解后续不再赘述。!--OpenFeign--dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId/dependency!--Web--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency!--EurekaClient--dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependencyyml配置对应eureka服务器的地址eureka:client:registry-fetch-interval-seconds:3serviceUrl:defaultZone:http://localhost:8761/eureka/远程调用降级openfeign自带负载均衡不再演示。name为调用的外部服务fallback用于指定降级提示类。FeignClient(namephoto-service,fallbackPhotoFeignFallback.class)publicinterfacePhotoFeign{GetMapping(/photo/getNearUserPhotoById/{id})Result?getNearUserPhotoById(PathVariable(id)Integerid);GetMapping(/photo/getAllPhotoByUserId/{id})Result?getAllPhotoByUserId(PathVariableIntegerid,RequestParamIntegerpageNum,RequestParamIntegerpageSize);}降级提示类ComponentpublicclassPhotoFeignFallbackimplementsPhotoFeign{OverridepublicResult?getNearUserPhotoById(Integerid){returnResult.error(图片服务暂时不可用);}OverridepublicResult?getAllPhotoByUserId(Integerid,IntegerpageNum,IntegerpageSize){returnResult.error(图片服务暂时不可用);}}分布式事务seata核心配置如下使用seata时需要在对应的数据库创建undolog表注意调用方和被调用方都需要undolog表。dependencygroupIdio.seata/groupIdartifactIdseata-spring-boot-starter/artifactIdversion1.8.0/version/dependencyCREATETABLEundo_log(idbigint(20)NOTNULLAUTO_INCREMENT,branch_idbigint(20)NOTNULL,xidvarchar(100)COLLATEutf8_unicode_ciNOTNULL,contextvarchar(128)COLLATEutf8_unicode_ciDEFAULTNULL,rollback_info longblob,log_statusint(11)NOTNULL,log_created datetimeDEFAULTNULL,log_modified datetimeDEFAULTNULL,extvarchar(100)COLLATEutf8_unicode_ciDEFAULTNULL,PRIMARYKEY(id),UNIQUEKEYux_undo_log(xid,branch_id))ENGINEInnoDBAUTO_INCREMENT2DEFAULTCHARSETutf8COLLATEutf8_unicode_ci新增用户时须额外新增一条积分记录由于用户与积分不属于同一服务应该使用seata分布式事务进行事务管理核心注解GlobalTransactionalOverrideGlobalTransactionalTransactionalpublicResult?addUser(Useruser){Stringusernameuser.getUsername();if(userRepository.getUserByUsername(username)!null){thrownewRuntimeException(该用户已存在);}user.setPassword(bCryptPasswordEncoder.encode(123456));user.setCreateTime(LocalDateTime.now());user.setUpdateTime(LocalDateTime.now());if(user.getPhone().length()!11){thrownewRuntimeException(手机号格式错误);}booleanflaguserRepository.addUser(user);if(!flag){thrownewRuntimeException(用户添加失败);}ScoreDtoscoreDtonewScoreDto();scoreDto.setScore(0);scoreDto.setUserId(user.getUserId());scoreFeign.addScore(scoreDto);returnResult.success(添加成功,user);}对应积分服务OverrideTransactionalpublicResultaddScore(ScoreDtoscore){score.setScore(0);booleanbscoreRepository.addScore(score);try{inti10/0;}catch(Exceptione){thrownewRuntimeException();}if(b){returnResult.success(增加积分记录成功);}returnResult.error(增加积分记录失败);}服务熔断与限流核心依赖由于resilience4j限流基于aop实现所以要引入aop相关依赖dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-circuitbreaker-resilience4j/artifactId/dependencyyml配置如下为方便测试参数设置不太合理仅供参考。resilience4j:ratelimiter:instances:getUserById:# 时间窗口内允许请求数量 limitForPeriod:2# 刷新周期 limitRefreshPeriod:10s # 限流等待时间 timeoutDuration:0s circuitbreaker:configs:default:slidingWindowType:COUNT_BASED# 最近请求次数 slidingWindowSize:4# 最低请求次数 minimumNumberOfCalls:2# 失败率 failureRateThreshold:50# 熔断器持续时间 waitDurationInOpenState:10s instances:PhotoFeign#getNearUserPhotoById(Integer):baseConfig:defaultPhotoFeign#getAllPhotoByUserId(Integer):baseConfig:default限流代码如下目前仅测试将限流方法放到controller后续可以统一写一个aop类用来管理限流。GetMapping(/getUserById/{id})RateLimiter(namegetUserById,fallbackMethodgetUserByIds)publicResult?getUserById(PathVariableIntegerid){returnuserService.getUserById(id);}publicResult?getUserByIds(Integerid,RequestNotPermittede){returnResult.error(请求过于频繁请稍后再试);}多次调用失败后可以看到控制台熔断器打开