Spring Cloud 基礎



Spring cloud 實現服務註冊(分佈式、負載均衡、微服務集羣)


基礎服務組件(配置文件存放) 服務組件(數據庫應用服務器) web服務器(SpringMVC/servlet/api網關)
| ↑↓
| 服務發現|註冊 服務發現
←———————————→eureka服務器—————————
服務註冊/發現




註釋:API網關是一個服務器,是系統的唯一入口。系統的後端總入口。有以下兩種方式:
單節點API網關:單節點的API網關爲每個客戶端提供不同的API,而不是提供一種萬能風格的API。
Backends for frontends網關:這種模式是針對不同的客戶端來實現一個不同的API網關
目前流行的網關:
Tyk 開放源碼的API網關,它是快速、可擴展和現代的。Tyk提供了一個API管理平臺,其中包括API網關、API分析、開發人員門戶和API管理面板。Try 是一個基於Go實現的網關服務。
Kong 可擴展的開放源碼API Layer(也稱爲API網關或API中間件)。Kong 在任何RESTful API的前面運行,通過插件擴展,它提供了超越核心平臺的額外功能和服務。
Orange 和Kong類似也是基於OpenResty的一個API網關程序,是由國人開發的,學姐也是貢獻者之一。
Netflix/zuul Zuul是一種提供動態路由、監視、彈性、安全性等功能的邊緣服務。Zuul是Netflix出品的一個基於JVM路由和服務端的負載均衡器。
apiaxle Nodejs實現的一個API網關。
api-umbrella Ruby實現的一個API網關。
第一步創建git工程
cloud-config-repo 配置文件存放的文件夾
cloud-simple-service 數據庫應用服務器1
cloud-simple-service 數據庫應用服務器2
cloud-config-server 配置管理服務器
cloud-eureka-server eureka註冊服務器
cloud-simple-ui 客戶端
.gitignore 默認配置
README.md 說明文檔


第二步配置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>cloud</groupId>
<artifactId>cloud-config-server</artifactId>
<version>0.0.1</version>
<packaging>jar</packaging>
<name>cloud-config-serve</name>
<description>cloud-config-server</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Brixton.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
</properties>
<repositories>
<repository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>public</id>
<name>Public Repositories</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>http://repo.spring.io/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>http://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>http://repo.spring.io/libs-release-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>Public Repositories</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<defaultGoal>compile</defaultGoal>
</build>
</project>


第三步:在cloud-config-repo目錄下創建兩個配置文件
cloud-config-dev.properties //開發環境配置文件
cloud-config-test.properties //測試環境配置信息,目前都相同吧
mysqldb.datasource.url=jdbc\:mysql\://123.207.16.155\:3306/test?useUnicode\=true&characterEncoding\=utf-8
mysqldb.datasource.username=csst
mysqldb.datasource.password=csst
logging.level.org.springframework.web:DEBUG


第四步:創建配置管理服務器cloud-config-server
ConfigServerApplication.java
@SpringBootApplication
@EnableConfigServer//激活該應用爲配置文件服務器
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
application.properties
#配置當前 web 應用綁定 8888 端口
server.port=8888
#指定配置文件所在的 git 工程路徑
spring.cloud.config.server.git.uri=https://gitee.com/anlemusic/Test.git
#searchPaths表示將搜索該文件夾下的配置文件
spring.cloud.config.server.git.searchPaths=cloud-config-repo
pom.xml
最後,還需要在 pom 文件中增加配置服務器的相關依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>


