SpringCloud 微服務

一微服務架構概述

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:9999
springcloud-eureka-server-ha分布式服務注冊中心(高可用版本)http://127.0.0.1:9998?http://127.0.0.1:9997
springcloud-provider-user-service用戶服務提供者http://127.0.0.1:9996/list?http://127.0.0.1:9995/list
springcloud-consumer-h5用戶服務調用者,采用原始的RestTemplate調用http://127.0.0.1:9991/user/get/4
springcloud-consumer-h5-ribbon-hystrix用戶服務調用者,采用ribbon做客戶端負載均衡http://127.0.0.1:9994/springcloud-provider-user-service
springcloud-consumer-h5-feignfeign聲明式服務調用者http://127.0.0.1:9993/list
springcloud-gateway網關服務http://127.0.0.1:9992/api-a/list
springcloud-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;}@Overridepublic 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;}@Overridepublic 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")@JsonBackReferenceprivate 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;}@Overridepublic 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.*/
@Repository
public 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*/
@Repository
public 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
@Transactional
public class UserServiceImpl implements UserService {@Autowiredprivate UserRepository userRepository;@Overridepublic User login(String name, String password) {return userRepository.findByNameAndPassword(name,password);}@Overridepublic User register(User user) {return userRepository.save(user);}@Overridepublic void writeOff(User user) {userRepository.delete(user);}@Overridepublic boolean isExists(User user) {return userRepository.findOne(user.getId())!=null?true:false;}@Overridepublic List<User> getAllUser() {return userRepository.findAll();}@Overridepublic 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
@Service
public class DepartmentImpl implements DepartmentService {@Autowiredprivate DepartmentRepository departmentRepository;@Overridepublic Department saveDepartment(Department department) {return departmentRepository.save(department);}@Overridepublic 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*/
@RestController
public class UserController {private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(UserController.class);@Autowiredprivate DiscoveryClient discoveryClient;@Autowiredprivate 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 {@Beanpublic 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;}@Beanpublic 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;}@Beanpublic PlatformTransactionManager transactionManager(DataSource druidDataSource, EntityManagerFactory entityManagerFactory){JpaTransactionManager jpaTransactionManager=new JpaTransactionManager();jpaTransactionManager.setDataSource(druidDataSource);jpaTransactionManager.setEntityManagerFactory(entityManagerFactory);return jpaTransactionManager;}@BeanPersistenceExceptionTranslationPostProcessor 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 flagshibernateProperties.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\u7f6e
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/springboot?characterEncoding=utf-8
spring.datasource.username=root
spring.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\u5927
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
# \u914d\u7f6e\u83b7\u53d6\u8fde\u63a5\u7b49\u5f85\u8d85\u65f6\u7684\u65f6\u95f4
spring.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\u79d2
spring.datasource.timeBetweenEvictionRunsMillis=60000
# \u914d\u7f6e\u4e00\u4e2a\u8fde\u63a5\u5728\u6c60\u4e2d\u6700\u5c0f\u751f\u5b58\u7684\u65f6\u95f4\uff0c\u5355\u4f4d\u662f\u6beb\u79d2
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
# \u6253\u5f00PSCache\uff0c\u5e76\u4e14\u6307\u5b9a\u6bcf\u4e2a\u8fde\u63a5\u4e0aPSCache\u7684\u5927\u5c0f
spring.datasource.poolPreparedStatements=true
spring.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\u5899
spring.datasource.filters=stat,wall,log4j
# \u901a\u8fc7connectProperties\u5c5e\u6027\u6765\u6253\u5f00mergeSql\u529f\u80fd\uff1b\u6162SQL\u8bb0\u5f55
spring.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 Config
logging.config=classpath:logback-spring.xml## SpringData JPA Config
spring.jpa.database=mysql
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.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 byits value in the running VM.The following properties are translated:user.home - User's home directoryuser.dir - User's current working directoryjava.io.tmpdir - Default temp file path --><!--指定一個目錄:當 EHCache 把數據寫到硬盤上時, 將把數據寫到這個目錄下.--><diskStore path="tempDirectory"/><!--Default Cache configuration. These will applied to caches programmatically created throughthe CacheManager.The following attributes are required for defaultCache:maxInMemory       - Sets the maximum number of objects that will be created in memoryeternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the elementis never expired.timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only usedif the element is not eternal. Idle time is now - last accessed timetimeToLiveSeconds - Sets the time to live for an element before it expires. Is only usedif the element is not eternal. TTL is now - creation timeoverflowToDisk    - Sets whether elements can overflow to disk when the in-memory cachehas reached the maxInMemory limit.--><!--設置緩存的默認數據過期策略--><defaultCachemaxElementsInMemory="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屬性; 默認值是falsetimeToIdleSeconds:設置對象空閑最長時間,以秒為單位, 超過這個時間,對象過期。當對象過期時,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實例)
@SpringBootApplication
public 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);@Testpublic void contextLoads() {}@Autowiredprivate DepartmentService departmentService;@Autowiredprivate UserService userService;@Testpublic 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);}@Testpublic 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);}@Testpublic void testWriteOff()throws Exception{User user =new User();user.setName("tony");user.setPassword("666666");userService.writeOff(user);}@Testpublic 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);}@Testpublic 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;}@Overridepublic 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;}@Overridepublic 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;@JsonBackReferenceprivate 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;}@Overridepublic 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
@EnableWebMvc
public class MVCConfiguration extends WebMvcConfigurerAdapter{/*** * @return*/@Beanpublic RequestMappingHandlerAdapter requestMappingHandlerAdapter(){RequestMappingHandlerAdapter requestMappingHandlerAdapter=new RequestMappingHandlerAdapter();List<HttpMessageConverter<?>> messageConverters =new ArrayList<>();messageConverters.add(mappingJackson2HttpMessageConverter());requestMappingHandlerAdapter.setMessageConverters(messageConverters);return requestMappingHandlerAdapter;}//@Beanpublic MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(){MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter =new MappingJackson2HttpMessageConverter();List<MediaType> supportedMediaTypes =new ArrayList<>();supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);mappingJackson2HttpMessageConverter.setSupportedMediaTypes(supportedMediaTypes);return  mappingJackson2HttpMessageConverter;}@Beanpublic 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*/
@RestController
public class UserController {@Autowiredprivate 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=9991
server.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;@SpringBootApplication
public 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
@SpringBootApplication
public 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-server
server.port=9999
eureka.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/eureka
eureka.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實例)
@SpringBootApplication
public 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-ha
server.port=9998eureka.instance.hostname=127.0.0.1
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:9997/eureka/
  • 1
  • 2
  • 3
  • 4
  • 5

application-slave.properties

spring.application.name=springcloud-eureka-server-ha
server.port=9997eureka.instance.hostname=127.0.0.1
eureka.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
@SpringBootApplication
public 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
@SpringBootApplication
public 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=tony
security.user.password=666666
security.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-ha
server.port=9998eureka.instance.hostname=127.0.0.1
eureka.client.serviceUrl.defaultZone=http://tony:666666@${eureka.instance.hostname}:9997/eureka/
  • 1
  • 2
  • 3
  • 4
  • 5

slave

spring.application.name=springcloud-eureka-server-ha
server.port=9997eureka.instance.hostname=127.0.0.1
eureka.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
@EnableWebMvc
public class MVCConfiguration extends WebMvcConfigurerAdapter{@Beanpublic MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(){return new MappingJackson2HttpMessageConverter();}@Beanpublic 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*/
@RestController
public class UserController {private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(UserController.class);@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate 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-hystrix
server.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*/
@RestController
public class UserFeignController {private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(UserFeignController.class);@AutowiredUserFeignClient userFeignClient;@Autowiredprivate 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的支持功能
@SpringBootApplication
public 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
@SpringBootApplication
public 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*/
@RestController
public class UserController {private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(UserController.class);@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate 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?
返回默認用戶信息?
返回默認用戶

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/387087.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/387087.shtml
英文地址,請注明出處:http://en.pswp.cn/news/387087.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

window起別名

http://www.bagualu.net/wordpress/archives/1714 轉載于:https://www.cnblogs.com/wei-huan/p/10654026.html

vue在ie9中的兼容問題

問題總結 https://github.com/vuejs-templates/webpack/issues/260 首先npm install --save babel-polyfill然后在main.js中的最前面引入babel-polyfillimport babel-polyfill在index.html 加入以下代碼&#xff08;非必須&#xff09;<meta http-equiv"X-UA-Compatib…

Lecture 9 Random built Binary Search Trees BSTs

Random built Binary Search Trees BSTs E[hight] near 3logn Quick Sort? Relation to Quick Sort: BST sort & Quick sort make same comparisons but in a different order. Randomized BST Sort: 1. Randomly permute A 2. BST sort(A)

spring boot 帶遠程調試啟動方式

比如啟動service-system-0.0.1-SNAPSHOT.jar和service-file-0.0.1-SNAPSHOT.jar nohup java -Xdebug -Xrunjdwp:servery,transportdt_socket,address7999,suspendn -jar service-system-0.0.1-SNAPSHOT.jar > /dev/null 2>&1 &nohup java -Xdebug -Xrunjdwp:se…

文件讀寫

讀寫文件通常都是IO操作&#xff0c;Python內置了讀文件的函數&#xff0c;用法和C是兼容的。 讀寫文件前&#xff0c;我們先必須了解一下&#xff0c;在磁盤上讀寫文件的功能都是有操作系統提供的&#xff0c;現代操作系統不允許普通的程序直接操作磁盤&#xff0c;所以&#…

Vue項目中遇到了大文件分片上傳的問題

Vue項目中遇到了大文件分片上傳的問題&#xff0c;之前用過webuploader&#xff0c;索性就把Vue2.0與webuploader結合起來使用&#xff0c;封裝了一個vue的上傳組件&#xff0c;使用起來也比較舒爽。 上傳就上傳吧&#xff0c;為什么搞得那么麻煩&#xff0c;用分片上傳&#x…

NDK學習筆記-使用現有so動態庫

前面將的都是如何使用C/C文件生成so動態庫&#xff0c;那么在使用別人的so動態庫的時候應該怎么做呢&#xff1f;這篇文章就是使用一個變聲功能的動態庫&#xff0c;完成對于以有so動態庫的說明。 動態庫來源 在互聯網中&#xff0c;有著許許多多動態庫&#xff0c;很多廠商也對…

Spring cloud系列十四 分布式鏈路監控Spring Cloud Sleuth

1. 概述 Spring Cloud Sleuth實現對Spring cloud 分布式鏈路監控 本文介紹了和Sleuth相關的內容&#xff0c;主要內容如下&#xff1a; Spring Cloud Sleuth中的重要術語和意義&#xff1a;Span、Trance、AnnotationZipkin中圖形化展示分布式鏈接監控數據并說明字段意義Spring …

Linux源碼編譯安裝程序

一、程序的組成部分 Linux下程序大都是由以下幾部分組成&#xff1a; 二進制文件&#xff1a;也就是可以運行的程序文件 庫文件&#xff1a;就是通常我們見到的lib目錄下的文件 配置文件&#xff1a;這個不必多說&#xff0c;都知道 幫助文檔&#xff1a;通常是我們在linux下用…

selenium用法詳解

selenium用法詳解 selenium主要是用來做自動化測試&#xff0c;支持多種瀏覽器&#xff0c;爬蟲中主要用來解決JavaScript渲染問題。 模擬瀏覽器進行網頁加載&#xff0c;當requests,urllib無法正常獲取網頁內容的時候一、聲明瀏覽器對象 注意點一&#xff0c;Python文件名或者…

haoop筆記

8:00 2019/3/141&#xff1a;什么是hadoop? hadoop是解決大數據問題的一整套技術方案2&#xff1a;hadoop的組成? 核心框架 分布式文件系統 分布式計算框架 分布式資源分配框架 hadoop對象存儲 機器計算3&#xff1a;hadoop 云計算 大數據 微服務 人工智能關系 參見word學習…

Sleuth則是用來共方便的集成Zipkin。

簡述 使用 spring cloud 用到最多的是各種rest服務調用&#xff0c;Twitter的Zipkin 是一種實現分布式跟蹤解決方案&#xff0c;Sleuth則是用來共方便的集成Zipkin。調用跟蹤系統的業務場景 隨著服務的拆分&#xff0c;系統的模塊變得越來越多&#xff0c;不同的模塊可能由不同…

Spring Cloud 中 分布式事務解決方案 -- 阿里GTS的使用

1&#xff1a;依賴引入<!--gts相關--><!--數據庫連接--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>mysql&…

《HTTP 權威指南》筆記:第十五章 實體與編碼

&#xfffc; 如果把 「HTTP 報文」想象為因特網貨運系統的「箱子」,那么「HTTP 實體」就是報文中的實際的「貨物」. 其中,實體又包含了「實體首部」 和 「實體主體」,實體首部用于描述各種參數,實體主體就是原始貨物. 常見的實體首部 實體的大小: Content-Length 定義: 報文的…

Spring Cloud Sleuth進階實戰

為什么需要Spring Cloud Sleuth 微服務架構是一個分布式架構&#xff0c;它按業務劃分服務單元&#xff0c;一個分布式系統往往有很多個服務單元。由于服務單元數量眾多&#xff0c;業務的復雜性&#xff0c;如果出現了錯誤和異常&#xff0c;很難去定位。主要體現在&#xff…

Element表格嵌入復選框以及單選框

1&#xff0c;element 表格嵌入CheckBox 效果圖如下&#xff1a; 2&#xff0c;element結合checkBox實現單選效果如下&#xff1a; html代碼&#xff1a; <template><div><p>shopInfo</p><el-tableref"multipleTable":data"tableDat…