美文网首页
yun-cloud编码规范以及编写指导

yun-cloud编码规范以及编写指导

作者: 一笑yo | 来源:发表于2017-01-14 10:30 被阅读0次

    编码规范

    命名

    严格遵守java命名规范

    • 命名采用驼峰式
    • 变量名将拥有一定的含义,要通俗易懂,杜绝使用含糊不清的变量名称
    • 其他参照java官方要求进行编码

    注释

    必须提供注释说明具体的业务逻辑。可以自行创建intellij注释模板。方法包括,方法注释,类注释, 方法需要写明具体的用途以及注意事项。

    微服务命名

    项目中的业务服务需要根据具体的业务类型携带service,比如user-service, order-service...

    编码注意事项

    所有微服务将完成服务注册于发现,不同业务之间调用,进项使用REST进行内部链接,不需重复写实现逻辑。服务的拆分本着粗粒度的原则,尽量避免分布式事务。

    技术选型

    jdk

    本次选择java版本为1.8

    spring-cloud

    本次使用的spring-cloud版本为Camden.SR4

    服务编写指南

    项目结构初始化将完成基本配置的构建(开发人员了解相关知识即可,后续有时间再深入研究)

    • 配置服务器(configserver)
    • 服务注册器(discovery)
    • 熔断UI(monitor-dashboard)
    • 授权服务器 (auth-service)
    • 路由 (gateway)
    • mysql数据库 (mysql)
    • mongodb数据库(mongodb)

    服务的创建过程

    yun-cloud 项目属于maven项目。通过spring-init 创建一个spring-cloud 的pom 项目, 该项目作为根pom。微服务通过new->module->maven-next->service-name 进行maven子项目的构建,创建完成之后src和配置文件都没有,我们需要修改pom文件,完善pom信息,创建SpringApplication程序入口,创建bootstrap.yml 进行服务器配置文件的加载。步骤如图所示:

    Paste_Image.png Paste_Image.png Paste_Image.png Paste_Image.png Paste_Image.png
    • 完善pom信息
    Paste_Image.png Paste_Image.png Paste_Image.png
    • 创建程序入口
    QQ图片20170114011243.png
    • 创建 bootstrap.yml
    Paste_Image.png
    • 创建 服务.yml 比如 user-service.yml 放置到配置服务器(config)中。
    Paste_Image.png

    tips: bootstrap.yml 为配置文件引导程序,application为应用配置,我们更倾向于把应用相关的配置到application.yml中,比如mysql 等。

    配置服务器的连接

    配置服务器有两种连接方式:

    • 通过指定配置进行连接
    spring:
      application:
        name: statistics-service
      cloud:
        config:
          uri: http://config:8888
    
    • 通过eureka自动进行配置发现(自动发现的前提是config-server的ServerID = configserver,默认的配置服务器如果不填写application.name 默认为configserver)
    spring:
      cloud:
        config:
          discovery:
            enabled: true   
    

    开发人员可以根据自己需求任选一种方式进行连接。

    注册发现的配置

    Eureka服务器已经启动,我们需要配置注册到服务器,并开启注册发现。

    • 引入依赖 (默认我们在根pom中已经全局引入了starter-eureka)
    • 通过注解进行注册和发现 ,注解有两种方式,一种是 @EnableDiscoveryClient ,另一种是@EnableEurekaClient ,两者的区别是,discoveryClient实现了许多的服务发现方法,比如使用eureka, consul, zookeeper ,而eurekaClient则是netflix提供的。两者选择一个即可。
    • 修改配置bootstrap.yml ,指定注册服务器
    eureka:
      client:
        serviceUrl:
          defaultZone: http://discovery:8761/eureka/
    
    

    hystrix-dashboard 的配置

    目前,我们将不配置hystrix-dashboard。后期我们通过引入依赖和稍许修改配置文件就可以实现该功能。详细配置可以参考官方教程或者是提供的sample样例。

    授权服务器的配置和使用

    待更新。。。

    路由配置

    我们使用Zuul 作为代理路由服务。

    • Zuul 为唯一对外暴露的端口.

    mysql

    mysql 我们使用tutumcloud/mysql 提供的镜像文件进行构建.可以cd 到mysql 执行docker build -t lvshangke/mysql . 进行手动镜像的构建,也可以通过compose.yml 指定build build ./mysql ,之后可以通过docker命令编译镜像,也可以通过docker-compose build 进行编译。

    mongodb

    mongodb 我们使用tutumcloud/mongodb 提供的镜像进行构建,使用版本3.2,具体用法跟mysql类似,可以参考文档进行配置也可以参考demo进行使用。

    mybatis

    我们在拆分微服务的时候,首先考虑的问题是服务的颗粒度,设计方向是,尽量的粗粒度的设计,避免事务的集群访问,关于涉及到事务的跨主机问题如果无法避免,则使用JMS实现消息队列的处理方案.

    • 服务的粒度直接影响到了服务之间调用复杂度
    • 我们习惯使用jpa处理表关系,之后就进行快速的开发迭代.但是在spring-cloud中,我们并不会首选使用jpa,考虑到JPA涉及到表关系处理,A业务处理用户基本CURD,B业务中处理用户银行卡的CURD,如果使用JPA,则A,B表具有重复的表关系配置,User,Bank.因为1个用户有多个银行卡.这样的业务逻辑设计就过分的细粒度.然而一旦使用JPA,无法避免的即使相互依赖,导致上述说的重复工作,所以我们建议不是必须使用,则首先考虑使用mybatis.
    • 在开发中,我们维护一套JPA,主要用户核心业务和数据库结构初始化工作(也可以不用)
    • 开发人员编写的小业务,复杂查询等,建议使用mybatis来完成数据库操作
      参考资料: mybatis,mybatis-spring-boot-autoconfigure

    mybatis 依赖

    我们选择1.2.0版本.

    <dependency>
     <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.2.0</version>
    </dependency>
    

    mybatis 配置

    mybatis支持注解的配置方式,对于简单的服务可以使用该方式,但是我们更倾向于使用xml配置的方式,因为自由度更高,对于一些复杂查询,可以更加方便使用.两种方式都做一些介绍

    • 基于注解
      使用注解@Mapper.使用选择器 @Select demo

    • 基于xml配置文件
      需要配置mybatis.config-location demo

    tips: mybatis

    feign

    JMS

    待更新。。。

    docker

    我们使用docker作为服务的测试和生产环境.

    docker 开发环境

    docker 作为开发环境的要求以及常见问题:

    • 所有的微服务创建 src/main/docker/Dockerfile 文件
    • 所有的微服务中pom 中需要增加maven-docker-plugin插件,并进行统一的配置
    • 项目根目录创建docker-compose.yml 文件,进行服务编写.

    tips: 我们通过links 进行容器host关联,不过,由于容器启动顺序问题,导致有些服务器还需要重新启动才可以,通过使用version 2 中的depends_on 语法替代links, 虽然容器在初始化网络中可以按照顺序,不过,微服务中很多需要等待配置服务器启动之后才可以调用配置.容器起来了,应用还没有起来.通过compose.yml 启动以后,建议使用手动重新启动下,必须保证配置服务器在注册服务器上已经注册服务. 官方参考中给予了关于启动顺序的解决方案,depends_on,可以使用wait-for-it,因为我们基于服务自动发现,所以导致无法正确的监听,在上述服务器配置中,我们通过主动声明的方式显式的指定配置服务器,之后就可以通过wait-for-it command: ["./wait-for-it.sh","configserver:8888","-t","0","--strict","--","java","-jar","/app/user-service-1.0.jar"] 启动监控,那么,当配置服务器启动之后将会触发微服务进行启动.在开发的过程中可以降低服务发现的时间,默认的每次心跳为30',我们可以修改eureka.instance.lease-renewal-interval-in-seconds: 1 进行时间压缩,生产环境将还原该配置,使用系统默认的心跳时间

    开发常用操作

    • 我们通过使用命令进行快速的编译,通过使用 mvn clean install 进行镜像的快速构建
    • 镜像构建以后,我们通过docker-compose up -d进行启动
    • 常用的compose 一般有,stop.start,rm,up.
    • 对于局部改动的镜像,我们可以通过intellij maven工具进行可视化操作.
    • 开发中我们经常使用UI工具作为快速查看容器日志,CURD等操作.

    docker 生产环境

    调试环境

    我们在开发微服务时候,将频繁的修改config,假如频繁的重新打包镜像,发布服务,这样下来无形中就耽误了很多时间,

    • 本地安装docker 环境
    • 项目初始化 ,执行mvn clean install docker-compose up -d,初始化环境包括,配置服务器,服务发现,路由,数据库等.
    • 创建自己的微服务模块, 运行Application.因为所有的docker服务编排已经提供了基本的服务,并且对外提供了端口,默认开发环境中都映射到localhost,比如数据库等等. 我们在开发测试过程中,可以直接运行自己的微服务,查看服务是否有问题,及时进行修改.
    • 编写application.yml 放置在resources中,因为开发阶段需要频繁的修改该文件,一般情况下这个文件在配置服务器中,难免会造成时间的浪费,我们通过手动指定,待程序测试通过后,把该文件重命名为服务.yml,放置到config中.
    • 开发测试没有问题之后,把配置文件放置在配置仓库,docker-compose.yml 中进行自己开发服务模块的编写.
    • 最后提交到git.

    tips:在开发测试阶段,尽量的压缩 镜像构建的时间,避免其他服务的问题导致开发时间的延长,所以,建议大家编写服务前首先更新git,然后运行一次镜像,发布服务, 进行自己服务的开发工作, 对于原有服务的更新,我们建议注释服务编排中的该服务,本地启动待修改的程序进行开发测试,完成之后再进行修改以及代码提交.(对于服务依赖,我们也可以本地启动多个服务进行调试),通过本地运行可以避免构建,依赖造成的时间浪费.

    安全机制

    我们使用oauth2.0作为授权机制来完成集群的授权处理.具体的配置如下:

    1. 授权服务器
      1.1 引入依赖
      <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-security</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-oauth2</artifactId>
            </dependency>
    

    1.2 Application 中使用@EnableGlobalMethodSecurity(prePostEnabled = true) 开启全局的方法鉴权,比如使用@PreAuthorize
    1.3 编写授权服务器的安全验证配置

    @Configuration
    @EnableResourceServer
    public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .authorizeRequests().anyRequest().authenticated()
                .and()
                .csrf().disable()
                .httpBasic().disable().anonymous().disable();
        }
    
        @Override
        public void configure(WebSecurity web) throws Exception {
            web.ignoring().antMatchers("/info");
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication()
                    .withUser("user").password("123456").roles("USER").and()
                    .withUser("admin").password("123456").roles("USER","ADMIN");
        }
        @Override
        @Bean
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
    }
    

    安全配置规则默认为所有的连接都需要授权.这里使用默认的方式采用内存存储token,初始化测试用户的时候也是通过配置, 我们在开发中通过注入service,实现自定义鉴权处理,主要涉及到的有 UserDetailsService,UserDetails.还需要初始化一个authenticationManagerBean 注意这个 @Bean

    1.4 编写oauth2 配置

    @Configuration
    @EnableAuthorizationServer
    public class OAuthConfiguration extends AuthorizationServerConfigurerAdapter {
    
        private TokenStore tokenStore = new InMemoryTokenStore();
    
        @Autowired
        @Qualifier("authenticationManagerBean")
        private AuthenticationManager authenticationManager;
    
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                        .withClient("mobile")
                        .authorizedGrantTypes("refresh_token", "password")
                        .scopes("api")
                    .and()
                        .withClient("user-service")
                        .secret("123456")
                        .authorizedGrantTypes("client_credentials", "refresh_token")
                        .scopes("server");
        }
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints
                    .tokenStore(tokenStore)
                    .authenticationManager(authenticationManager);
        }
    
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            security
                    .tokenKeyAccess("permitAll()")
                    .checkTokenAccess("isAuthenticated()");
        }
    }
    
    

    oauth2的授权,需要有客户端链接完成请求, 这里分配了多个客户端链接,默认我们使用mobile作为移动端api的调用请求(外部), 其他客户端作为 服务之间进行鉴权的链接(内部). 外部的鉴权方式,我们采用password方式,方便api调用,内部采用client_credentials的方式进行鉴权. token我们暂时使用内存token,后续再进行redis的迁移. 这里我们需要忽略的链接可以配置到config(WebSecurity web)中.

    1.5 配置文件的修改

    server:
      context-path: /uaa
    

    默认所有的内部微服务都有context-path 假如外部方位 /uaa/test 通过zuul 代理之后,就到了 service/uaa/test.在进行spring-security开发的过程中,我们一般需要开启日志

    logging:
      level:
        org.springframework.cloud: DEBUG
        org.springframework.security: DEBUG
    

    还有在开发过程中,一旦开启了DEBUG,需要控制下eureka心跳时间,要不然日志特别多,不好定位问题.

    1.6 配置zuul 代理
    因为文档上面说application 加上@EnableOAuth2Sso @EnableZuulProxy 就可以开启token转发,我们就按要求配置下, 实际情况是 好像没什么用.
    作为对外的zuul ,我们这里可以按照上面1.3的方式给他单独配置一个安全过滤器,不过作为对外提供的端口,我们这里不做安全控制, 所有的校验机制交给鉴权服务器去处理.

      routes:
        auth-service:
            path: /uaa/**
            url: http://localhost:8084
    
        account-service:
            path: /user/**
            serviceId: user-service
    
    

    我们定义一个转发规则. zuul这里我们就配置spring-security了,我们开放这个微服务, 允许访问的连接进行代理配置即可

    1.7 微服务编写
    微服务相当于一个小应用, 这里可以配置微服务自己的安全机制,参考上面的1.3.进行自己服务的连接处理,可以忽略一些连接,也可以验证一些连接.我们先看配置,具体的流程下面再整理
    我们通过

    @SpringBootApplication
    @EnableOAuth2Client
    @EnableEurekaClient
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class UserApplication {
    
        public static void main(String [] args){
            SpringApplication.run(UserApplication.class, args);
        }
    
    

    我们按照上面的配置方法,开启全局方法验证,开启Oauth2Client. 之后编写自己服务的安全控制.

    @Configuration
    @EnableResourceServer
    public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .authorizeRequests().anyRequest().authenticated()
                .and()
                .csrf().disable()
                .httpBasic().disable();
        }
        @Override
        @Bean
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
    }
    

    这里测试使用全部需要授权才能访问. 假如用户已经获取到token, 访问该资源,该资源又要去鉴权服务器查看是否正确. 默认我们内部的访问为"server"通过客户端鉴权,查看token的合法性. 所以这里需要注入几个bean

    @Bean
        @ConfigurationProperties(prefix = "security.oauth2.client")
        public ClientCredentialsResourceDetails clientCredentialsResourceDetails() {
            return new ClientCredentialsResourceDetails();
        }
    
        @Bean
        public RequestInterceptor oauth2FeignRequestInterceptor(){
            return new OAuth2FeignRequestInterceptor(new DefaultOAuth2ClientContext(), clientCredentialsResourceDetails());
        }
    
        @Bean
        public OAuth2RestTemplate clientCredentialsRestTemplate() {
            return new OAuth2RestTemplate(clientCredentialsResourceDetails());
        }
    

    通过@Configuration进行注入.
    1.8 微服务的配置

    security:
      oauth2:
        client:
          clientId: user-service
          clientSecret: 123456
          accessTokenUri: http://localhost:8084/uaa/oauth/token
          grant-type: client_credentials
          scope: server
        resource:
          user-info-uri: http://localhost:8084/uaa/me
    

    注意user-info-uri,当用户实际已经获取token,则访问该资源,该资源会去查看是否已经被鉴权服务器认证, 我们在auth-service 中需要创建一个controller

    @RequestMapping("/me")
        public Principal getCurrentLoggedInUser(Principal user) {
            return user;
        }
    

    1.9 使用
    我们开放了zuul作为对外暴露的端口, 当用户通过zuul访问资源时候,每个资源都属于一个微服务,每个微服务都是一个spring项目,都有自己的一套验证机制,是否需要鉴权呀? 如果需要, 则用户没有权利就收到反馈结果, 用户需要申请一个token

    申请token: localhost:8080/uaa/oauth/token
    请求类型: POST
    参数: username ,password, grant_type, scope(选填)
    请求头: Authorization: Basic bW9iaWxlOg== (这里的Basic 后面跟的是mobile:的base64编码方式,代表请求的客户端为mobile,密码为空)
    

    用户一旦申请了token 在遇到一些需要授权的连接的时候就可以携带该token

    请求: localhost/user/
    类型: GET
    请求头: Authorization: Bearer $token
    

    我们可以通过log日志查看整体的流程, 通过token访问zuul, zuul转发到对应的微服务,是否有安全配置, 一层层过滤器下来,用户通过userinfouri访问鉴权服务器,(携带client: user-service,password:,scope:) ,鉴权服务器查看client是否合法,完成鉴权.

    jenkins自动化构建

    监控系统

    相关文章

      网友评论

          本文标题:yun-cloud编码规范以及编写指导

          本文链接:https://www.haomeiwen.com/subject/lgbsbttx.html