第五步:創建數據庫
CREATE TABLE `user` (`id` varchar(50) NOT NULL DEFAULT '',
`username` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


第六步:cloud-simple-service的數據庫應用的配置(執行時兩次,不同的端口)
DataSourceProperties.java
package cloud.simple.service.conf;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = DataSourceProperties.DS, ignoreUnknownFields = false)
public class DataSourceProperties {
//對應配置文件裏的配置鍵
public final static String DS="mysqldb.datasource";
private String driverClassName ="com.mysql.jdbc.Driver";
private String url; 
private String username; 
private String password;
private int maxActive = 100;
private int maxIdle = 8;
private int minIdle = 8;
private int initialSize = 10;
private String validationQuery;
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getMaxActive() {
return maxActive;
}
public void setMaxActive(int maxActive) {
this.maxActive = maxActive;
}
public int getMaxIdle() {
return maxIdle;
}
public void setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
}
public int getMinIdle() {
return minIdle;
}
public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}
public int getInitialSize() {
return initialSize;
}
public void setInitialSize(int initialSize) {
this.initialSize = initialSize;
}
public String getValidationQuery() {
return validationQuery;
}
public void setValidationQuery(String validationQuery) {
this.validationQuery = validationQuery;
}
public boolean isTestOnBorrow() {
return testOnBorrow;
}
public void setTestOnBorrow(boolean testOnBorrow) {
this.testOnBorrow = testOnBorrow;
}
public boolean isTestOnReturn() {
return testOnReturn;
}
public void setTestOnReturn(boolean testOnReturn) {
this.testOnReturn = testOnReturn;
}
private boolean testOnBorrow = false;
private boolean testOnReturn = false;
}
MybatisDataSource.java
package cloud.simple.service.conf;
import javax.annotation.PreDestroy;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
@EnableConfigurationProperties(DataSourceProperties.class)
//mybaits dao 搜索路徑
@MapperScan("cloud.simple.service.dao")
public class MybatisDataSource {
@Autowired
private DataSourceProperties dataSourceProperties;
//mybaits mapper xml搜索路徑
private final static String mapperLocations="classpath:cloud/simple/service/dao/*.xml"; 
private org.apache.tomcat.jdbc.pool.DataSource pool;
@Bean(destroyMethod = "close")
public DataSource dataSource() {
DataSourceProperties config = dataSourceProperties;
this.pool = new org.apache.tomcat.jdbc.pool.DataSource();
this.pool.setDriverClassName(config.getDriverClassName());
this.pool.setUrl(config.getUrl());
if (config.getUsername() != null) {
this.pool.setUsername(config.getUsername());
}
if (config.getPassword() != null) {
this.pool.setPassword(config.getPassword());
}
this.pool.setInitialSize(config.getInitialSize());
this.pool.setMaxActive(config.getMaxActive());
this.pool.setMaxIdle(config.getMaxIdle());
this.pool.setMinIdle(config.getMinIdle());
this.pool.setTestOnBorrow(config.isTestOnBorrow());
this.pool.setTestOnReturn(config.isTestOnReturn());
this.pool.setValidationQuery(config.getValidationQuery());
return this.pool;
}
@PreDestroy
public void close() {
if (this.pool != null) {
this.pool.close();
}
}
@Bean
public SqlSessionFactory sqlSessionFactoryBean() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource());
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sqlSessionFactoryBean.setMapperLocations(resolver.getResources(mapperLocations));
return sqlSessionFactoryBean.getObject();
}
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
}
UserDao.java
package cloud.simple.service.dao;
import java.util.List;
import cloud.simple.service.model.User;
public interface UserDao {
List<User> findAll();
}
UserService.java
package cloud.simple.service.domain;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import cloud.simple.service.dao.UserDao;
import cloud.simple.service.model.User;
@Service
@Transactional
public class UserService {
@Autowired
private UserDao userMapper;
public List<User> searchAll(){
List<User> list = userMapper.findAll();
return list;
}
}
User.java
package cloud.simple.service.model;
public class User {
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
UserController.java
package cloud.simple.service.web;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import cloud.simple.service.domain.UserService;
import cloud.simple.service.model.User;
@RestController
public class UserController {
@Autowired
UserService userService;
@RequestMapping(value="/user",method=RequestMethod.GET)
public List<User> readUserInfo(){
List<User> ls=userService.searchAll();
return ls;
}
}
src\main\resources\cloud\simple\service\dao
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cloud.simple.service.dao.UserDao">
  <select id="findAll" resultType="cloud.simple.service.model.User">
    select * from user
  </select>
</mapper>
src\main\resources\bootstrap.properties
#指定遠程加載配置信息的地址
#${config.port:8888}如果在命令行提供了config.port 參數,我們就用這個端口,否則就用 8888 端口
spring.cloud.config.uri=http://127.0.0.1:${config.port:8888}
#配置文件名稱config.name爲simple-service,config.profile爲dev.即{application}-{profile}.properties=cloud-config-dev.properties
spring.cloud.config.name=cloud-config
spring.cloud.config.profile=${config.profile:dev}
#service discovery url
eureka.client.serviceUrl.defaultZone=http\://localhost\:8761/eureka/,http\://zlhost\:8762/eureka/
#service name
spring.application.name=cloud-simple-service
server.port=8081


第七步:創建cloud-eureka-server 註冊服務器
所有的服務端及訪問服務的客戶端都需要連接到註冊管理器( eureka 服務器)。服務在啓動時
會自動註冊自己到 eureka 服務器,每一個服務都有一個名字,這個名字會被註冊到 eureka 服務
器。使用服務的一方只需要使用該名字加上方法名就可以調用到服務。
pom.xml
<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>
EurekaServer.java
@SpringBootApplication
//使用@EnableEurekaServer 註解就可以讓應用變爲 Eureka 服務器
@EnableEurekaServer
public class EurekaServer {
public static void main(String[] args) {
SpringApplication.run(EurekaServer.class, args);
}
}
application.properties
#server.port 配置 eureka 服務器端口號
server.port=8761
eureka.instance.hostname=localhost
#是否註冊自身到 eureka 服務器
eureka.client.registerWithEureka=false
#是否從 eureka 服務器獲取註冊信息
eureka.client.fetchRegistry=false
#設置 eureka 服務器所在的地址,查詢服務和註冊服務都需要依賴這個地址。
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
#manager url:http://localhost:8761/


//其他服務使用eureka服務器,只需添加@EnableDiscoveryClient註解就可以了
//然後在其配置文件中添加:
//eureka.client.serviceUrl.defaultZone=http\://localhost\:8761/eureka/
//spring.application.name=cloud-simple-service
//pom 文件也需要增加:
//<dependency>
//<groupId>org.springframework.cloud</groupId>
//<artifactId>spring-cloud-starter-eureka</artifactId>
//</dependency>








第八步:創建cloud-simple-ui客戶端
在spring boot中已經不推薦使用jsp,所以你如果使用jsp來實現ui端將會很麻煩.
這可能跟現在的開發主流偏重移動端有關,跟微服務有關,跟整個時代當前的技術需求有關。
單純以html來作爲客戶端,有很多好處.比如更利於使用高速緩存、使後臺服務無狀態話、更利於處理高併發、更利於頁面作爲服務、小服務組合成大服務等。
User.java
package cloud.simple.model;
public class User {
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
WebApplication.java
@SpringBootApplication
//配置本應用將使用服務註冊和服務發現
@EnableEurekaClient
//示啓用斷路器,斷路器依賴於服務註冊和發現
@EnableHystrix
public class WebApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(WebApplication.class, args);
}
}
UserService.java
@Service
public class UserService {
@Autowired
RestTemplate restTemplate;//restTemplate進行服務調用
final String SERVICE_NAME="cloud-simple-service";
//斷路器的基本作用就是@HystrixCommand註解的方法失敗後,系統將自動切換到fallbackMethod方法執行(回退機制)
//使用該功能允許快速失敗並迅速恢復或者回退並優雅降級
@HystrixCommand(fallbackMethod = "fallbackSearchAll")
public List<User> searchAll() {
return restTemplate.getForObject("http://"+SERVICE_NAME+"/user", List.class);
}
private List<User> fallbackSearchAll() {
System.out.println("HystrixCommand fallbackMethod handle!");
List<User> ls = new ArrayList<User>();
User user = new User();
user.setUsername("TestHystrix");
ls.add(user);
return ls;
}
}
UserController.java
@RestController
public class UserController {
@Autowired
UserService userService;
@RequestMapping(value="/users")
public ResponseEntity<List<User>> readUserInfo(){
List<User> users=userService.searchAll();
return new ResponseEntity<List<User>>(users,HttpStatus.OK);
} }
index.html
<html ng-app="users">
<head>
<script src="http://cdn.bootcss.com/angular.js/1.3.18/angular.min.js"></script>
<script src="http://cdn.bootcss.com/angular.js/1.3.18/angular-route.js"></script>
<script src="js/app.js"></script>
</head>
<body>
<div ng-view class="container">
</div>
</body>
</html>
user-page.html
<div>
<ul ng-controller="userCtr">
<li ng-repeat="item in userList">
{{item.username}}
</li>
</ul>
</div>
app.js
angular.module('users', ['ngRoute']).config(function ($routeProvider) {
$routeProvider.when('/', {
templateUrl: 'user-page.html',
controller: 'userCtr'
})
}).controller('userCtr', function ($scope, $http) {
$http.get('users').success(function (data) {
//alert(data+"");
$scope.userList = data;
});
});
//使用了angularJS庫
bootstrap.properties 
spring.cloud.config.uri=http://127.0.0.1:${config.port:8888}
spring.cloud.config.name=cloud-config
spring.cloud.config.profile=${config.profile:dev}
eureka.client.serviceUrl.defaultZone=http\://localhost\:8761/eureka/
spring.application.name=simple-ui-phone
pom.xml(因爲使用了斷路器,加入hystrix依賴)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
log4j.properties
log4j.rootCategory=INFO, CONSOLE
PID=????
LOG_PATTERN=[%d{yyyy-MM-dd HH:mm:ss.SSS}] log4j%X{context} - ${PID} %5p [%t] --- %c{1}: %m%n
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=${LOG_PATTERN}
log4j.category.org.hibernate.validator.internal.util.Version=INFO
log4j.category.org.apache.coyote.http11.Http11NioProtocol=INFO
log4j.category.org.apache.tomcat.util.net.NioSelectorPool=INFO
log4j.category.org.apache.catalina.startup.DigesterFactory=INFO
log4j.logger.org.apache=INFO


