使用SpringCloud实战微服务

来源:互联网 发布:mac系统怎么装win7 编辑:程序博客网 时间:2024/05/08 08:28

一微服务架构概述

1.1 微服务特性以及优点

  • 每个服务可以独立运行在自己的进程里
  • 一系列独立运行的微服务(goods,order,pay,user,search…)共同构建了整个系统
  • 每个服务为独立的业务开发,一个微服务只关注某个特定的功能,例如用户管理,商品管理微服务
  • 微服务之间通过一些轻量级的通信机制进行通讯,例如通过Restful API进行调用
  • 技术栈不受限:可以使用不同的开发语言和数据存储技术
  • 全自动的部署机制
  • 按需伸缩:根据需求和应用场景,实现细粒度的水平扩展

1.2 微服务带来的挑战

  • 运维要求较高
  • 分布式的复杂性
  • 接口调整成本较高

1.3 微服务设计原则

  • 单一职责原则
  • 服务自治原则
  • 轻量级通讯机制
  • 微服务粒度

1.4 微服务开发框架

  • SpringCloud:众多组件构造完善的分布式系统
  • Dubbo/Dubbox:关注服务治理
  • Dropwizard:关注单个微服务开发

二 SpringCloud概述与开发环境

2.1 SpringCloud概述

SpringCloud是基于SpringBoot之上的用来快速构建微服务系统的工具集,拥有功能完善的轻量级微服务组件,例如服务治理(Eureka),声明式REST调用(Feign),客户端负载均衡(Ribbon),服务容错(Hystrix),服务网关(Zuul)以及服务配置(Spring Cloud Config),服务跟踪(Sleuth)等等。

官网链接:http://projects.spring.io/spring-cloud/ 
目前主流的版本为SpringBoot1.4.5.RELEAE和SpringCloudCamden.SR7, 
Maven pom配置如下:

<parent>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-parent</artifactId>    <version>1.4.5.RELEASE</version></parent><dependencyManagement>    <dependencies>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-dependencies</artifactId>            <version>Camden.SR7</version>            <type>pom</type>            <scope>import</scope>        </dependency>    </dependencies></dependencyManagement><dependencies>    <dependency>        <groupId></groupId>        <artifactId>spring-cloud-starter-config</artifactId>    </dependency>    <dependency>        <groupId></groupId>        <artifactId>spring-cloud-starter-eureka</artifactId>    </dependency></dependencies>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

2.2 开发环境

MacOS10.12+JDK8u131+IntelliJ IDEA2017.1.4

Tomcat8.5+Maven3.3.9+Git2.12+Firefox54

Spring4.3.9.RELEASE+SpringBoot1.4.5.RELEASE+SpringCloud Camden.SR7

三 工程机器模块说明

3.1 工程说明

工程首先自定义了Maven父工程,其中定义如下的公共组件:

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <!-- 自定义工程的maven坐标-->    <groupId>com.ekeyfund.springcloud</groupId>    <artifactId>springcloud-parent</artifactId>   <version>2.0.0-SNAPSHOT</version>    <packaging>pom</packaging>    <!-- 基于SpringBoot 1.4.5.RELEASE-->    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>1.4.5.RELEASE</version>        <relativePath/>    </parent>    <!-- 定义引用类库版本 -->    <properties>        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>        <java.version>1.8</java.version>        <spring-cloud.version>Camden.SR7</spring-cloud.version>        <springcloud-parent.version>2.0.0-SNAPSHOT</springcloud-parent.version>        <druid.version>1.0.31</druid.version>        <jackson.version>2.8.8</jackson.version>        <commons-lang3.version>3.5</commons-lang3.version>        <ehcache.version>3.1.4</ehcache.version>        <hibernate.version>5.0.12.Final</hibernate.version>        <servlet-api.version>3.1.0</servlet-api.version>        <commons-collection4.version>4.1</commons-collection4.version>        <springframework.oxm.version>4.3.9.RELEASE</springframework.oxm.version>    </properties>    <!-- 引入SpringCloud微服务常用组件-->    <modules>        <module>springcloud-eureka-server</module>        <module>springcloud-eureka-server-ha</module>        <module>springcloud-provider-user-service</module>        <module>springcloud-consumer-h5-ribbon-hystrix</module>        <module>springcloud-consumer-h5-feign</module>        <module>springcloud-api-gateway</module>        <module>springcloud-consumer-h5</module>        <module>springcloud-config-server</module>    </modules>    <dependencies>        <!-- 服务发现组件-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-eureka-server</artifactId>        </dependency>        <!-- 应用监控-->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-actuator</artifactId>        </dependency>        <!-- 应用测试-->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <scope>runtime</scope>        </dependency>        <dependency>            <groupId>com.alibaba</groupId>            <artifactId>druid</artifactId>            <version>${druid.version}</version>        </dependency>        <dependency>            <groupId>javax.servlet</groupId>            <artifactId>javax.servlet-api</artifactId>            <version>${servlet-api.version}</version>        </dependency>        <dependency>            <groupId>com.fasterxml.jackson.core</groupId>            <artifactId>jackson-annotations</artifactId>            <version>${jackson.version}</version>        </dependency>        <dependency>            <groupId>org.apache.commons</groupId>            <artifactId>commons-lang3</artifactId>            <version>${commons-lang3.version}</version>        </dependency>        <dependency>            <groupId>org.apache.commons</groupId>            <artifactId>commons-collections4</artifactId>            <version>${commons-collection4.version}</version>        </dependency>        <dependency>            <groupId>org.hibernate</groupId>            <artifactId>hibernate-ehcache</artifactId>            <version>${hibernate.version}</version>        </dependency>        <dependency>            <groupId>org.ehcache</groupId>            <artifactId>ehcache</artifactId>            <version>${ehcache.version}</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-oxm</artifactId>            <version>${springframework.oxm.version}</version>        </dependency>    </dependencies>    <dependencyManagement>        <dependencies>            <dependency>                <groupId>org.springframework.cloud</groupId>                <artifactId>spring-cloud-dependencies</artifactId>                <version>${spring-cloud.version}</version>                <type>pom</type>                <scope>import</scope>            </dependency>        </dependencies>    </dependencyManagement></project>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144

3.2 模块说明

模块则借助IntelliJ IDEA结合Spring Initializer自动生成工程结构,主要模块和说明如下:

模块名称模块说明访问地址springcloud-eureka-server分布式服务注册中心(单点)http://127.0.0.1:9999springcloud-eureka-server-ha分布式服务注册中心(高可用版本)http://127.0.0.1:9998http://127.0.0.1:9997springcloud-provider-user-service用户服务提供者http://127.0.0.1:9996/listhttp://127.0.0.1:9995/listspringcloud-consumer-h5用户服务调用者,采用原始的RestTemplate调用http://127.0.0.1:9991/user/get/4springcloud-consumer-h5-ribbon-hystrix用户服务调用者,采用ribbon做客户端负载均衡http://127.0.0.1:9994/springcloud-provider-user-servicespringcloud-consumer-h5-feignfeign声明式服务调用者http://127.0.0.1:9993/listspringcloud-gateway网关服务http://127.0.0.1:9992/api-a/listspringcloud-config-server配置中心待定

四 使用SpringBoot实现服务提供者

所属maven模块:springcloud-provider-user-service 
基于SpringBoot的Web和JPA模块实现Restful API的常用方法

4.1 entity

主要包含User,Role,Department三个实体

Role.java

