一、Piggy Metrics介绍
PiggyMetrics是一个模仿的个人记账理财的运用,原作者称其为一个端到端的微服务PoC(Proof of Concept),也就是说他开发这个是为了验证微服务架构和Spring Cloud技能栈。PiggyMetrics现在在github上有超越12k星,是学习微服务架构和Spring Cloud技能栈的一个不错参阅。
项目工程结构
│
├─account-service # 账户服务
│ │ Dockerfile
│ │ pom.xml
│ │
│ └─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─piggymetrics
│ │ │ └─account
│ │ │ │ AccountApplication.java
│ │ │ ├─client
│ │ │ │ AuthServiceClient.java
│ │ │ │ StatisticsServiceClient.java
│ │ │ │ StatisticsServiceClientFallback.java
│ │ │ ├─config
│ │ │ │ ResourceServerConfig.java
│ │ │ │
│ │ │ ├─controller
│ │ │ │ AccountController.java
│ │ │ │ ErrorHandler.java
│ │ │ │
│ │ │ ├─domain
│ │ │ │ Account.java
│ │ │ │ Currency.java
│ │ │ │ Item.java
│ │ │ │ Saving.java
│ │ │ │ TimePeriod.java
│ │ │ │ User.java
│ │ │ │
│ │ │ ├─repository
│ │ │ │ AccountRepository.java
│ │ │ └─service
│ │ │ │ AccountService.java
│ │ │ │ AccountServiceImpl.java
│ │ │ │
│ │ │ └─security
│ │ │ CustomUserInfoTokenServices.java
│ │ │
│ │ └─resources
│ │ bootstrap.yml
│
├─auth-service # 授权认证服务
│ │ Dockerfile
│ │ pom.xml
│ │
│ └─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─piggymetrics
│ │ │ └─auth
│ │ │ │ AuthApplication.java
│ │ │ ├─config
│ │ │ │ OAuth2AuthorizationConfig.java
│ │ │ │ WebSecurityConfig.java
│ │ │ ├─controller
│ │ │ │ UserController.java
│ │ │ ├─domain
│ │ │ │ User.java
│ │ │ ├─repository
│ │ │ │ UserRepository.java
│ │ │ └─service
│ │ │ │ UserService.java
│ │ │ │ UserServiceImpl.java
│ │ │ └─security
│ │ │ MongoUserDetailsService.java
│ │ │
│ │ └─resources
│ │ bootstrap.yml
│ │
│ └─
├─config # 装备中心
│ │ Dockerfile
│ │ pom.xml
│ │
│ └─src
│ └─main
│ ├─java
│ │ └─com
│ │ └─piggymetrics
│ │ └─config
│ │ ConfigApplication.java
│ │ SecurityConfig.java
│ │
│ └─resources
│ │ application.yml
│ │
│ └─shared
│ account-service.yml
│ application.yml
│ auth-service.yml
│ gateway.yml
│ monitoring.yml
│ notification-service.yml
│ registry.yml
│ statistics-service.yml
│ turbine-stream-service.yml
│
├─gateway # API网关
│ │ Dockerfile
│ │ pom.xml
│ │
│ └─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─piggymetrics
│ │ │ └─gateway
│ │ │ GatewayApplication.java
│ │ │
│ │ └─resources
│
├─monitoring
│ │ Dockerfile
│ │ pom.xml
│ │
│ └─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─piggymetrics
│ │ │ └─monitoring
│ │ │ MonitoringApplication.java
│ │ │
│ │ └─resources
│ │ bootstrap.yml
│ │
│ └─test
│ ├─java
│ │ └─com
│ │ └─piggymetrics
│ │ └─monitoring
│ │ MonitoringApplicationTests.java
│ │
│ └─resources
│ bootstrap.yml
│
├─notification-service # 告诉服务
│ │ Dockerfile
│ │ pom.xml
│ │
│ └─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─piggymetrics
│ │ │ └─notification
│ │ │ │ NotificationServiceApplication.java
│ │ │ │
│ │ │ ├─client
│ │ │ │ AccountServiceClient.java
│ │ │ │
│ │ │ ├─config
│ │ │ │ ResourceServerConfig.java
│ │ │ │
│ │ │ ├─controller
│ │ │ │ RecipientController.java
│ │ │ │
│ │ │ ├─domain
│ │ │ │ Frequency.java
│ │ │ │ NotificationSettings.java
│ │ │ │ NotificationType.java
│ │ │ │ Recipient.java
│ │ │ │
│ │ │ ├─repository
│ │ │ │ │ RecipientRepository.java
│ │ │ │ │
│ │ │ │ └─converter
│ │ │ │ FrequencyReaderConverter.java
│ │ │ │ FrequencyWriterConverter.java
│ │ │ │
│ │ │ └─service
│ │ │ EmailService.java
│ │ │ EmailServiceImpl.java
│ │ │ NotificationService.java
│ │ │ NotificationServiceImpl.java
│ │ │ RecipientService.java
│ │ │ RecipientServiceImpl.java
│ │ │
│ │ └─resources
│ │ bootstrap.yml
│ │
├─registry # 注册服务
│ │ Dockerfile
│ │ pom.xml
│ │
│ └─src
│ └─main
│ ├─java
│ │ └─com
│ │ └─piggymetrics
│ │ └─registry
│ │ RegistryApplication.java
│ │
│ └─resources
│ bootstrap.yml
│
├─statistics-service # 核算服务
│ │ Dockerfile
│ │ pom.xml
│ │
│ └─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─piggymetrics
│ │ │ └─statistics
│ │ │ │ StatisticsApplication.java
│ │ │ │
│ │ │ ├─client
│ │ │ │ ExchangeRatesClient.java
│ │ │ │ ExchangeRatesClientFallback.java
│ │ │ │
│ │ │ ├─config
│ │ │ │ ResourceServerConfig.java
│ │ │ │
│ │ │ ├─controller
│ │ │ │ StatisticsController.java
│ │ │ │
│ │ │ ├─domain
│ │ │ │ │ Account.java
│ │ │ │ │ Currency.java
│ │ │ │ │ ExchangeRatesContainer.java
│ │ │ │ │ Item.java
│ │ │ │ │ Saving.java
│ │ │ │ │ TimePeriod.java
│ │ │ │ │
│ │ │ │ └─timeseries
│ │ │ │ DataPoint.java
│ │ │ │ DataPointId.java
│ │ │ │ ItemMetric.java
│ │ │ │ StatisticMetric.java
│ │ │ │
│ │ │ ├─repository
│ │ │ │ │ DataPointRepository.java
│ │ │ │ │
│ │ │ │ └─converter
│ │ │ │ DataPointIdReaderConverter.java
│ │ │ │ DataPointIdWriterConverter.java
│ │ │ │
│ │ │ └─service
│ │ │ │ ExchangeRatesService.java
│ │ │ │ ExchangeRatesServiceImpl.java
│ │ │ │ StatisticsService.java
│ │ │ │ StatisticsServiceImpl.java
│ │ │ │
│ │ │ └─security
│ │ │ CustomUserInfoTokenServices.java
│ │ │
│ │ └─resources
│ │ bootstrap.yml
│
└─turbine-stream-service # turbine流服务
│ Dockerfile
│ pom.xml
│
└─src
├─main
│ ├─java
│ │ └─com
│ │ └─piggymetrics
│ │ └─turbine
│ │ TurbineStreamServiceApplication.java
│ │
│ └─resources
│ bootstrap.yml
└─
二、Piggy Metrics服务拆分
技能栈 | 技能栈选型 |
---|---|
API网关 | Spring Cloud Zuul |
负载均衡 | Eurake+Ribbon |
长途调用 | Feign |
限流熔断 | Hystrix |
服务注册与发现 | Spring Cloud Eurake |
授权认证 | Spring Cloud OAuth2 |
装备中心 | Spring Cloud Config&&Apollo |
分布式调用链监控 | Spring Cloud Sleuth |
链路追踪 | Zipkin |
音讯队列 | RabbitMQ |
日志监控 | ELK |
三、Piggy Metrics业架构规划
现在PiggMetrics选用前后别离架构,前端是单页SPA,后端选用依据Spring Cloud技能栈的微服务架构。
3.1 事务服务架构
Piggy Metrics
项目主要有三个服务,Account service 账户服务,存储用户账户和记账信息、Statistics service 核算服务,核算用户财务状况和核算信息、Notification service 告诉服务,存储告诉和备份等相关装备。
3.2 原根底服务架构
-
API网关: 依据
Spring Cloud Zuul
的网关,是调用后台API的聚合入口,完成反向路由和负载均衡(Eureka+Ribbon
)、限流熔断(Hystrix
)等功用。CLIENT
单页运用和ZUUL
网关暂住在一起,简化布置。 -
服务注册和发现: 依据
Spring Cloud Eureka
的服务注册中心。事务服务启动时经过Eureka
注册,网关调用后台服务经过Eureka
做服务发现,服务之间调用也经过Eureka
做服务发现。 -
授权认证服务: 依据
Spring Security OAuth2
的授权认证中心。客户端登录时经过AUTHSERVICE
获取拜访令牌(走用户名暗码形式)。服务之间调用也经过AUTHSERVICE
获取拜访令牌(走客户端形式)。令牌校验方法、各资源服务器去AUTHSERVICE
会集校验令牌。 -
装备服务: 依据
Spring Cloud Config
的装备中心,会集办理所有Spring
服务的装备文件。 -
分布式调用链: 依据
Spring Cloud Sleuth
的调用链监控。网关调用后台服务,服务之间调用,都选用Zipkin进行埋点和盯梢。 -
软负载和限流熔断: 依据
Spring Cloud Ribbon&Hystrix
,Zuul
调用后台服务,服务之间相互调用,都经过Ribbon
完成软负载,也经过Hystrix
完成熔断限流维护。 -
METRICS & DASHBOARD: 依据
Spring Cloud Turbine + Hystrix Dashboard
,对所有Hystrix
产生的Metrics
流进行聚合,并展现在Hystrix Dashboard
上。 -
日志监控: 选用
ELK
栈会集搜集和分析运用日志。
3.3 优化后的根底服务架构
上图是经过造后优化后的架构,浅蓝色标示的都归于根底服务,主要替换的组件如下:
-
授权认证服务:替换为运用第8模块为课程定制开发的
Gravitee OAuth2
服务器。 -
装备服务:替换为运用携程
Apollo
做一致装备中心,会集办理所有Spring
微服务的装备。 -
分布式调用链:替换为运用大众点评开源的
CAT
做调用链监控,从网关调后台服务,服务之间相互调用,都选用CAT
客户端进行埋点监控。CAT
埋点既演示运用拦截器(interceptor
)方法,也演示运用AOP
非侵入方法。 -
METRICS&ALERTING:网关和微服务都启用
Prometheus Metrics
端点,便于集成Prometheus
监控和告警。
其它组件,比如Zuul
网关、Eureka
服务发现、Ribbon
软负载、Hystrix
限流熔断,以及ELK
会集日志都同原架构,没有太大变化。
四、技能亮点
经过Piggy Metrics
项目能够从中学习到以下几点:
- 怎么运用
Apollo
会集办理Spring
运用的装备? - 网关会集验证令牌
token
怎么完成? - 依据
OAuth2
的注册登录和API
调用详细是怎么完成的? -
CAT
非侵入式埋点怎么做,怎么尽量减少事务研发直接运用CAT
进行埋点?
注册登录流程
上图展现PiggyMetrics的登录注册流程,简化流程如下:
- 客户端运用向后台建议注册恳求。
- 恳求经过网关反向路由到账户服务(
Account Svc
)。 - 账户服务先去授权认证服务(
Gravitee OAuth2
)创立一个用户(包含用户和暗码,这样后续才能够登录获取拜访令牌)。账户服务再保存新账户信息到本地MongoDB
数据库。 - 注册成功以后,客户运用向授权认证服务恳求拜访令牌(走用户名暗码形式),拿到令牌以后缓存本地
localstorage
。
服务调用流程
上图展现PiggyMetrics的API调用流程,简化流程如下:
- 客户端向后台服务建议API调用,调用时在HTTP授权头上带上拜访令牌。
- 网关截获API恳求,依据安全需求判断是否需求验令牌,假如需求,则向授权服务器建议令牌校验恳求。授权服务器校验令牌并回来有用型性信息,假如令牌有用,一起回来用户名等相关信息。网关再判断校验是否经过,假如经过,则
将用户名以HTTP HEADER方法向后台服务传递
,假如不经过,则直接报授权错到客户端。 - 资源服务器从
HTTP HEADER
恳求头获取用户名等信息,可经过用户名进一步查询用户相关信息,完成事务逻辑。
客户端调用后台服务,经过改造为网关会集校验令牌
方法,这样能够简化安全架构,即在企业内网,资源服务器端可直接获取用户名信息,不需求再到授权服务器做会集令牌校验。别的,服务之间的调用也改造为能够直接调用,不需求授权认证和令牌,这种做法也是许多一线企业实践落地的做法,即在出产环境中,内部服务之间调用不授权认证,这样能够简化服务的开发和布置,可是关于安全灵敏的服务要求做好出产网段阻隔(需运维合作)。
五、代码解读
项目选用Spring Security&OAuth2
完成用户认证授权,经过完成UserDetailsService
接口完成身份认证:
UserDetailsService
@Service
public class MongoUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository repository;
/**
* 依据用户名获取用户(用户的人物、权限等信息)
*
* @return UserDetails
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return repository.findById(username).orElseThrow(() -> new UsernameNotFoundException(username));
}
}
WebSecurityConfig装备类
security的WebSecurityConfigurerAdapter常用于装备任认证办理器装备、中心过滤器装备、安全过滤器链装备, HttpSecurity运用了builder的构建方法来灵敏指定拜访策略。
/**
* spring security装备
*
* @return UserDetails
*/
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 自界说身份认证逻辑
*/
@Autowired
private MongoUserDetailsService userDetailsService;
/**
* anyRequest | 匹配所有恳求途径
* access | SpringEl表达式成果为true时能够拜访
* anonymous | 匿名能够拜访
* denyAll | 用户不能拜访
* fullyAuthenticated | 用户完全认证能够拜访(非remember-me下自动登录)
* hasAnyAuthority | 假如有参数,参数表明权限,则其间任何一个权限能够拜访
* hasAnyRole | 假如有参数,参数表明人物,则其间任何一个人物能够拜访
* hasAuthority | 假如有参数,参数表明权限,则其权限能够拜访
* hasIpAddress | 假如有参数,参数表明IP地址,假如用户IP和参数匹配,则能够拜访
* hasRole | 假如有参数,参数表明人物,则其人物能够拜访
* permitAll | 用户能够任意拜访
* rememberMe | 答应经过remember-me登录的用户拜访
* authenticated | 用户登录后可拜访
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest()
.authenticated()
.and()
.csrf().disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(new BCryptPasswordEncoder());
}
/**
* 处理 无法直接注入 AuthenticationManager
*/
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
AuthorizationServerConfigurer
AuthorizationServerConfigurer
是装备OAuth2
授权装备服务的装备类接口,只需求添加@EnableAuthorizationServer
,Spring会完成自动注入,接口有三个方法,可完成客户端的装备、安全功用、以及各个Endpoint(端点)的相关装备。
public interface AuthorizationServerConfigurer {
// 装备授权服务器的安全性
void configure(AuthorizationServerSecurityConfigurer security) throws Exception;
//客户端信息装备
void configure(ClientDetailsServiceConfigurer clients) throws Exception;
//装备授权服务器端点的非安全功用,如令牌存储、令牌定制、用户批准和授权类型。默许情况下你不需求做任何事情,除非你需求暗码授权,在这种情况下你需求供给一个 {@link AuthenticationManager}。
void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception;
}
AuthorizationServerConfigurerAdapter
AuthorizationServerConfigurerAdapter继承了AuthorizationServerConfigurer接口
public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {
public AuthorizationServerConfigurerAdapter() {
}
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
}
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
}
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
}
}
PiggyMetrics界说OAuth2AuthorizationConfig
OAuth2AuthorizationConfig
是AuthorizationServerConfigurer
默许的完成类,它是Spring Security OAuth2
授权服务器的默许装备,当没有自界说装备时,将会运用此装备。
EnableAuthorizationServer
public class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
//保存 OAuth2 token ,有InMemoryTokenStore、JdbcTokenStore、JwkTokenStore、RedisTokenStore
private TokenStore tokenStore = new InMemoryTokenStore();
private final String NOOP_PASSWORD_ENCODE = "{noop}";
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Autowired
private MongoUserDetailsService userDetailsService;
@Autowired
private Environment env;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 在内存中创立一个Oauth2 Client
clients.inMemory()
.withClient("browser")
// 授权类型调集
.authorizedGrantTypes("refresh_token", "password")
.scopes("ui")
.and()
.withClient("account-service")
// 暗码
.secret(env.getProperty("ACCOUNT_SERVICE_PASSWORD"))
.authorizedGrantTypes("client_credentials", "refresh_token")
.scopes("server")
.and()
.withClient("statistics-service")
.secret(env.getProperty("STATISTICS_SERVICE_PASSWORD"))
.authorizedGrantTypes("client_credentials", "refresh_token")
.scopes("server")
.and()
.withClient("notification-service")
.secret(env.getProperty("NOTIFICATION_SERVICE_PASSWORD"))
.authorizedGrantTypes("client_credentials", "refresh_token")
// 授权规划
.scopes("server");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// token存储器
endpoints
.tokenStore(tokenStore)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.passwordEncoder(NoOpPasswordEncoder.getInstance());
}
}
Feign服务调用
Piggy Metrics在AccountService调用用户服务来创立用户,经过在账户服务界说FeignClient(AuthServiceClient)完成长途调用:
@FeignClient(name = "auth-service")
public interface AuthServiceClient {
@RequestMapping(method = RequestMethod.POST, value = "/uaa/users", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
void createUser(User user);
}
AuthService#UserController开放创立用户的API:
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping(value = "/current", method = RequestMethod.GET)
public Principal getUser(Principal principal) {
return principal;
}
@PreAuthorize("#oauth2.hasScope('server')")
@RequestMapping(method = RequestMethod.POST)
public void createUser(@Valid @RequestBody User user) {
userService.create(user);
}
}
六、Piggy Metrics扩展点
实践上这个架构进行出产化,仍需做出产化扩展,下面是一些或许的扩展点:
- 安全,选用网关会集令牌校验后,内部服务能够直接调用,不需求授权认证,但在出产环境中,特别是关于安全灵敏的服务,需求考虑安全增强,例如出产网段阻隔和IP白名单等机制。
-
CAT客户端进一步封装,事例演示中为了简化,运用一些手艺埋点,但在实践出产中,一般需求有独立结构团队对CAT客户端进行进一步封装,对常用根底组件(服务结构,数据拜访层,MVC结构,音讯系统,缓存系统等)进行会集埋点,并供给封装好的客户端(最好做到无侵入,可参阅
Spring Cloud Sleuth Starter
埋点方法),方便事务研发团队接入。基本上,结构层会集埋点以后,事务运用只需引进依赖即可,一般不需求再手艺埋点。 -
用户服务解耦,演示事例中,用户服务(包含用户数据库)和
Gravitee OAuth2
集成在一起,但实践企业中用户服务或许是独立不耦合的,Gravitee OAuth2
能够扩展集成独立用户服务,账户服务也能够集成对接独立用户服务。 - 前后别离布置,演示事例中,为简化布置,前端运用和网关住在一起,但在实践出产中,依据企业事务和团队规划,前端运用和后端微服务或许是完全别离布置的,详细做法可参阅波波的视频课程。
-
Gravitee OAuth2,别的
Gravitee OAuth2
自身也需求扩展 Gravitee OAuth2。