剩下最重要的事情就是進行部署了,我們以此使用命令啓動這些服務及應用:
java -jar cloud-config-server-0.0.1.jar 啓動配置服務器,固定綁定端口8888
java -jar cloud-eureka-server-1.0.0.jar 啓動註冊服務器,固定綁定端口8671
java -jar cloud-simple-service-1.0.0.jar --server.port=8081 >log8081.log 啓動後臺服務,綁定端口8081
java -jar cloud-simple-service-1.0.0.jar --server.port=8082 >log8082.log 啓動後臺服務,綁定端口8082
java -jar cloud-simple-ui-1.0.0.jar --server.port=8080 >log8080.log 啓動前端 ui 應用,綁定端口8080
運行 http://localhost:8080/即可看到運行的結果。其中“>log8080.log”表示輸出日誌到文件。
這裏運行了兩個cloud-simple-service實例,主要是爲了演示ribbon負載均衡。默認情況下使用
ribbon不需要再作任何配置,不過它依賴於註冊服務器。


















docker發佈spring cloud應用
Docker是一種虛擬機技術,在 linux 虛擬機技術 LXC 基礎上又封裝了一層,是基於 LXC 的容器技術。
可以把容器看做是一個簡易版的 Linux 環境(包括 root 用戶權限、進程空間、用戶空間和網絡空間等)
和運行在其中的應用程序。容器是用來裝東西的,Docker 可以裝載應用本身及其運行環境進容器
這是一個很小的文件,然後把這個文件扔到任何兼容的服務器上就可以運行,也是基於這一點
Docker 可以同時讓應用的部署、測試和分發都變得前所未有的高效和輕鬆!