package com.ekeyfund.springcloud.entity;import javax.persistence.*;import java.io.Serializable;/** * Role Entity * * @author Liuguanglei liuguanglei@ekeyfund.com * @create 2017-06-下午2:36 */@Entity@Table(name = "springboot_role")public class Role  implements Serializable{    @Id    @GeneratedValue(strategy = GenerationType.IDENTITY)    @Column(name = "role_id")    private Long id;    @Column(name = "role_name")    private String name;    public Long getId() {        return id;    }    public void setId(Long id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public String toString() {        return new org.apache.commons.lang3.builder.ToStringBuilder(this)                .append("id", id)                .append("name", name)                .toString();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

Department.java

package com.ekeyfund.springcloud.entity;import org.apache.commons.lang3.builder.ToStringBuilder;import org.hibernate.annotations.CacheConcurrencyStrategy;import javax.persistence.*;import java.io.Serializable;/** * Department Entity * * @author Liuguanglei liuguanglei@ekeyfund.com * @create 2017-06-下午2:31 */@Entity@Table(name = "springboot_department")@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)public class Department implements Serializable {    @Id    @GeneratedValue(strategy = GenerationType.IDENTITY)    @Column(name = "department_id")    private Long id;    @Column(name = "department_name")    private String name;    public Long getId() {        return id;    }    public void setId(Long id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public String toString() {        return new ToStringBuilder(this)                .append("id", id)                .append("name", name)                .toString();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

User.java

package com.ekeyfund.springcloud.entity;import com.fasterxml.jackson.annotation.JsonBackReference;import org.apache.commons.lang3.builder.ToStringBuilder;import org.springframework.format.annotation.DateTimeFormat;import javax.persistence.*;import java.io.Serializable;import java.util.Date;import java.util.List;/** * User Entity * * @author Liuguanglei liuguanglei@ekeyfund.com * @create 2017-06-下午2:32 */@Entity@Table(name = "springboot_user")public class User  implements Serializable{    @Id    @Column(name = "user_id")    @GeneratedValue(strategy = GenerationType.IDENTITY)    private Long id;    @Column(name = "user_name")    private String name;    @Column(name = "user_password")    private String password;    @Column(name = "user_create_date")    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")    private Date createDate;    @ManyToOne    @JoinColumn(name = "department_id")    @JsonBackReference    private Department department;    @ManyToMany(cascade = {},fetch = FetchType.EAGER)    @JoinTable(name = "springboot_user_role",joinColumns = {@JoinColumn(name="user_id")},                inverseJoinColumns = {@JoinColumn(name = "role_id")}    )    private List<Role> roleList;    public Long getId() {        return id;    }    public void setId(Long id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getPassword() {        return password;    }    public void setPassword(String password) {        this.password = password;    }    public Date getCreateDate() {        return createDate;    }    public void setCreateDate(Date createDate) {        this.createDate = createDate;    }    public Department getDepartment() {        return department;    }    public void setDepartment(Department department) {        this.department = department;    }    public List<Role> getRoleList() {        return roleList;    }    public void setRoleList(List<Role> roleList) {        this.roleList = roleList;    }    @Override    public String toString() {        return new ToStringBuilder(this)                .append("id", id)                .append("name", name)                .append("password", password)                .append("createDate", createDate)                .append("department", department)                .append("roleList", roleList)                .toString();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113

4.2 数据访问Repository

主要包含UserRepository和DepartmentRepository

DepartmentRepository.java

package com.ekeyfund.springcloud.repository;import com.ekeyfund.springcloud.entity.Department;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.stereotype.Repository;/** * Created by tony on 2017/6/19. */@Repositorypublic interface DepartmentRepository extends JpaRepository<Department,Long> {}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

UserRepoistory.java

package com.ekeyfund.springcloud.repository;import com.ekeyfund.springcloud.entity.User;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.stereotype.Repository;import java.util.Collection;import java.util.Date;import java.util.List;/** * User Repository * * @author Liuguanglei liuguanglei@ekeyfund.com * @create 2017-06-下午2:54 */@Repositorypublic interface UserRepository  extends JpaRepository<User,Long>{    /**     *  and     * @param id     * @param name     * @return     */    User findByIdAndName(Long id, String name);    User findByNameAndPassword(String name, String password);    /**     *  or     * @param id     * @param name     * @return     */    User findByIdOrName(Long id, String name);    /**     * between     * @param start     * @param end     * @return     */    List<User> findByCreateDateBetween(Date start, Date end);    /**     * lessThan     * @param start     * @return     */    List<User> getByCreateDateLessThan(Date start);    /**     * Greater Than     * @param start     * @return     */    List<User> findByCreateDateGreaterThan(Date start);    /**     * is null     * @return     */    List<User> findByNameIsNull();    /**     * in     * @param nameList     * @return     */    List<User> findByNameIn(Collection<String> nameList);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79

4.3 业务逻辑Service

主要包含UserService,DepartmentService及其实现

UserService.java

package com.ekeyfund.springcloud.service;import com.ekeyfund.springcloud.entity.User;import java.util.List;/** * Created by tony on 2017/6/19. */public interface UserService {    /**     * 登录     * @param name     * @param password     * @return     */    public User login(String name, String password);    /**     * 注册     * @param user     * @return     */    public User register(User user);    /**     * 注销     * @param user     * @return     */    void writeOff(User user);    /**     * 当前用户是否已经存在     * @param user     * @return     */    boolean isExists(User user);     List<User> getAllUser();     User getUserById(Long id);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

UserServiceImpl.java

package com.ekeyfund.springcloud.service.impl;import com.ekeyfund.springcloud.entity.User;import com.ekeyfund.springcloud.repository.UserRepository;import com.ekeyfund.springcloud.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import javax.transaction.Transactional;import java.util.List;/** * User Service Impl * * @author Liuguanglei liuguanglei@ekeyfund.com * @create 2017-06-下午3:34 */@Service@Transactionalpublic class UserServiceImpl implements UserService {    @Autowired    private UserRepository userRepository;    @Override    public User login(String name, String password) {        return userRepository.findByNameAndPassword(name,password);    }    @Override    public User register(User user) {        return userRepository.save(user);    }    @Override    public void writeOff(User user) {         userRepository.delete(user);    }    @Override    public boolean isExists(User user) {        return userRepository.findOne(user.getId())!=null?true:false;    }    @Override    public List<User> getAllUser() {        return userRepository.findAll();    }    @Override    public User getUserById(Long id) {        return userRepository.findOne(id);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

DepartmentService.java

package com.ekeyfund.springcloud.service;import com.ekeyfund.springcloud.entity.Department;/** * Created by tony on 2017/6/19. */public interface DepartmentService {     Department saveDepartment(Department department);     Department getDepartmentById(Long id);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

DepartmentServiceImpl.java

package com.ekeyfund.springcloud.service.impl;import com.ekeyfund.springcloud.entity.Department;import com.ekeyfund.springcloud.repository.DepartmentRepository;import com.ekeyfund.springcloud.service.DepartmentService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import javax.transaction.Transactional;/** * Department Impl * * @author Liuguanglei liuguanglei@ekeyfund.com * @create 2017-06-下午3:12 */@Transactional@Servicepublic class DepartmentImpl implements DepartmentService {    @Autowired    private DepartmentRepository departmentRepository;    @Override    public Department saveDepartment(Department department) {        return departmentRepository.save(department);    }    @Override    public Department getDepartmentById(Long id) {        return departmentRepository.findOne(id);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

4.4 Controller层

主要包含提供User完整的Restful API 的UserController

UserController.java

package com.ekeyfund.springcloud.controller;import com.ekeyfund.springcloud.entity.User;import com.ekeyfund.springcloud.service.UserService;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.springframework.web.bind.annotation.*;import java.util.Date;import java.util.List;/** * UserController * Restful API * @author Liuguanglei liuguanglei@ekeyfund.com * @create 2017-06-下午11:24 */@RestControllerpublic class UserController {    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(UserController.class);    @Autowired    private DiscoveryClient discoveryClient;    @Autowired    private UserService userService;    @GetMapping(value = "/list")   public List<User> list(){        ServiceInstance instance=discoveryClient.getLocalServiceInstance();        LOGGER.info("call user/list service  host is  "+instance.getHost()+"service_id is "+instance.getServiceId());        return userService.getAllUser();   }   @GetMapping(value = "/login")   public User login( @RequestParam String name,@RequestParam String password){       User user=userService.login(name,password);       return user;   }   @PostMapping("/register")   public String register(@ModelAttribute User user){       User result =userService.register(user);       return result!=null?"success":"fail";   }   @GetMapping("/get/{id}")   public User get(@PathVariable Long id){       return userService.getUserById(id);   }   @PutMapping("/update/{id}")   public String update(@PathVariable Long id,@ModelAttribute User user){       User updatedUser =userService.getUserById(id);       updatedUser.setName(user.getName());       updatedUser.setPassword(user.getPassword());       updatedUser.setCreateDate(new Date());       User result= userService.register(updatedUser);       return result!=null?"success":"fail";   }   @DeleteMapping("/delete/{id}")   public String delete(@PathVariable Long id){       User user =new User();       user.setId(id);       userService.writeOff(user);       return "success";   }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89

4.5 Configuration

主要包含数据源Druid和JPA的配置

DruidConfiguation.java

package com.ekeyfund.springcloud.configuration;import com.alibaba.druid.support.http.StatViewServlet;import com.alibaba.druid.support.http.WebStatFilter;import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.boot.web.servlet.ServletRegistrationBean;import org.springframework.context.annotation.Bean;/** * Druid Configuration * * @author Liuguanglei liuguanglei@ekeyfund.com * @create 2017-06-下午5:48 */public class DruidConfiguration {    @Bean    public ServletRegistrationBean statViewServle(){        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(),"/druid/*");        //白名单:        servletRegistrationBean.addInitParameter("allow","192.168.1.218,127.0.0.1");        //IP黑名单 (存在共同时,deny优先于allow) : 如果满足deny的即提示:Sorry, you are not permitted to view this page.        servletRegistrationBean.addInitParameter("deny","192.168.1.100");        //登录查看信息的账号密码.        servletRegistrationBean.addInitParameter("loginUsername","druid");        servletRegistrationBean.addInitParameter("loginPassword","12345678");        //是否能够重置数据.        servletRegistrationBean.addInitParameter("resetEnable","false");        return servletRegistrationBean;    }    @Bean    public FilterRegistrationBean statFilter(){        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());        //添加过滤规则.        filterRegistrationBean.addUrlPatterns("/*");        //添加不需要忽略的格式信息.        filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");        return filterRegistrationBean;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

JPAPersistenceConfiguration

package com.ekeyfund.springcloud.configuration;import com.alibaba.druid.pool.DruidDataSource;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.autoconfigure.domain.EntityScan;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.Ordered;import org.springframework.core.annotation.Order;import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;import org.springframework.data.jpa.repository.config.EnableJpaRepositories;import org.springframework.orm.jpa.JpaTransactionManager;import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;import org.springframework.orm.jpa.vendor.HibernateJpaDialect;import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;import org.springframework.transaction.PlatformTransactionManager;import org.springframework.transaction.annotation.EnableTransactionManagement;import javax.persistence.EntityManagerFactory;import javax.sql.DataSource;import java.sql.SQLException;import java.util.Properties;/** * JPA Persistence Configuration * * @author Liuguanglei liuguanglei@ekeyfund.com * @create 2017-06-上午11:26 */@Order(Ordered.HIGHEST_PRECEDENCE)@Configuration@EnableTransactionManagement(proxyTargetClass = true) //启用JPA的事务管理@EnableJpaRepositories(basePackages = "com.ekeyfund.springcloud.repository" )//启用JPA资源库并指定资源库接口位置@EntityScan(basePackages = "com.ekeyfund.springcloud.entity")//指定实体的位置public class JPAPersistenceConfiguration {    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(JPAPersistenceConfiguration.class);    /*******************数据库和连接池配置信息,读取application.properties文件的属性值****************************/    @Value("${spring.datasource.driver-class-name}")    private String driverClass;    @Value("${spring.datasource.username}")    private String userName;    @Value("${spring.datasource.password}")    private String password;    @Value("${spring.datasource.url}")    private String url;    @Value("${spring.datasource.initialSize}")    private int initialSize;    @Value("${spring.datasource.minIdle}")    private int minIdle;    @Value("${spring.datasource.maxActive}")    private int maxActive;    @Value("${spring.datasource.maxWait}")    private long maxWait;    @Value("${spring.datasource.timeBetweenEvictionRunsMillis}")    private long timeBetweenEvictionRunsMillis;    @Value("${spring.datasource.minEvictableIdleTimeMillis}")    private long minEvictableIdleTimeMillis;    @Value("${spring.datasource.filters}")    private String filters;    @Value("${spring.datasource.connectionProperties}")    private String connectionProperties;    @Bean(name = "druidDataSource",initMethod = "init",destroyMethod = "close")    public DataSource dataSource(){        DruidDataSource druidDataSource =new DruidDataSource();        druidDataSource.setDriverClassName(driverClass);        druidDataSource.setUsername(userName);        druidDataSource.setPassword(password);        druidDataSource.setUrl(url);        druidDataSource.setInitialSize(initialSize);        druidDataSource.setMinIdle(minIdle);        druidDataSource.setMaxActive(maxActive);        druidDataSource.setMaxWait(maxWait);        druidDataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);        druidDataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);        druidDataSource.setConnectionProperties(connectionProperties);        try {            druidDataSource.setFilters(filters);        } catch (SQLException e) {            LOGGER.error("build datasoure exception ",e.getMessage());        }        return druidDataSource;    }    @Bean(name = "entityManagerFactory")    public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource druidDataSource){        LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean =new LocalContainerEntityManagerFactoryBean();        localContainerEntityManagerFactoryBean.setDataSource(druidDataSource);        localContainerEntityManagerFactoryBean.setPackagesToScan("com.ekeyfund.springcloud.entity");        localContainerEntityManagerFactoryBean.setJpaProperties(buildHibernateProperties());        localContainerEntityManagerFactoryBean.setJpaDialect(new HibernateJpaDialect());        localContainerEntityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter(){            {                setDatabase(org.springframework.orm.jpa.vendor.Database.MYSQL);                setDatabasePlatform("org.hibernate.dialect.MySQL5Dialect");            }        });        return localContainerEntityManagerFactoryBean;    }    @Bean    public PlatformTransactionManager transactionManager(DataSource druidDataSource, EntityManagerFactory entityManagerFactory){        JpaTransactionManager jpaTransactionManager=new JpaTransactionManager();        jpaTransactionManager.setDataSource(druidDataSource);        jpaTransactionManager.setEntityManagerFactory(entityManagerFactory);        return jpaTransactionManager;    }    @Bean    PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor(){        return new PersistenceExceptionTranslationPostProcessor();    }    protected Properties buildHibernateProperties(){        Properties hibernateProperties =new Properties();        hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");        hibernateProperties.setProperty("hibernate.hbm2ddl.auto","update");        hibernateProperties.setProperty("hibernate.show_sql", "false");        hibernateProperties.setProperty("hibernate.use_sql_comments", "false");        hibernateProperties.setProperty("hibernate.format_sql", "true");        hibernateProperties.setProperty("hibernate.generate_statistics", "false");        hibernateProperties.setProperty("javax.persistence.validation.mode", "none");        //Audit History flags        hibernateProperties.setProperty("org.hibernate.envers.store_data_at_delete", "true");        hibernateProperties.setProperty("org.hibernate.envers.global_with_modified_flag", "true");        hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", "true");        hibernateProperties.setProperty("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.EhCacheRegionFactory");        hibernateProperties.setProperty("hibernate.cache.use_query_cache", "true");        return hibernateProperties;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163

4.6 应用配置

主要包含springboot的application.properties,logback的logback-spring.xml以及缓存框架的ehcache.xml

application.propeties

##DataSource Config##\u6570\u636e\u5e93\u8fde\u63a5\u6c60\u4fe1\u606f\u914d\u7f6espring.datasource.type=com.alibaba.druid.pool.DruidDataSourcespring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.url=jdbc:mysql://127.0.0.1:3306/springboot?characterEncoding=utf-8spring.datasource.username=rootspring.datasource.password=guanglei# \u4e0b\u9762\u4e3a\u8fde\u63a5\u6c60\u7684\u8865\u5145\u8bbe\u7f6e\uff0c\u5e94\u7528\u5230\u4e0a\u9762\u6240\u6709\u6570\u636e\u6e90\u4e2d# \u521d\u59cb\u5316\u5927\u5c0f\uff0c\u6700\u5c0f\uff0c\u6700\u5927spring.datasource.initialSize=5spring.datasource.minIdle=5spring.datasource.maxActive=20# \u914d\u7f6e\u83b7\u53d6\u8fde\u63a5\u7b49\u5f85\u8d85\u65f6\u7684\u65f6\u95f4spring.datasource.maxWait=60000# \u914d\u7f6e\u95f4\u9694\u591a\u4e45\u624d\u8fdb\u884c\u4e00\u6b21\u68c0\u6d4b\uff0c\u68c0\u6d4b\u9700\u8981\u5173\u95ed\u7684\u7a7a\u95f2\u8fde\u63a5\uff0c\u5355\u4f4d\u662f\u6beb\u79d2spring.datasource.timeBetweenEvictionRunsMillis=60000# \u914d\u7f6e\u4e00\u4e2a\u8fde\u63a5\u5728\u6c60\u4e2d\u6700\u5c0f\u751f\u5b58\u7684\u65f6\u95f4\uff0c\u5355\u4f4d\u662f\u6beb\u79d2spring.datasource.minEvictableIdleTimeMillis=300000spring.datasource.validationQuery=SELECT 1 FROM DUALspring.datasource.testWhileIdle=truespring.datasource.testOnBorrow=falsespring.datasource.testOnReturn=false# \u6253\u5f00PSCache\uff0c\u5e76\u4e14\u6307\u5b9a\u6bcf\u4e2a\u8fde\u63a5\u4e0aPSCache\u7684\u5927\u5c0fspring.datasource.poolPreparedStatements=truespring.datasource.maxPoolPreparedStatementPerConnectionSize=20# \u914d\u7f6e\u76d1\u63a7\u7edf\u8ba1\u62e6\u622a\u7684filters\uff0c\u53bb\u6389\u540e\u76d1\u63a7\u754c\u9762sql\u65e0\u6cd5\u7edf\u8ba1\uff0c'wall'\u7528\u4e8e\u9632\u706b\u5899spring.datasource.filters=stat,wall,log4j# \u901a\u8fc7connectProperties\u5c5e\u6027\u6765\u6253\u5f00mergeSql\u529f\u80fd\uff1b\u6162SQL\u8bb0\u5f55spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000# \u5408\u5e76\u591a\u4e2aDruidDataSource\u7684\u76d1\u63a7\u6570\u636e#spring.datasource.useGlobalDataSourceStat=true# druid \u8bbf\u95ee\u5730\u5740 http://host:port/druid/index.html##Log Configlogging.config=classpath:logback-spring.xml## SpringData JPA Configspring.jpa.database=mysqlspring.jpa.show-sql=truespring.jpa.hibernate.ddl-auto=updatespring.jpa.generate-ddl=trueserver.port=9996
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

ehcache.xml

<ehcache>    <!-- Sets the path to the directory where cache .data files are created.         If the path is a Java System Property it is replaced by         its value in the running VM.         The following properties are translated:         user.home - User's home directory         user.dir - User's current working directory         java.io.tmpdir - Default temp file path -->    <!--        指定一个目录:当 EHCache 把数据写到硬盘上时, 将把数据写到这个目录下.    -->    <diskStore path="tempDirectory"/>    <!--Default Cache configuration. These will applied to caches programmatically created through        the CacheManager.        The following attributes are required for defaultCache:        maxInMemory       - Sets the maximum number of objects that will be created in memory        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element                            is never expired.        timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used                            if the element is not eternal. Idle time is now - last accessed time        timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used                            if the element is not eternal. TTL is now - creation time        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache                            has reached the maxInMemory limit.        -->    <!--        设置缓存的默认数据过期策略    -->    <defaultCache            maxElementsInMemory="10000"            eternal="false"            timeToIdleSeconds="120"            timeToLiveSeconds="120"            overflowToDisk="true"    />    <!--        设定具体的命名缓存的数据过期策略。每个命名缓存代表一个缓存区域        缓存区域(region):一个具有名称的缓存块,可以给每一个缓存块设置不同的缓存策略。        如果没有设置任何的缓存区域,则所有被缓存的对象,都将使用默认的缓存策略。即:<defaultCache.../>        Hibernate 在不同的缓存区域保存不同的类/集合。         对于类而言,区域的名称是类名。如:com.ekeyfund.springboot.jpa.entity.User         对于集合而言,区域的名称是类名加属性名。如com.ekeyfund.springboot.jpa.entity.User.roleList    -->    <!--        name: 设置缓存的名字,它的取值为类的全限定名或类的集合的名字     maxElementsInMemory: 设置基于内存的缓存中可存放的对象最大数目     eternal: 设置对象是否为永久的, true表示永不过期,     此时将忽略timeToIdleSeconds 和 timeToLiveSeconds属性; 默认值是false     timeToIdleSeconds:设置对象空闲最长时间,以秒为单位, 超过这个时间,对象过期。     当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。     timeToLiveSeconds:设置对象生存最长时间,超过这个时间,对象过期。     如果此值为0,表示对象可以无限期地存在于缓存中. 该属性值必须大于或等于 timeToIdleSeconds 属性值     overflowToDisk:设置基于内存的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中    -->    <cache name="com.ekeyfund.springcloud.entity.Department"           maxElementsInMemory="1"           eternal="false"           timeToIdleSeconds="300"           timeToLiveSeconds="600"           overflowToDisk="true"    />    <cache name="com.ekeyfund.springcloud.entity.User"           maxElementsInMemory="1000"           eternal="true"           timeToIdleSeconds="0"           timeToLiveSeconds="0"           overflowToDisk="false"    />    <cache name="com.ekeyfund.springcloud.entity.Role"           maxElementsInMemory="1000"           eternal="true"           timeToIdleSeconds="0"           timeToLiveSeconds="0"           overflowToDisk="false"    /></ehcache>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90

logback.xml

<?xml version="1.0" encoding="UTF-8"?><configuration>    <!--<include resource="org/springframework/boot/logging/logback/base.xml"/>-->    <!-- 项目的appid -->    <property name="APP_ID" value="SpringCloud-Provider-User-Service"/>    <property name="LOG_PATH" value="logs"></property>    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">        <encoder>            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>        </encoder>    </appender>    <appender name="FILE_LOG"              class="ch.qos.logback.core.rolling.RollingFileAppender">        <filter class="ch.qos.logback.classic.filter.LevelFilter">            <level>DEBUG</level>        </filter>        <file>${LOG_PATH}/${APP_ID}/access.log</file>        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">            <fileNamePattern>${LOG_PATH}/${APP_ID}/access.log.%d{yyyy-MM-dd}.zip            </fileNamePattern>            <maxHistory>10</maxHistory>        </rollingPolicy>        <encoder>            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>        </encoder>    </appender>    <appender name="FILE_DEBUG"              class="ch.qos.logback.core.rolling.RollingFileAppender">        <filter class="ch.qos.logback.classic.filter.LevelFilter">            <level>DEBUG</level>            <onMatch>ACCEPT</onMatch>            <onMismatch>DENY</onMismatch>        </filter>        <file>${LOG_PATH}/${APP_ID}/access_debug.log</file>        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">            <fileNamePattern>${LOG_PATH}/${APP_ID}/access_debug.log.%d{yyyy-MM-dd}.zip            </fileNamePattern>            <maxHistory>10</maxHistory>        </rollingPolicy>        <encoder>            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>        </encoder>    </appender>    <appender name="FILE_INFO"              class="ch.qos.logback.core.rolling.RollingFileAppender">        <filter class="ch.qos.logback.classic.filter.LevelFilter">            <level>INFO</level>            <onMatch>ACCEPT</onMatch>            <onMismatch>DENY</onMismatch>        </filter>        <file>${LOG_PATH}/${APP_ID}/access_info.log</file>        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">            <fileNamePattern>${LOG_PATH}/${APP_ID}/access_info.log.%d{yyyy-MM-dd}.zip            </fileNamePattern>            <maxHistory>10</maxHistory>        </rollingPolicy>        <encoder>            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>        </encoder>    </appender>    <appender name="FILE_WARN"              class="ch.qos.logback.core.rolling.RollingFileAppender">        <filter class="ch.qos.logback.classic.filter.LevelFilter">            <level>WARN</level>            <onMatch>ACCEPT</onMatch>            <onMismatch>DENY</onMismatch>        </filter>        <file>${LOG_PATH}/${APP_ID}/access_warn.log</file>        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">            <fileNamePattern>${LOG_PATH}/${APP_ID}/access_warn.log.%d{yyyy-MM-dd}.zip            </fileNamePattern>            <maxHistory>10</maxHistory>        </rollingPolicy>        <encoder>            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>        </encoder>    </appender>    <appender name="FILE_ERROR"              class="ch.qos.logback.core.rolling.RollingFileAppender">        <filter class="ch.qos.logback.classic.filter.LevelFilter">            <level>ERROR</level>            <onMatch>ACCEPT</onMatch>            <onMismatch>DENY</onMismatch>        </filter>        <file>${LOG_PATH}/${APP_ID}/access_error.log</file>        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">            <fileNamePattern>${LOG_PATH}/${APP_ID}/access_error.log.%d{yyyy-MM-dd}.zip            </fileNamePattern>            <maxHistory>10</maxHistory>        </rollingPolicy>        <encoder>            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>        </encoder>    </appender>    <appender name="ASYNC_LOG" class="ch.qos.logback.classic.AsyncAppender">        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->        <discardingThreshold>0</discardingThreshold>        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->        <queueSize>512</queueSize>        <appender-ref ref="FILE_LOG"/>    </appender>    <appender name="ASYNC_LOG" class="ch.qos.logback.classic.AsyncAppender">        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->        <discardingThreshold>0</discardingThreshold>        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->        <queueSize>512</queueSize>        <appender-ref ref="FILE_LOG"/>    </appender>    <appender name="ASYNC_LOG_DEBUG" class="ch.qos.logback.classic.AsyncAppender">        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->        <discardingThreshold>0</discardingThreshold>        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->        <queueSize>512</queueSize>        <appender-ref ref="FILE_DEBUG"/>    </appender>    <appender name="ASYNC_LOG_INFO" class="ch.qos.logback.classic.AsyncAppender">        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->        <discardingThreshold>0</discardingThreshold>        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->        <queueSize>512</queueSize>        <appender-ref ref="FILE_INFO"/>    </appender>    <appender name="ASYNC_LOG_WARN" class="ch.qos.logback.classic.AsyncAppender">        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->        <discardingThreshold>0</discardingThreshold>        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->        <queueSize>512</queueSize>        <appender-ref ref="FILE_WARN"/>    </appender>    <appender name="ASYNC_LOG_ERROR" class="ch.qos.logback.classic.AsyncAppender">        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->        <discardingThreshold>0</discardingThreshold>        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->        <queueSize>512</queueSize>        <appender-ref ref="FILE_ERROR"/>    </appender>    <root level="INFO">        <!-- appender referenced after it is defined -->        <appender-ref ref="STDOUT"/>        <appender-ref ref="ASYNC_LOG"/>        <appender-ref ref="ASYNC_LOG_DEBUG"/>        <appender-ref ref="ASYNC_LOG_INFO"/>        <appender-ref ref="ASYNC_LOG_WARN"/>        <appender-ref ref="ASYNC_LOG_ERROR"/>    </root>    <logger name="org.springframework" level="INFO"/></configuration>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153

4.7 pom.xml

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <artifactId>springcloud-provider-user-service</artifactId>    <packaging>jar</packaging>    <name>springcloud-user-service</name>    <description>SpringCloud User Service Application</description>    <parent>        <groupId>com.ekeyfund.springcloud</groupId>        <artifactId>springcloud-parent</artifactId>        <version>2.0.0-SNAPSHOT</version>        <relativePath>../pom.xml</relativePath>    </parent>    <properties>        <start-class>com.ekeyfund.springcloud.SpringcloudUserServiceMasterApplication</start-class>    </properties>    <dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-data-jpa</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>                <configuration>                    <mainClass>${start-class}</mainClass>                    <layout>ZIP</layout>                </configuration>                <executions>                    <execution>                        <goals>                            <goal>repackage</goal>                        </goals>                    </execution>                </executions>            </plugin>        </plugins>    </build></project>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

4.8 启动类

package com.ekeyfund.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;//@EnableDiscoveryClient//激活Eureka中的DiscoveryClient实现(自动化配置,创建DiscoveryClient接口针对Eureka客户端的EurekaDiscoveryClient实例)@SpringBootApplicationpublic class SpringcloudUserServiceMasterApplication {    public static void main(String[] args) {        SpringApplication.run(SpringcloudUserServiceMasterApplication.class, args);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

4.9 测试用例

package com.ekeyfund.springcloud;import com.ekeyfund.springcloud.entity.Department;import com.ekeyfund.springcloud.entity.User;import com.ekeyfund.springcloud.service.DepartmentService;import com.ekeyfund.springcloud.service.UserService;import org.junit.Test;import org.junit.runner.RunWith;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.context.annotation.ComponentScan;import org.springframework.test.context.junit4.SpringRunner;import java.util.Date;@RunWith(SpringRunner.class)@SpringBootTest@ComponentScan("com.ekeyfund.springcloud")public class SpringcloudUserServiceApplicationTests {    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(SpringcloudUserServiceApplicationTests.class);    @Test    public void contextLoads() {    }    @Autowired    private DepartmentService departmentService;    @Autowired    private UserService userService;    @Test    public void testDepartmentService(){        Department department=new Department();        department.setName("dev");        Department result =departmentService.saveDepartment(department);        LOGGER.info("add result "+result);        Long id =1L;        result =departmentService.getDepartmentById(id);        LOGGER.info("get department "+result);    }    @Test    public void testUserRegister()throws Exception{        User user =new User();        user.setName("tony");        user.setPassword("666666");        user.setCreateDate(new Date());        Department department=departmentService.getDepartmentById(1L);        user.setDepartment(department);        User result =userService.register(user);        LOGGER.info("register result "+result);    }    @Test    public void testWriteOff()throws Exception{        User user =new User();        user.setName("tony");        user.setPassword("666666");        userService.writeOff(user);    }    @Test    public void testUserLogin()throws Exception{        User user =new User();        user.setName("tony");        user.setPassword("666666");        User result =userService.login(user.getName(),user.getPassword());        LOGGER.info("login  "+result);    }    @Test    public void testUserIsExist()throws Exception{        User user =new User();        user.setId(4L);        boolean result =userService.isExists(user);        LOGGER.info("isExist  "+result);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102

五 使用SpringBoot实现服务消费者

所属maven模块:springcloud-consumer-h5

5.1 entity

主要包含User,Department,Role 
Role.java

package com.ekeyfund.springcloud.entity;/** * Role Entity * * @author Liuguanglei liuguanglei@ekeyfund.com * @create 2017-06-下午2:36 */public class Role {    private Long id;    private String name;    public Long getId() {        return id;    }    public void setId(Long id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public String toString() {        return new org.apache.commons.lang3.builder.ToStringBuilder(this)                .append("id", id)                .append("name", name)                .toString();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

Department.java

package com.ekeyfund.springcloud.entity;import org.apache.commons.lang3.builder.ToStringBuilder;/** * Department Entity * * @author Liuguanglei liuguanglei@ekeyfund.com * @create 2017-06-下午2:31 */public class Department {    private Long id;    private String name;    public Long getId() {        return id;    }    public void setId(Long id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public String toString() {        return new ToStringBuilder(this)                .append("id", id)                .append("name", name)                .toString();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

User.java

package com.ekeyfund.springcloud.entity;import com.fasterxml.jackson.annotation.JsonBackReference;import org.apache.commons.lang3.builder.ToStringBuilder;import org.springframework.format.annotation.DateTimeFormat;import java.util.Date;import java.util.List;/** * User Entity * * @author Liuguanglei liuguanglei@ekeyfund.com * @create 2017-06-下午2:32 */public class User {    private Long id;    private String name;    private String password;    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")    private Date createDate;    @JsonBackReference    private Department department;    private List<Role> roleList;    public Long getId() {        return id;    }    public void setId(Long id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getPassword() {        return password;    }    public void setPassword(String password) {        this.password = password;    }    public Date getCreateDate() {        return createDate;    }    public void setCreateDate(Date createDate) {        this.createDate = createDate;    }    public Department getDepartment() {        return department;    }    public void setDepartment(Department department) {        this.department = department;    }    public List<Role> getRoleList() {        return roleList;    }    public void setRoleList(List<Role> roleList) {        this.roleList = roleList;    }    @Override    public String toString() {        return new ToStringBuilder(this)                .append("id", id)                .append("name", name)                .append("password", password)                .append("createDate", createDate)                .append("department", department)                .append("roleList", roleList)                .toString();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101

5.2 Configuraiton

主要包含Spring MVC相关配置

MVCConfiguration.java

package com.ekeyfund.springcloud.configuration;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.MediaType;import org.springframework.http.converter.HttpMessageConverter;import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;import org.springframework.web.client.RestTemplate;import org.springframework.web.servlet.config.annotation.EnableWebMvc;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;import java.util.ArrayList;import java.util.List;@Configuration@EnableWebMvcpublic class MVCConfiguration extends WebMvcConfigurerAdapter{        /**         *          * @return         */        @Bean        public RequestMappingHandlerAdapter requestMappingHandlerAdapter(){          RequestMappingHandlerAdapter requestMappingHandlerAdapter=new RequestMappingHandlerAdapter();          List<HttpMessageConverter<?>> messageConverters =new ArrayList<>();          messageConverters.add(mappingJackson2HttpMessageConverter());          requestMappingHandlerAdapter.setMessageConverters(messageConverters);          return requestMappingHandlerAdapter;      }        //@Bean        public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(){            MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter =new MappingJackson2HttpMessageConverter();            List<MediaType> supportedMediaTypes =new ArrayList<>();            supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);            mappingJackson2HttpMessageConverter.setSupportedMediaTypes(supportedMediaTypes);            return  mappingJackson2HttpMessageConverter;        }        @Bean        public RestTemplate restTemplate(){            RestTemplate restTemplate =new RestTemplate();            return restTemplate;        }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

5.3 Controller

主要包含UserController

UserController.java

package com.ekeyfund.springcloud.controller;import com.ekeyfund.springcloud.entity.User;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.client.RestTemplate;/** * UserController * * @author tony 18601767221@163.com * @create 2017-06-27-下午11:42 * @see * @since JDK1.8u133 */@RestControllerpublic class UserController {    @Autowired    private RestTemplate restTemplate;    @GetMapping("user/get/{id}")    public User get(@PathVariable Long id){        return this.restTemplate.getForObject("http://127.0.0.1:9996/get/{1}",User.class,id);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

5.4 应用配置

主要包含springboot的application.properties

application.properties

server.port=9991server.tomcat.uri-encoding=UTF-8
  • 1
  • 2
  • 3

5.5 启动类

package com.ekeyfund.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class SpringcloudConsumerH5Application {    public static void main(String[] args) {        SpringApplication.run(SpringcloudConsumerH5Application.class, args);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

5.6 测试服务调用

9991端口的服务消费者会调用9996端口的服务提供者接口。 
通常在实际的开发中会遇到类似的情景:例如PC/H5/Android/IOS会去调用User模块的登录服务。

RestTemplate Call

弊端:因为程序调用的IP和端口采用了硬编码,而IP和端口可能变更,因此难以维护。 
而当服务提供者宕机后服务会不可用,后面会在系统中引入服务注册、发现组件,该组件主要是维护了一个服务注册表,服务提供者和消费者将服务注册到该注册表中,而服务注册和发现组件会通过发送心跳来判断服务是否可用。

六 使用Eureka实现服务发现组件

所属maven模块:springcloud-eureka-server

1 pom.xml

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>com.ekeyfund.springcloud</groupId>    <artifactId>springcloud-eureka-server</artifactId>    <packaging>jar</packaging>    <name>springcloud-eureka-server</name>    <description>SpringCloud Eureka Server </description>    <parent>        <groupId>com.ekeyfund.springcloud</groupId>        <artifactId>springcloud-parent</artifactId>        <version>2.0.0-SNAPSHOT</version>        <relativePath>../pom.xml</relativePath>    </parent>    <properties>        <start-class>com.ekeyfund.springcloud.SpringcloudEurekaServerApplication</start-class>    </properties></project>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

2 编写启动类,在启动类上添加@EnableEurekaServer注解,声明这是一个Eureka Server

package com.ekeyfund.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;/** * 单点eureka server */@EnableEurekaServer //启用Eureka Server@SpringBootApplicationpublic class SpringcloudEurekaServerApplication {    public static void main(String[] args) {        SpringApplication.run(SpringcloudEurekaServerApplication.class, args);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

3 在应用配置文件application.properties添加如下内容:

spring.application.name=springcloud-cureka-serverserver.port=9999eureka.instance.hostname=127.0.0.1#定义注册中心的地址eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/#不向注册中心注册自己eureka.client.register-with-eureka=false#注册中心的职责就是去维护服务实例,不需要去检索服务eureka.client.fetch-registry=falseeureka.instance.prefer-ip-address=true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

4 测试访问Eureka Server

运行SpringcloudEurekaServerApplication,在浏览器输入 http://127.0.0.1:9999即可以访问Eureka Server

Eureka Server

七 服务注册

所属maven模块 springcloud-provider-user-service

1 pom.xml

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <artifactId>springcloud-provider-user-service</artifactId>    <packaging>jar</packaging>    <name>springcloud-user-service</name>    <description>SpringCloud User Service Application</description>    <parent>        <groupId>com.ekeyfund.springcloud</groupId>        <artifactId>springcloud-parent</artifactId>        <version>2.0.0-SNAPSHOT</version>        <relativePath>../pom.xml</relativePath>    </parent>    <properties>        <start-class>com.ekeyfund.springcloud.SpringcloudUserServiceMasterApplication</start-class>    </properties>    <dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-data-jpa</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <!-- eureka client-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-eureka</artifactId>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>                <configuration>                    <mainClass>${start-class}</mainClass>                    <layout>ZIP</layout>                </configuration>                <executions>                    <execution>                        <goals>                            <goal>repackage</goal>                        </goals>                    </execution>                </executions>            </plugin>        </plugins>    </build></project>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

2 在application.properties文件中添加以下配置:

#####Eureka Client Config########设置服务名称spring.application.name=springcloud-provider-user-service#eureka 单实例配置#eureka.client.service-url.defaultZone=http://127.0.0.1:9999/eurekaeureka.instance.prefer-ip-address=true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

其中spring.application.name是指定注册到Eureka Server上的应用名称 
eureka.instance.prefer-ip-address表示将自己的IP注册到Eureka Server

3 编写启动类,在启动类上添加@EnableDiscoveryClient注解,声明这是一个Eureka Client

package com.ekeyfund.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@EnableDiscoveryClient//激活Eureka中的DiscoveryClient实现(自动化配置,创建DiscoveryClient接口针对Eureka客户端的EurekaDiscoveryClient实例)@SpringBootApplicationpublic class SpringcloudUserServiceApplication {    public static void main(String[] args) {        SpringApplication.run(SpringcloudUserServiceMasterApplication.class, args);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

4 测试服务注册 
首先运行SpringcloudEurekaServerApplication,启动Eureka Server,然后启动SpringcloudUserServiceApplication,启动Eureka Client。 
访问Eureka Server,效果如下图: 
Eureka Server Register

八 Eureka Server 高可用实现

在生产环境中,如果Eureka Server所在的机器发生宕机,那么服务发现组件将会变得不可用,因此需要实现高可用。Eureka Server可以通过运行多个实例并相互注册的方式实现高可用部署,实例之间会彼此增量同步信息,从而保持所有节点的数据一致。

所属maven模块springcloud-eureka-server-ha

该模块使用了springboot的多环境配置特性来激活两个Eureka Server,因此准备了application.properties,application-master.properties和application-slave.properties三个配置文件

application.properties

spring.profiles.active=master##使用ip地址的形式定义注册中心的地址eureka.instance.prefer-ip-address=true#禁用自我保护模式#eureka.server.enable-self-preservation=false###eureka server 启用安全验证#security.user.name=tony#security.user.password=666666#security.basic.enabled=true##服务续约任务的调用间隔时间默认为30s#eureka.instance.lease-renewal-interval-in-seconds=3###定义服务失效的时间默认是90s#eureka.instance.lease-expiration-duration-in-seconds=540
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

application-master.properties

spring.application.name=springcloud-eureka-server-haserver.port=9998eureka.instance.hostname=127.0.0.1eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:9997/eureka/
  • 1
  • 2
  • 3
  • 4
  • 5

application-slave.properties

spring.application.name=springcloud-eureka-server-haserver.port=9997eureka.instance.hostname=127.0.0.1eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:9998/eureka/
  • 1
  • 2
  • 3
  • 4
  • 5

同时准备两个启动类用来启动两个Eureka Server

SpringcloudEurekaMasterServerApplication.java

package com.ekeyfund.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@EnableEurekaServer@SpringBootApplicationpublic class SpringcloudEurekaMasterServerApplication {    public static void main(String[] args) {        SpringApplication.run(SpringcloudEurekaMasterServerApplication.class, args);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

SpringcloudEurekaSlaveServiceApplication.java

package com.ekeyfund.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@EnableEurekaServer@SpringBootApplicationpublic class SpringcloudEurekaSlaveServiceApplication {    public static void main(String[] args) {        SpringApplication.run(SpringcloudEurekaSlaveServiceApplication.class, args);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

分别运行SpringcloudEurekaMasterServerApplication和SpringcloudEurekaSlaveServiceApplication后会发现两个注册中心已经完成相互注册

9998端口的Eureka Server 
9998端口的Eureka Server

9997端口的Eureka Server 
9997端口的Eureka Server

所属maven模块 springcloud-provider-user-service

服务提供者的服务注册只需要改变application.properties的注册地址即可

#####Eureka Client Config########设置服务名称spring.application.name=springcloud-provider-user-service#指定服务注册中心的地址 ###高可用改造后可以加上多个注册中心的地址eureka.client.service-url.defaultZone=http://127.0.0.1:9998/eureka/,http://127.0.0.1:9997/eureka/eureka.instance.prefer-ip-address=true#####Eureka Client Config########指定服务提供者的端口server.port=9996
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

刷新Eureka Server,查看高可用的Eureka Server

user服务同时注册在两个Eureka Server

eureka server ha

eureka server ha

九 Eureka Server实现安全验证

默认情况下Eureka Server允许匿名访问,这里创建一个需要登录后才能访问Eureka Server

在pom.xml中添加安全依赖

<dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-security</artifactId>        </dependency>
  • 1
  • 2
  • 3
  • 4

然后在application.properties中添加http basic验证配置

##eureka server 启用安全验证security.user.name=tonysecurity.user.password=666666security.basic.enabled=true
  • 1
  • 2
  • 3
  • 4

运行SpringcloudEurekaMasterServerApplication或者SpringcloudEurekaSlaveServerApplication后,访问Eureka Server需要提供用户名和密码才能访问 
eureka security

将服务注册到需要认证的Eureka Server,只需要将eureka.client.serviceUrl.defaultZone配置为http://user:password@127.0.0.1:9998/eureka/,http://user:password@127.0.0.1:9997/eureka/即可

例如 高可用Eureka Server的注册地址变更为

master

spring.application.name=springcloud-eureka-server-haserver.port=9998eureka.instance.hostname=127.0.0.1eureka.client.serviceUrl.defaultZone=http://tony:666666@${eureka.instance.hostname}:9997/eureka/
  • 1
  • 2
  • 3
  • 4
  • 5

slave

spring.application.name=springcloud-eureka-server-haserver.port=9997eureka.instance.hostname=127.0.0.1eureka.client.serviceUrl.defaultZone=http://tony:666666@${eureka.instance.hostname}:9998/eureka/
  • 1
  • 2
  • 3
  • 4
  • 5

十 使用Ribbon实现客户端负载均衡

所属maven模块:springcloud-consumer-h5-ribbon-hystrix

Ribbon是Netflix发布的负载均衡器,有助于控制HTTP和TCP客户端的行为,为Ribbon配置服务提供者地址列表后,Ribbon可以基于负载均衡算法(例如轮询、随机)自动的帮助消费者去请求。 
在SpringCloud中,当Ribbon与Eureka配合使用时,Ribbon可自动从Eureka Server获取服务提供者地址列表,并基于负载均衡算法,请求其中一个服务提供者实例。

10.1 为服务消费者整合Ribbon

pom.xml

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>com.ekeyfund.springcloud</groupId>    <artifactId>springcloud-consumer-h5-ribbon-hystrix</artifactId>    <packaging>jar</packaging>    <name>springcloud-consumer-h5-ribbon-hystrix</name>    <description>Spring Cloud H5 Appliaction</description>    <parent>        <groupId>com.ekeyfund.springcloud</groupId>        <artifactId>springcloud-parent</artifactId>        <version>2.0.0-SNAPSHOT</version>        <relativePath>../pom.xml</relativePath>    </parent>    <properties>        <start-class>com.ekeyfund.springcloud.SpringcloudH5RibbonHystrixApplication</start-class>    </properties>    <dependencies>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-ribbon</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-hystrix</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-eureka</artifactId>        </dependency>        <dependency>            <groupId>com.fasterxml.jackson.core</groupId>            <artifactId>jackson-databind</artifactId>            <version>2.8.8</version>        </dependency>    </dependencies>    <dependencyManagement>        <dependencies>            <dependency>                <groupId>org.springframework.cloud</groupId>                <artifactId>spring-cloud-dependencies</artifactId>                <version>${spring-cloud.version}</version>                <type>pom</type>                <scope>import</scope>            </dependency>        </dependencies>    </dependencyManagement>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>                <configuration>                    <mainClass>${start-class}</mainClass>                    <layout>ZIP</layout>                </configuration>                <executions>                    <execution>                        <goals>                            <goal>repackage</goal>                        </goals>                    </execution>                </executions>            </plugin>        </plugins>    </build></project>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93

MVCConfiguration.java

package com.ekeyfund.springcloud.configuration;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.converter.HttpMessageConverter;import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;import org.springframework.web.client.RestTemplate;import org.springframework.web.servlet.config.annotation.EnableWebMvc;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;import java.util.ArrayList;import java.util.List;/** * MVCConfiguration * * @author Liuguanglei 18601767221@163.com * @create 2017-06-下午1:01 */@Configuration@EnableWebMvcpublic class MVCConfiguration extends WebMvcConfigurerAdapter{    @Bean    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(){        return new MappingJackson2HttpMessageConverter();    }    @Bean    public RequestMappingHandlerAdapter requestMappingHandlerAdapter(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter){        RequestMappingHandlerAdapter requestMappingHandlerAdapter=new RequestMappingHandlerAdapter();        List<HttpMessageConverter<?>> messageConverters=new ArrayList<>();        messageConverters.add(mappingJackson2HttpMessageConverter);        requestMappingHandlerAdapter.setMessageConverters(messageConverters);        return requestMappingHandlerAdapter;    }    @Bean    @LoadBalanced//开启客户端负载均衡    public RestTemplate restTemplate(){     return new RestTemplate();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

相比之前的配置主要是在RestTemplate中添加了@LoadBalanced注解即可整合Ribbon实现客户端的负载均衡。

UserController.java

package com.ekeyfund.springcloud.controller;import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;import com.ekeyfund.springcloud.entity.User;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.*;import org.springframework.web.client.RestTemplate;import java.util.ArrayList;import java.util.Arrays;import java.util.List;/** * User Controller * * @author Liuguanglei 18601767221@163.com * @create 2017-06-上午12:55 */@RestControllerpublic class UserController {    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(UserController.class);    @Autowired    private RestTemplate restTemplate;    @Autowired    private LoadBalancerClient loadBalancerClient;    @GetMapping("/login")    public User login(@RequestParam String name, @RequestParam String password){        LOGGER.info("call user service login method");        ResponseEntity<User> responseEntity =this.restTemplate.getForEntity("http://SPRINGCLOUD-PROVIDER-USER-SERVICE/login?name={1},password={2}",User.class,name,password);        return responseEntity.getBody();    }    @GetMapping("/list")    public List<User> list(){        User[] users=this.restTemplate.getForObject("http://SPRINGCLOUD-PROVIDER-USER-SERVICE/list",User[].class);        List<User> userList = Arrays.asList(users);        return userList;    }    @GetMapping("user/get/{id}")    public User get(@PathVariable Long id){        return this.restTemplate.getForObject("http://SPRINGCLOUD-PROVIDER-USER-SERVICE/get/id={1}",User.class,id);    }    /**     * ribbon负载均衡测试方法     */    @GetMapping("/log-user-service-instance")    public void logUserServiceInstance(){        ServiceInstance serviceInstance=this.loadBalancerClient.choose("springcloud-provider-user-service");        LOGGER.info("serviceInstance info ---> serviceId is  "+serviceInstance.getServiceId()+" host is "+serviceInstance.getHost()+"port is "+serviceInstance.getPort() );    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82

从UserController中的login,get和list方法可以看出,我们将请求地址变更为http://SPRINGCLOUD-PROVIDER-USER-SERVICE,而SPRINGCLOUD-PROVIDER-USER-SERVICE是用户微服务的虚拟主机名,当Ribbon和Eureka配合使用时,会自动将虚拟主机名映射城微服务的网络地址。

在新增的logUserServiceInstance方法中可以通过LoadBalancerClient的API更加直观的获取当前选择的用户微服务节点。

启动如下服务: 
ribbon-loadbalance

访问地址:http://127.0.0.1:9994/log-user-service-instance

观察控制台输出:

2017-06-29 11:26:39.112 INFO 13474 — [nio-9994-exec-7] c.e.s.controller.UserController : serviceInstance info —> serviceId is springcloud-provider-user-service host is 192.168.1.136port is 9996 
2017-06-29 11:26:41.000 INFO 13474 — [nio-9994-exec-8] c.e.s.controller.UserController : serviceInstance info —> serviceId is springcloud-provider-user-service host is 192.168.1.136port is 9995 
2017-06-29 11:26:42.126 INFO 13474 — [nio-9994-exec-1] c.e.s.controller.UserController : serviceInstance info —> serviceId is springcloud-provider-user-service host is 192.168.1.136port is 9996 
2017-06-29 11:26:48.926 INFO 13474 — [nio-9994-exec-4] c.e.s.controller.UserController : serviceInstance info —> serviceId is springcloud-provider-user-service host is 192.168.1.136port is 9995

可以看到此时请求会均匀分布到两个不同用户微服务节点上,说明已经实现了负载均衡。

application.properties

spring.application.name=springcloud-consumer-h5-ribbon-hystrixserver.port=9994eureka.client.service-url.defaultZone=http://tony:666666@127.0.0.1:9998/eureka/,http://tony:666666@127.0.0.1:9997/eureka/#eureka.instance.prefer-ip-address=true##修改服务负载均衡规则为随机springcloud-provier-user-service.ribbon.NFLoadBalanceRuleClassName=com.netflix.loadbalancer.RandomRule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

十一 使用Feign实现声明式REST调用

Feign是Netflix公司开发的声明式、模板化的HTTP客户端,其主要用途是更加便捷、优雅的调用HTTP API,Spring Cloud在原有基础上使Feign支持SpringMVC注解,并且整合了Ribbon和Eureka。

所属maven模块 springcloud-consumer-h5-feign

之前的maven模块springcloud-consumer-h5-ribbon-hystrix是使用RestTemplate(负载均衡是使用ribbon实现)调用RESTful API。 如下的登录方法:

 @GetMapping("/login")    public User login(@RequestParam String name, @RequestParam String password){        LOGGER.info("call user service login method");        ResponseEntity<User> responseEntity =this.restTemplate.getForEntity("http://SPRINGCLOUD-PROVIDER-USER-SERVICE/login?name={1},password={2}",User.class,name,password);        return responseEntity.getBody();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

由代码可知通过拼接字符串的方式构造URL,而在其他业务场景中可能还会有更多的参数,如果还以这种方式构造URL,那么就会变得更低效,难以维护。

引入Netflix公司开发的Feign实现声明式的RESTful API 调用

pom.xml

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>com.ekeyfund.springcloud</groupId>    <artifactId>springcloud-consumer-h5-feign</artifactId>    <packaging>jar</packaging>    <name>springcloud-consumer-h5-feign</name>    <description>SpringCloud Feign H5 Application</description>    <parent>        <groupId>com.ekeyfund.springcloud</groupId>        <artifactId>springcloud-parent</artifactId>        <version>2.0.0-SNAPSHOT</version>        <relativePath>../pom.xml</relativePath>    </parent>    <properties>        <start-class>com.ekeyfund.springcloud.SpringcloudFeignH5Application</start-class>    </properties>    <dependencies>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-feign</artifactId>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>                <configuration>                    <mainClass>${start-class}</mainClass>                    <layout>ZIP</layout>                </configuration>                <executions>                    <execution>                        <goals>                            <goal>repackage</goal>                        </goals>                    </execution>                </executions>            </plugin>        </plugins>    </build></project>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

创建一个Feign接口,并添加@FeignClient注解

package com.ekeyfund.springcloud.feign;import com.ekeyfund.springcloud.entity.User;import org.springframework.cloud.netflix.feign.FeignClient;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RequestParam;import java.util.List;/** * User Feign Client * * @author tony 18601767221@163.com * @create 2017-06-29-下午2:44 * @see * @since JDK1.8u133 */@FeignClient(value = "springcloud-provider-user-service") //public interface UserFeignClient {    @RequestMapping(value = "/list",method = RequestMethod.GET)    List<User> list();    @RequestMapping(value = "/login",method = RequestMethod.GET)    User login(@RequestParam("name")  String name, @RequestParam("password") String password);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

@FeignClient注解中的springcloud-provider-user-service是一个任意的客户端名称,用于创建Ribbon负载均衡器。

修改Controller,让其调用Feign接口

package com.ekeyfund.springcloud.controller;import com.ekeyfund.springcloud.entity.User;import com.ekeyfund.springcloud.feign.UserFeignClient;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import java.util.List;/** * User Feign Controller * * @author Liuguanglei 18601767221@163.com * @create 2017-06-下午1:50 */@RestControllerpublic class UserFeignController {    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(UserFeignController.class);    @Autowired    UserFeignClient userFeignClient;    @Autowired    private LoadBalancerClient loadBalancerClient;    @GetMapping(value = "/list")    public List<User> list(){        return userFeignClient.list();    }    @GetMapping("/login")    public User login(@RequestParam String name,@RequestParam String password){        return userFeignClient.login(name,password);    }    /**     * ribbon负载均衡测试方法     * springcloud 将feign和ribbon以及eureka进行了集成     */    @GetMapping("/log-user-service-instance")    public void loguserserviceinstance(){        ServiceInstance serviceInstance=this.loadBalancerClient.choose("springcloud-provider-user-service");        LOGGER.info("serviceInstance info ---> serviceId is  "+serviceInstance.getServiceId()+" host is "+serviceInstance.getHost()+"port is "+serviceInstance.getPort() );    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

启动类

package com.ekeyfund.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;import org.springframework.cloud.netflix.feign.EnableFeignClients;@EnableDiscoveryClient@EnableFeignClients //开启SpringCloud Feign的支持功能@SpringBootApplicationpublic class SpringcloudFeignH5Application {    public static void main(String[] args) {        SpringApplication.run(SpringcloudFeignH5Application.class, args);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

启动如下服务: 
feign

访问地址:http://127.0.0.1:9993/login?name=tony&password=666666

调用login接口返回的结果 
调用login接口返回的结果

访问http://127.0.0.1:9993/log-user-service-instance

查看控制台日志输出 
2017-06-29 14:56:56.341 INFO 16173 — [nio-9993-exec-4] c.e.s.controller.UserFeignController : serviceInstance info —> serviceId is springcloud-provider-user-service host is 192.168.1.136port is 9995 
2017-06-29 14:56:58.055 INFO 16173 — [nio-9993-exec-5] c.e.s.controller.UserFeignController : serviceInstance info —> serviceId is springcloud-provider-user-service host is 192.168.1.136port is 9996 
2017-06-29 14:56:59.310 INFO 16173 — [nio-9993-exec-6] c.e.s.controller.UserFeignController : serviceInstance info —> serviceId is springcloud-provider-user-service host is 192.168.1.136port is 9995 
2017-06-29 14:57:05.287 INFO 16173 — [nio-9993-exec-7] c.e.s.controller.UserFeignController : serviceInstance info —> serviceId is springcloud-provider-user-service host is 192.168.1.136port is 9996 
2017-06-29 14:57:06.340 INFO 16173 — [nio-9993-exec-8] c.e.s.controller.UserFeignController : serviceInstance info —> serviceId is springcloud-provider-user-service host is 192.168.1.136port is 9995

以上结果说明不仅实现了声明式的Restful API调用,还实现了客户端的负载均衡

十二 使用Hystrix实现微服务的容错处理

pom.xml

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>com.ekeyfund.springcloud</groupId>    <artifactId>springcloud-consumer-h5-ribbon-hystrix</artifactId>    <packaging>jar</packaging>    <name>springcloud-consumer-h5-ribbon-hystrix</name>    <description>Spring Cloud H5 Appliaction</description>    <parent>        <groupId>com.ekeyfund.springcloud</groupId>        <artifactId>springcloud-parent</artifactId>        <version>2.0.0-SNAPSHOT</version>        <relativePath>../pom.xml</relativePath>    </parent>    <properties>        <start-class>com.ekeyfund.springcloud.SpringcloudH5RibbonHystrixApplication</start-class>    </properties>    <dependencies>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-ribbon</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-hystrix</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-eureka</artifactId>        </dependency>        <dependency>            <groupId>com.fasterxml.jackson.core</groupId>            <artifactId>jackson-databind</artifactId>            <version>2.8.8</version>        </dependency>    </dependencies>    <dependencyManagement>        <dependencies>            <dependency>                <groupId>org.springframework.cloud</groupId>                <artifactId>spring-cloud-dependencies</artifactId>                <version>${spring-cloud.version}</version>                <type>pom</type>                <scope>import</scope>            </dependency>        </dependencies>    </dependencyManagement>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>                <configuration>                    <mainClass>${start-class}</mainClass>                    <layout>ZIP</layout>                </configuration>                <executions>                    <execution>                        <goals>                            <goal>repackage</goal>                        </goals>                    </execution>                </executions>            </plugin>        </plugins>    </build></project>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95

启动类:

添加@EnableCircuitBreaker为项目启动断路器支持

package com.ekeyfund.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@EnableCircuitBreaker //启动断路器支持@EnableDiscoveryClient@SpringBootApplicationpublic class SpringcloudH5RibbonHystrixApplication {    public static void main(String[] args) {        SpringApplication.run(SpringcloudH5RibbonHystrixApplication.class, args);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

修改UserController,让其中的list方法具备容错能力

package com.ekeyfund.springcloud.controller;import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;import com.ekeyfund.springcloud.entity.User;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.*;import org.springframework.web.client.RestTemplate;import java.util.ArrayList;import java.util.Arrays;import java.util.List;/** * User Controller * * @author Liuguanglei 18601767221@163.com * @create 2017-06-上午12:55 */@RestControllerpublic class UserController {    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(UserController.class);    @Autowired    private RestTemplate restTemplate;    @Autowired    private LoadBalancerClient loadBalancerClient;    @GetMapping("/login")    public User login(@RequestParam String name, @RequestParam String password){        LOGGER.info("call user service login method");        ResponseEntity<User> responseEntity =this.restTemplate.getForEntity("http://SPRINGCLOUD-PROVIDER-USER-SERVICE/login?name={1},password={2}",User.class,name,password);        return responseEntity.getBody();    }    @HystrixCommand(fallbackMethod = "listFallback",commandProperties = {            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "50000"),            @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds",value = "10000")    },            threadPoolProperties = {                @HystrixProperty(name = "coreSize",value = "1"),                    @HystrixProperty(name="maxQueueSize",value = "20")            }    )    @GetMapping("/list")    public List<User> list(){        User[] users=this.restTemplate.getForObject("http://SPRINGCLOUD-PROVIDER-USER-SERVICE/list",User[].class);        List<User> userList = Arrays.asList(users);        return userList;    }    /**     * 当list方法所在的服务不可用时,会调用此方法     * @return     */    public List<User> listFallback(){        User user =new User();        user.setName("admin");        List<User> userList=new ArrayList<>();        userList.add(user);        return userList;    }    @GetMapping("user/get/{id}")    public User get(@PathVariable Long id){        return this.restTemplate.getForObject("http://SPRINGCLOUD-PROVIDER-USER-SERVICE/get/id={1}",User.class,id);    }    /**     * ribbon负载均衡测试方法     */    @GetMapping("/log-user-service-instance")    public void loguserserviceinstance(){        ServiceInstance serviceInstance=this.loadBalancerClient.choose("springcloud-provider-user-service");        LOGGER.info("serviceInstance info ---> serviceId is  "+serviceInstance.getServiceId()+" host is "+serviceInstance.getHost()+"port is "+serviceInstance.getPort() );    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101

由代码可知,为list方法添加一个回退方法listFallback,该方法与list方法具有相同的参数和返回值类型。

在list方法上,使用注解@HystrixCommand的fallBackMethod属性,指定回退方法是listFallback。

启动如下服务: 
这里写图片描述

访问地址:http://127.0.0.1:9994/list 
当服务状态可用时的返回结果 
服务状态可用

关掉两个springcloud-provider-user-service进程后 
user不可用
再次访问http://127.0.0.1:9994/list 
返回默认用户信息 
返回默认用户

原创粉丝点击