Spring Cloud
Spring Cloud是一個(gè)基于Spring Boot實(shí)現(xiàn)的云應(yīng)用開發(fā)工具敷搪,它為基于JVM的云應(yīng)用開發(fā)中的配置管理嫂便、服務(wù)發(fā)現(xiàn)、斷路器、智能路由、微代理、控制總線、全局鎖苍在、決策競選、分布式會(huì)話和集群狀態(tài)管理等操作提供了一種簡單的開發(fā)方式。
Spring Cloud包含了多個(gè)子項(xiàng)目(針對分布式系統(tǒng)中涉及的多個(gè)不同開源產(chǎn)品),比如:Spring Cloud Config、Spring Cloud Netflix、Spring Cloud CloudFoundry屑埋、Spring Cloud AWS莺丑、Spring Cloud Security洪鸭、Spring Cloud Commons俱济、Spring Cloud Zookeeper浮梢、Spring Cloud CLI等項(xiàng)目。
以下所有的Spring Cloud文章Demo都基于父工程parent實(shí)現(xiàn)
新建一個(gè)基礎(chǔ)Maven工程
pom.xml
文件如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<modules>
<!-- Eureka注冊中心-->
<module>eureka</module>
<!-- 配置中心-->
<module>config</module>
<!-- 智能網(wǎng)關(guān)路由-->
<module>zuul</module>
<!-- Zipkin服務(wù)追蹤-->
<module>zipkin</module>
<!--服務(wù)生產(chǎn)者-->
<module>service-producer</module>
<!-- 服務(wù)消費(fèi)者-->
<module>service-consumer</module>
<!-- Feign服務(wù)消費(fèi)者-->
<module>service-consumer-feign</module>
<!-- 帶有負(fù)載均衡的消費(fèi)者-->
<module>service-consumer-ribbon</module>
<!-- 帶有熔斷機(jī)制的消費(fèi)者-->
<module>service-consumer-ribbon-hystrix</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!-- compiler -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<!-- resources -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
</plugins>
</build>
這里使用的org.springframework.boot版本為1.5.2.RELEASE
依賴的org.springframework.cloud版本為Edgware.SR5
為了配合后續(xù)數(shù)據(jù)庫使用含思,請先執(zhí)行sql目錄下的initSQL.sql
文件:
CREATE SCHEMA spring_cloud_config;
CREATE SCHEMA spring_cloud_demo;
CREATE SCHEMA spring_cloud_zipkin;
USE spring_cloud_demo;
CREATE TABLE user
(
id INT AUTO_INCREMENT
COMMENT '主鍵'
PRIMARY KEY,
name VARCHAR(64) NOT NULL
COMMENT '姓名',
birthday DATE
COMMENT '生日',
address VARCHAR(256)
COMMENT '地址'
)
CHARSET = utf8;
INSERT INTO user ( name, birthday, address) VALUES('test-admin', '1994-12-21', '測試地址');
服務(wù)注冊中心(Spring Cloud Eureka)
Spring Cloud Eureka是Spring Cloud Netflix項(xiàng)目下的服務(wù)治理模塊饰抒。而Spring Cloud Netflix項(xiàng)目是Spring Cloud的子項(xiàng)目之一也颤,主要內(nèi)容是對Netflix公司一系列開源產(chǎn)品的包裝贯溅,它為Spring Boot應(yīng)用提供了自配置的Netflix OSS整合姐霍。通過一些簡單的注解恨胚,開發(fā)者就可以快速的在應(yīng)用中配置一下常用模塊并構(gòu)建龐大的分布式系統(tǒng)。它主要提供的模塊包括:服務(wù)發(fā)現(xiàn)(Eureka),斷路器(Hystrix)勺阐,智能路由(Zuul)懒闷,客戶端負(fù)載均衡(Ribbon)等。
創(chuàng)建一個(gè)"服務(wù)注冊中心"工程
在父工程中新建一個(gè)基礎(chǔ)Spring的Maven Moudle工程命名為eureka
pom.xml
文件如下:
<parent>
<groupId>com.wkedong.springcloud</groupId>
<artifactId>parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath/><!-- lookup parent from repository -->
</parent>
<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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
申明一個(gè)eureka服務(wù)很簡單,通過@EnableEurekaServer
注解啟動(dòng)一個(gè)應(yīng)用作為服務(wù)注冊中心,提供給其他應(yīng)用進(jìn)行注冊訪問店展。
/**
* @author wkedong
*/
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(EurekaApplication.class).web(true).run(args);
}
}
@EnableEurekaServer
:表示該應(yīng)用注冊為eureka服務(wù)注冊中心概说,提供給其他應(yīng)用使用
@SpringBootApplication
:spring boot提供了一個(gè)統(tǒng)一的注解@SpringBootApplication逝变,作為應(yīng)用標(biāo)識(shí)
@SpringBootApplication = (默認(rèn)屬性)@Configuration + @EnableAutoConfiguration + @ComponentScan。
- @Configuration:提到@Configuration就要提到他的搭檔@Bean奋构。使用這兩個(gè)注解就可以創(chuàng)建一個(gè)簡單的spring配置類壳影,可以用來替代相應(yīng)的xml配置文件。
@Configuration的注解類標(biāo)識(shí)這個(gè)類可以使用Spring IoC容器作為bean定義的來源弥臼。@Bean注解告訴Spring宴咧,一個(gè)帶有@Bean的注解方法將返回一個(gè)對象,該對象應(yīng)該被注冊為在Spring應(yīng)用程序上下文中的bean径缅。 - @EnableAutoConfiguration:能夠自動(dòng)配置spring的上下文掺栅,試圖猜測和配置你想要的bean類,通常會(huì)自動(dòng)根據(jù)你的類路徑和你的bean定義自動(dòng)配置纳猪。
- @ComponentScan:會(huì)自動(dòng)掃描指定包下的全部標(biāo)有@Component的類柿冲,并注冊成bean,當(dāng)然包括@Component下的子注解@Service,@Repository,@Controller兆旬。
啟動(dòng)一個(gè)注冊中心所有需要一些基礎(chǔ)的配置,這里使用yml格式作為配置文件
application.yml
如下:
server:
port: 6060 #服務(wù)端口號(hào)為 6060
eureka:
instance:
hostname: localhost #主機(jī)名
client:
healthcheck:
enabled: true #健康檢查開啟
registerWithEureka: false #禁止注冊中心注冊自己
fetchRegistry: false #禁止檢索服務(wù)
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #注冊中心地址
此時(shí)我們啟動(dòng)應(yīng)用并訪問 http://localhost:6060/ 將會(huì)看到下面的頁面怎栽,由于我們沒有注冊其他的服務(wù)丽猬,所以沒有發(fā)現(xiàn)任何服務(wù)宿饱。
服務(wù)提供方(service-producer)
有了服務(wù)注冊中心,下面來新建一個(gè)服務(wù)的提供方即
service-producer
脚祟,并向eureka
中注冊自己
在父工程中新建一個(gè)基礎(chǔ)Spring的Maven Moudle工程命名為service-producer
pom.xml
如下:
<parent>
<groupId>com.wkedong.springcloud</groupId>
<artifactId>parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<properties>
</properties>
<dependencies>
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- fastjson依賴 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.39</version>
</dependency>
<!-- 與數(shù)據(jù)庫操作相關(guān)的依賴 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>4.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
由于demo中后續(xù)使用到了MyBatis和json的相關(guān)操作谬以,所以在service-producer中我們事先依賴了阿里的fastjson和mybatis相應(yīng)的jar包。
然后我們對工程做一些配置工作由桌,
bootstrap.yml
如下:
eureka:
client:
healthcheck:
enabled: true #健康檢查開啟
serviceUrl:
defaultZone: http://localhost:6060/eureka/ #注冊中心服務(wù)地址
spring:
## 從配置中心讀取文件
cloud:
config:
uri: http://localhost:6010/
label: develop
profile: dev
name: service-producer
application:
name: service-producer #當(dāng)前服務(wù)ID
zipkin:
base-url: http://localhost:6040 #zipkin服務(wù)地址
sleuth:
enabled: true #服務(wù)追蹤開啟
sampler:
percentage: 1 #zipkin收集率
datasource: #數(shù)據(jù)庫信息
url: jdbc:mysql://127.0.0.1:3306/spring_cloud_demo?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
management:
security:
enabled: false
#mybatis config
mybatis:
type-aliases-package: com.wkedong.springcloud.serviceproducer.entity #entity實(shí)體對象所在的路徑位置
通過spring:application:name屬性为黎,我們可以指定微服務(wù)的名稱后續(xù)在調(diào)用的時(shí)候只需要使用該名稱就可以進(jìn)行服務(wù)的訪問。
eureka:client:serviceUrl:defaultZone屬性對應(yīng)服務(wù)注冊中心的配置內(nèi)容行您,指定服務(wù)注冊中心的位置铭乾。
多端口設(shè)置
application-peer1.yml
:
spring:
profiles:
active: peer1
server:
port: 6070
application-peer2.yml
:
spring:
profiles:
active: peer2
server:
port: 6080
application-peer3.yml
:
spring:
profiles:
active: peer3
server:
port: 6090
spring:profiles:active屬性設(shè)置使用配置文件。
server:port屬性設(shè)置不同的端口娃循。
接下來我們來實(shí)現(xiàn)服務(wù)的提供炕檩,應(yīng)用啟動(dòng)類Application,具體如下:
/**
* @author wkedong
*/
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.wkedong.springcloud.serviceproducer.mapper")
public class ServiceProducerApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceProducerApplication.class, args);
}
}
@MapperScan:掃描mybatis所配置的mapper路徑
新建一個(gè)controller類來實(shí)現(xiàn)/testGet
,/testPost
,/testFile
,/testConfig
,/testRibbon
,/testFeign
,/testHystrix
接口
ProducerController.java
:
/**
* @author wkedong
* <p>
* 2019/1/14
*/
@RestController
public class ProducerController {
private final Logger logger = Logger.getLogger(getClass());
@Autowired
ProducerService producerService;
@GetMapping(value = "/testGet")
public String testGet() {
logger.info("===<call testGet>===");
return producerService.testGet();
}
@PostMapping(value = "/testPost")
public String testPost(@RequestBody JSONObject jsonRequest) {
logger.info("===<call testPost>===");
return producerService.testPost(jsonRequest);
}
@PostMapping(value = "/testFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) {
logger.info("===<call testFile>===");
return file.getOriginalFilename();
}
@GetMapping(value = "/testConfig")
public String testConfig() {
logger.info("===<call testConfig>===");
return producerService.testConfig();
}
@GetMapping(value = "/testRibbon")
public String testRibbon() {
logger.info("===<call testRibbon>===");
return producerService.testRibbon();
}
@GetMapping(value = "/testFeign")
public String testFeign() {
logger.info("===<call testFeign>===");
return producerService.testFeign();
}
@GetMapping(value = "/testHystrix")
public String testHystrix() {
logger.info("===<call testHystrix>===");
return producerService.testHystrix();
}
}
controller調(diào)用的Service捌斧,新建了ProducerService
接口類及ProducerServiceImpl
實(shí)現(xiàn)類笛质,分別如下:
/**
* @author wkedong
* <p>
* 2019/1/15
*/
public interface ProducerService {
String testGet();
String testPost(JSONObject jsonRequest);
String testConfig();
String testRibbon();
String testFeign();
String testHystrix();
}
/**
* @author wkedong
* <p>
* 2019/1/14
*/
@Service
public class ProducerServiceImpl implements ProducerService {
private Logger logger = LoggerFactory.getLogger(ProducerServiceImpl.class);
@Autowired
private UserMapper userMapper;
@Autowired
private EurekaInstanceConfig eurekaInstanceConfig;
@Value("${server.port}")
private int serverPort = 0;
@Value("${name}")
private String configName = "";
private String returnMessage = "";
@Override
public String testGet() {
this.logger.info("/testGet, instanceId:{}, host:{}", eurekaInstanceConfig.getInstanceId(), eurekaInstanceConfig.getHostName(false));
setReturnMessage(" Get info is testGet Success");
return returnMessage;
}
@Override
public String testPost(@RequestBody JSONObject jsonRequest) {
this.logger.info("/testPost, instanceId:{}, host:{}", eurekaInstanceConfig.getInstanceId(), eurekaInstanceConfig.getHostName(false));
setReturnMessage(" PostParam is " + jsonRequest.toString());
return returnMessage;
}
@Override
public String testConfig() {
this.logger.info("/testConfig, instanceId:{}, host:{}", eurekaInstanceConfig.getInstanceId(), eurekaInstanceConfig.getHostName(false));
setReturnMessage(" ConfigName is " + configName);
return returnMessage;
}
@Override
public String testRibbon() {
this.logger.info("/testRibbon, instanceId:{}, host:{}", eurekaInstanceConfig.getInstanceId(), eurekaInstanceConfig.getHostName(false));
setReturnMessage(" This is a testRibbon result");
return returnMessage;
}
@Override
public String testFeign() {
this.logger.info("/testFeign, instanceId:{}, host:{}", eurekaInstanceConfig.getInstanceId(), eurekaInstanceConfig.getHostName(false));
setReturnMessage(" This is a testFeign result");
return returnMessage;
}
@Override
public String testHystrix() {
this.logger.info("/testHystrix, instanceId:{}, host:{}", eurekaInstanceConfig.getInstanceId(), eurekaInstanceConfig.getHostName(false));
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
setReturnMessage(" This is a testHystrix result");
return returnMessage;
}
private void setReturnMessage(String info) {
returnMessage = "Hello, Spring Cloud! My port is " + String.valueOf(serverPort) + info;
}
}
接口實(shí)現(xiàn)類里實(shí)現(xiàn)了后續(xù)各消費(fèi)者所調(diào)用的各個(gè)接口功能,所以事先放出來
數(shù)據(jù)庫操作的mapper文件如下:
/**
* @author wkedong
* <p>
* 2019/1/15
*/
public interface UserMapper {
@Select("SELECT * FROM user")
List<UserEntity> getAll();
@Select("SELECT * FROM user WHERE id = #{id}")
UserEntity getOne(int id);
@Insert("INSERT INTO user(name,birthday,address) VALUES(#{name}, #{birthday}, #{address})")
void insert(UserEntity user);
@Update("UPDATE user SET name=#{name},birthday=#{birthday},address=#{address} WHERE id =#{id}")
void update(UserEntity user);
@Delete("DELETE FROM user WHERE id =#{id}")
void delete(int id);
}
分別實(shí)現(xiàn)了增刪查改基礎(chǔ)的實(shí)現(xiàn)方法
對應(yīng)的實(shí)體類如下:
/**
* @author wkedong
* 測試實(shí)體
* 2019/1/15
*/
@RefreshScope
@Component//加入到Spring容器
public class UserEntity {
private String id;
private String name;
@DateTimeFormat(style = "yyyy-MM-dd")
private String birthday;
private String address;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return super.toString();
}
啟動(dòng)該工程后捞蚂,再次訪問:http://localhost:6060/ 可以如下圖內(nèi)容妇押,我們定義的服務(wù)被成功注冊了。
訪問 http://localhost:6070/testGet 得到以下返回值:
Hello, Spring Cloud! My port is 6070 Get info By Mybatis is {"address":"江蘇南京","birthday":"1994-12-21","name":"wkedong"}
文章目錄:
整體demo的GitHub地址:Spring-Cloud