优秀开源项目解读(六) - Piggy Metrics微服务项目

一、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微服务项目

Piggy Metrics项目主要有三个服务,Account service 账户服务,存储用户账户和记账信息、Statistics service 核算服务,核算用户财务状况和核算信息、Notification service 告诉服务,存储告诉和备份等相关装备。

3.2 原根底服务架构

优秀开源项目解读(六) - Piggy Metrics微服务项目

  • 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&HystrixZuul调用后台服务,服务之间相互调用,都经过Ribbon完成软负载,也经过Hystrix完成熔断限流维护。
  • METRICS & DASHBOARD: 依据Spring Cloud Turbine + Hystrix Dashboard,对所有Hystrix产生的Metrics流进行聚合,并展现在Hystrix Dashboard上。
  • 日志监控: 选用ELK栈会集搜集和分析运用日志。

3.3 优化后的根底服务架构

优秀开源项目解读(六) - Piggy Metrics微服务项目

上图是经过造后优化后的架构,浅蓝色标示的都归于根底服务,主要替换的组件如下:

  1. 授权认证服务:替换为运用第8模块为课程定制开发的Gravitee OAuth2服务器。
  2. 装备服务:替换为运用携程Apollo做一致装备中心,会集办理所有Spring微服务的装备。
  3. 分布式调用链:替换为运用大众点评开源的CAT做调用链监控,从网关调后台服务,服务之间相互调用,都选用CAT客户端进行埋点监控。CAT埋点既演示运用拦截器(interceptor)方法,也演示运用AOP非侵入方法。
  4. METRICS&ALERTING:网关和微服务都启用Prometheus Metrics端点,便于集成Prometheus监控和告警。

其它组件,比如Zuul网关、Eureka服务发现、Ribbon软负载、Hystrix限流熔断,以及ELK会集日志都同原架构,没有太大变化。

四、技能亮点

经过Piggy Metrics项目能够从中学习到以下几点:

  1. 怎么运用Apollo会集办理Spring运用的装备?
  2. 网关会集验证令牌token怎么完成?
  3. 依据OAuth2的注册登录和API调用详细是怎么完成的?
  4. CAT非侵入式埋点怎么做,怎么尽量减少事务研发直接运用CAT进行埋点?

注册登录流程

优秀开源项目解读(六) - Piggy Metrics微服务项目

上图展现PiggyMetrics的登录注册流程,简化流程如下:

  1. 客户端运用向后台建议注册恳求。
  2. 恳求经过网关反向路由到账户服务(Account Svc)。
  3. 账户服务先去授权认证服务(Gravitee OAuth2)创立一个用户(包含用户和暗码,这样后续才能够登录获取拜访令牌)。账户服务再保存新账户信息到本地MongoDB数据库。
  4. 注册成功以后,客户运用向授权认证服务恳求拜访令牌(走用户名暗码形式),拿到令牌以后缓存本地localstorage

服务调用流程

优秀开源项目解读(六) - Piggy Metrics微服务项目

上图展现PiggyMetrics的API调用流程,简化流程如下:

  1. 客户端向后台服务建议API调用,调用时在HTTP授权头上带上拜访令牌。
  2. 网关截获API恳求,依据安全需求判断是否需求验令牌,假如需求,则向授权服务器建议令牌校验恳求。授权服务器校验令牌并回来有用型性信息,假如令牌有用,一起回来用户名等相关信息。网关再判断校验是否经过,假如经过,则将用户名以HTTP HEADER方法向后台服务传递,假如不经过,则直接报授权错到客户端。
  3. 资源服务器从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

OAuth2AuthorizationConfigAuthorizationServerConfigurer默许的完成类,它是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扩展点

实践上这个架构进行出产化,仍需做出产化扩展,下面是一些或许的扩展点:

  1. 安全,选用网关会集令牌校验后,内部服务能够直接调用,不需求授权认证,但在出产环境中,特别是关于安全灵敏的服务,需求考虑安全增强,例如出产网段阻隔IP白名单等机制。
  2. CAT客户端进一步封装,事例演示中为了简化,运用一些手艺埋点,但在实践出产中,一般需求有独立结构团队对CAT客户端进行进一步封装,对常用根底组件(服务结构,数据拜访层,MVC结构,音讯系统,缓存系统等)进行会集埋点,并供给封装好的客户端(最好做到无侵入,可参阅Spring Cloud Sleuth Starter埋点方法),方便事务研发团队接入。基本上,结构层会集埋点以后,事务运用只需引进依赖即可,一般不需求再手艺埋点。
  3. 用户服务解耦,演示事例中,用户服务(包含用户数据库)和Gravitee OAuth2集成在一起,但实践企业中用户服务或许是独立不耦合的,Gravitee OAuth2能够扩展集成独立用户服务,账户服务也能够集成对接独立用户服务。
  4. 前后别离布置,演示事例中,为简化布置,前端运用和网关住在一起,但在实践出产中,依据企业事务和团队规划,前端运用和后端微服务或许是完全别离布置的,详细做法可参阅波波的视频课程。
  5. Gravitee OAuth2,别的Gravitee OAuth2自身也需求扩展 Gravitee OAuth2。