創建cloud-simple-docker一個簡單的spring boot應用
MusicApplication.java
@SpringBootApplication
@RestController
public class MusicApplication {
@RequestMapping("/")
public String home() {
return "Hello Docker World";
}


public static void main(String[] args) {
SpringApplication.run(MusicApplication.class, args);
}
}
創建src/main/docker/Dockerfile文件
#------------------------------------------------------------------------------------
# 基於那個鏡像
FROM daocloud.io/java:8
# 將本地文件夾掛載到當前容器(tomcat使用)
VOLUME /tmp
# 拷貝文件到容器
ADD cloud-simple-docker-1.0.0.jar /app.jar
# 打開服務端口
EXPOSE 8080
# 配置容器啓動後執行的命令
RUN bash -c 'touch /app.jar'
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
#------------------------------------------------------------------------------------
Pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.5.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<properties>
<docker.image.prefix>springio</docker.image.prefix>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.7</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
部署docker yum -y install docker-io(Linux請先安裝)
將編譯後的 jar 文件考到服務器某個目錄,這裏是 tmp 目錄。然後將 Dockerfile 也考到該目
錄,最後進入到該目錄下運行命令(後面有個點):docker build -t local/cloud-simple-docker .
(此處後應內核版本低沒有親自嘗試)
運行成功後,就創建了一個鏡像,可以使用 docker images 來查看該鏡像。
有了鏡像就可以運行了,使用下面命令運行:
docker run -p 8080:8080 –t local/cloud-simple-docker
其中 8080:8080 表示本機端口映射到 Docker 實例端口
成功後可以使用 docker ps –a查看鏡像運行情況:
local/cloud-simple-docker | latest | 3ef51d55eb27 | 22 minutes ago | 667.2 MB
可以看到這個包括了 java 運行環境的 web 應用鏡像是667MB










