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

编码规范

命名

严格遵守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自动化构建

监控系统

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,594评论 18 139
  • 原文:https://dzone.com/articles/microservice-architecture-w...
    Oopsguy阅读 1,801评论 1 15
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,733评论 6 342
  • 没有最喜欢的颜色 没有最喜欢的衣服 但有最快乐的我 追着蝴蝶 跑遍开满小野菊的田野 外婆在种玉米或者红薯 打着火把...
    轻音袅袅阅读 148评论 0 1
  • 我又一次打破个规矩,本来都是没晚写的东西,结果又第二天早上写,真的是,我昨天是我放假以来最闲的一天,但是我却觉得我...
    包逗豆DoubleD阅读 200评论 0 0