使用spring boot和thrift、zookeeper建立微服務
thrift:由Facebook開發的遠程服務調用框架Apache Thrift,它採用接口描述語言定義並創建服務,支持可擴展的跨語言服務開發
所包含的代碼生成引擎可以在多種語言中,如 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk
等創建高效的、無縫的服務,其傳輸數據採用二進制格式,相對 XML 和 JSON 體積更小,對於高併發、大數據量和多語言的環境更有優勢。
zookeeper:
cloud-thrift-interface 接口及傳輸對象定義
cloud-thrift-server 服務提供方
cloud-thrift-client 服務調用方




第一步:cloud-thrift-interface
pom.xml
<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>cloud.simple</groupId>
<artifactId>cloud-thrift-interface</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>cloud-thrift-interface</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
</properties>
<repositories>
<repository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>public</id>
<name>Public Repositories</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>Public Repositories</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<defaultGoal>compile</defaultGoal>
</build>
</project>


創建src\main\resources\UserService.thrift
namespace java cloud.simple.service
struct UserDto {
1: i32 id
2: string username
}
service UserService {
UserDto getUser()
}
http://apache.fayea.com/thrift/可下載隨意版本,放入文件夾,加入環境變量
執行thrift -gen java %Path%UserService.thrift
接口定義完後,使用 thrift 命令生成對應的 java 文件,主要生成兩個文件,分別是
UserService.java 和 UserDto.java,把這兩個文件放入 cloud-thrift-interface 工程,因爲客戶端也需
要這個接口定義。




第二步:cloud-thrift-server
pom.xml
<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>cloud.simple</groupId>
<artifactId>cloud-thrift-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>cloud-thrift-server</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<repositories>
<repository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>public</id>
<name>Public Repositories</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>Public Repositories</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</pluginRepository>
</pluginRepositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
</properties>
<dependencies>
<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>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency>
<!-- 引入cloud-thrift-interface -->
<dependency>
<groupId>cloud.simple</groupId>
<artifactId>cloud-thrift-interface</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>org.apache.helix</groupId>
<artifactId>helix-core</artifactId>
<version>0.6.4</version>
</dependency>
<!-- zookeeper -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<defaultGoal>compile</defaultGoal>
</build>
</project>
ThriftConfig.java
@Configuration
public class ThriftConfig {
// 創建一個使用單個 worker 線程的 Executor,以無界隊列方式來運行該線程
ExecutorService executor = Executors.newSingleThreadExecutor();
@Bean
public TServerTransport tServerTransport() {
try {
return new TServerSocket(7911);
} catch (TTransportException e) {
e.printStackTrace();
}
return null;
}
@Bean
public TServer tServer() {
// 在服務的提供端需要實現接口,並且還要把實現類註冊到 thrift 服務器。
UserService.Processor processor = new UserService.Processor(new UserServiceImpl());
// UserServiceImpl 就是接口實現類,將其註冊爲Tserver。
TServer server = new TThreadPoolServer(new TThreadPoolServer.Args(tServerTransport()).processor(processor));
return server;
}
// 註冊完服務後,需要啓動 TServer,很顯然這個需要在線程裏啓動。
@PostConstruct
public void init() {
executor.execute(new Runnable() {
@Override
public void run() {
tServer().serve();
}
});
}
}
使用zookeeper進行服務名稱註冊
上面是註冊具體的服務執行類,這一步是將服務的實例註冊進 zookeeper,這樣才能實現負載均
衡。讓客戶端可以根據服務實例列表選擇服務來執行。當然這裏只需要註冊服務所在服務器的 IP
即可,因爲客戶端只要知道 IP,也就知道訪問那個 IP 下的該服務。
ZooKeeperConfig.java
@Configuration
public class ZooKeeperConfig {
@Value("${service.name}")
String serviceName;
@Value("${zookeeper.server.list}")
String serverList;
ExecutorService executor = Executors.newSingleThreadExecutor();
@PostConstruct
public void init() {
executor.execute(new Runnable() {
@Override
public void run() {
registService();
try {
Thread.sleep(1000 * 60 * 60 * 24 * 360 * 10);// 休眠十年(註冊服務十年)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
// 註冊服務
public ZkClient registService() {
String servicePath = "/" + serviceName;// 根節點路徑
ZkClient zkClient = new ZkClient(serverList);
boolean rootExists = zkClient.exists(servicePath);
if (!rootExists) {
zkClient.createPersistent(servicePath);
}
InetAddress addr = null;
try {
addr = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
String ip = addr.getHostAddress().toString();
String serviceInstance = System.nanoTime() + "-" + ip;
// zkClient.createEphemeral建立臨時節點,如果這臺服務器宕機,這個臨時節點是會被清除的,這樣客戶端在訪問時就不會再選擇該服務器上的服務。
zkClient.createEphemeral(servicePath + "/" + serviceInstance);
System.out.println("提供的服務爲:" + servicePath + "/" + serviceInstance);
return zkClient;
}
}
application.properties
service.name=cloud-thrift-service
zookeeper.server.list=127.0.0.1\:2181


第三步:cloud-thrift-client
pom.xml
<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>cloud.simple</groupId>
<artifactId>cloud-thrift-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>cloud-thrift-client</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<repositories>
<repository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>public</id>
<name>Public Repositories</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>Public Repositories</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</pluginRepository>
</pluginRepositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
</properties>


<dependencies>
<!--web應用基本環境配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--監控基本環境配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>cloud.simple</groupId>
<artifactId>cloud-thrift-interface</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>org.apache.helix</groupId>
<artifactId>helix-core</artifactId>
<version>0.6.4</version>
</dependency>
<!-- zookeeper -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<defaultGoal>compile</defaultGoal>
</build>
</project>
ZooKeeperConfig.java
@Configuration
public class ZooKeeperConfig {
@Value("${service.name}")
String serviceName;
@Value("${zookeeper.server.list}")
String zookeeperList;
ExecutorService executor = Executors.newSingleThreadExecutor();
// thrift實例列表
public static Map<String, UserService.Client> serviceMap = new HashMap<String, UserService.Client>();
@PostConstruct
private void init() {
executor.execute(new Runnable() {
@Override
public void run() {
startZooKeeper();
try {
Thread.sleep(1000 * 60 * 60 * 24 * 360 * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
// 註冊服務
private void startZooKeeper() {
List<String> currChilds = new ArrayList<String>();
String servicePath = "/" + serviceName;// 根節點路徑
ZkClient zkClient = new ZkClient(zookeeperList);
boolean serviceExists = zkClient.exists(servicePath);
if (serviceExists) {
currChilds = zkClient.getChildren(servicePath);
} else {
throw new RuntimeException("service not exist!");
}
for (String instanceName : currChilds) {
// 沒有該服務,建立該服務
if (!serviceMap.containsKey(instanceName)) {
serviceMap.put(instanceName, createUserService(instanceName));
}
}
// 註冊事件監聽
zkClient.subscribeChildChanges(servicePath, new IZkChildListener() {
// @Override
public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
// 實例(path)列表:當某個服務實例宕機,實例列表內會減去該實例
for (String instanceName : currentChilds) {
// 沒有該服務,建立該服務
if (!serviceMap.containsKey(instanceName)) {
serviceMap.put(instanceName, createUserService(instanceName));
}
}
for (Map.Entry<String, UserService.Client> entry : serviceMap.entrySet()) {
// 該服務已被移除
if (!currentChilds.contains(entry.getKey())) {
UserService.Client c = serviceMap.get(entry.getKey());
try {
c.getInputProtocol().getTransport().close();
c.getOutputProtocol().getTransport().close();
} catch (Exception e) {
e.printStackTrace();
}
serviceMap.remove(entry.getKey());
}
}
System.out.println(parentPath + "事件觸發");
}
});
}
// 創建一個服務實例
private UserService.Client createUserService(String serviceInstanceName) {
String ip = serviceInstanceName.split("-")[1];
TSocket transport = new TSocket(ip, 7911);
try {
transport.open();
} catch (TTransportException e) {
e.printStackTrace();
}
return new UserService.Client(new TBinaryProtocol(transport));
}
}
UserServiceProvider.java
@Component
public class UserServiceProvider {
public UserService.Client getBalanceUserService(){
Map<String, UserService.Client> serviceMap =ZooKeeperConfig.serviceMap;
//以負載均衡的方式獲取服務實例
for (Map.Entry<String, UserService.Client> entry : serviceMap.entrySet()) {
System.out.println("可供選擇服務:"+entry.getKey());
}
int rand=new Random().nextInt(serviceMap.size());
String[] mkeys = serviceMap.keySet().toArray(new String[serviceMap.size()]);
return serviceMap.get(mkeys[rand]);
}
}
UserController.java
@Controller
public class UserController {
@Autowired
UserServiceProvider userServiceProvider;
@ResponseBody
@RequestMapping(value = "/hello")
String hello() throws TException {
UserService.Client svr=userServiceProvider.getBalanceUserService();
UserDto userDto= svr.getUser();
return "hi "+userDto.getUsername();
}
}//最後控制層實現數據獲取


















spring boot 自動部署方案
現在主流的自動部署方案大都是基於Docker的了,但傳統的自動部署方案比較適合中小型公司,下面的方案就是比較傳統的自動部署方案
自動部署一般都是通過以下步驟進行的。
首選由持續性集成工具進行自動編譯產生項目的輸出,對於我們來說也就是 jar 包。
然後該 jar 經過測試就可以分發到各個服務器,各個服務器的監控腳本監控到該新版本
自動停止舊實例重新運行新實例。


詳細版本:
Jenkins 編譯的結果需要暫時存放,以便於測試人員拉取進行測試。這裏存放在 maven 庫中。
測試通過後也需要手動推送到生產環境,因爲不可能每個版本都推送到生產環境。生產環境需要一
臺 FTP 或 GIT、 SVN Server 作爲中轉機,暫存打包的應用,然後生產的服務器通過腳本輪詢該中
轉機獲得新的版本。獲得新的版本後,自動停止舊的版本,運行新的版本。










spring boot/cloud 應用監控
編寫監控程序:cloud-monitor-server
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>cloud</groupId>
<artifactId>cloud-monitor-server</artifactId>
<version>1.0.0</version>
<name>cloud-monitor-server</name>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.5.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>public</id>
<name>Public Repositories</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>Public Repositories</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</pluginRepository>
</pluginRepositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</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-eureka</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui</artifactId>
<version>1.3.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<defaultGoal>compile</defaultGoal>
</build>
</project>
SpringBootAdminApplication
@Configuration
@EnableAutoConfiguration
@EnableDiscoveryClient
@EnableAdminServer
public class SpringBootAdminApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootAdminApplication.class, args);
}
}
application.properties
server.port=8050
[email protected]@
endpoints.health.sensitive=false
eureka.instance.leaseRenewalIntervalInSeconds=5
#指定了服務註冊中心的位置
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
#在默認設置下,Eureka服務註冊中心也會將自己作爲客戶端來嘗試註冊它自己,所以我們需要禁用它的客戶端註冊行爲。
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
logback-spring.xml //當前日誌級別管理僅限 Logback
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<jmxConfigurator/>
</configuration>




被監聽程序需要先添加依賴
pom.xml
<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>
application.properties
spring.boot.admin.url=http://localhost:8050
#爲了在 Spring Boot Admin 的應用管理列表顯示被管理應用的版本號,你需要設置 info.version
[email protected]@
ClientEurekaSampleApplication.java
@SpringBootApplication
//添加 @EnableDiscoveryClient 或 @EnableEurekaClient 註解到啓動類上,將自己註冊到 Erueka Server。
@EnableEurekaClient
public class ClientEurekaSampleApplication {
    public static void main(String[] args) {
        SpringApplication.run(ClientEurekaSampleApplication.class, args);
    }
}
logback.xml(與上者平級)
<configuration>
    <include resource="org/springframework/boot/logging/logback/base.xml"/>
    <jmxConfigurator/>
</configuration>
//https://blog.csdn.net/kinginblue/article/details/52132113可參考
























hystrix-turbine 監控的使用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章