本周投入了不少的精力着重研究了springCloud相关的基础知识,把网上的相关博文,资料翻了一遍,搭建出了第一个SpringCould的Demo集群,把SrpingCould的入门坑踩了一遍,在这记录一下,希望大家能少走一些弯路。
Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智能路由,微代理,控制总线)。简单来说,SpringCloud为开发人员提供了一系列贴有Spring品牌的适合于微服务应用开发的服务组件。
本文涉及到的主要内容如下图所示。
这里面涉及的内容较多,由于本文侧重实战就不详细说明了,请参考网络上的博客和https://www.springcloud.cc/spring-cloud-dalston.html与https://www.springcloud.cc/。
建立开发环境,我们一共需要5个工程来完成Demo的搭建,开发环境是maven,jdk8,intellij。
目录结构如下图所示,采用maven约定的结构。
demo-eureka工程
DemoApplication.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
application.properties
ps这个配置不可用于生产环境,缺乏优化
server.port=8181#端口
spring.application.name=mmb-eureka-server#服务名称
eureka.client.register-with-eureka=false#注册自己,用于集群形式
eureka.client.fetch-registry=false#若为true的话会报错
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.0modelVersion>
<groupId>com.examplegroupId>
<artifactId>demoartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>jarpackaging>
<name>demoname>
<description>Demo project for Spring Bootdescription>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>1.5.4.RELEASEversion>
<relativePath/>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
<spring-cloud.version>Dalston.SR1spring-cloud.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eureka-serverartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
DemoClient2Application.java
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;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class DemoClient2Application {
public static void main(String[] args) {
SpringApplication.run(DemoClient2Application.class, args);
}
}
DemoController.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@RestController
public class DemoController {
@Autowired
private WordFeignClient wordFeignClient;
@RequestMapping("/abc")
public String getSentence() {
return wordFeignClient.getWord() + wordFeignClient.getWord();
}
}
WordFeignClient.java
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Created by zhaoenwei on 2017/7/1.
*/
@FeignClient("mmb-eureka-subject")
public interface WordFeignClient {
@RequestMapping(value = "/", method = RequestMethod.GET)
String getWord();
}
application.properties
eureka.client.service-url.defaultZone=http://localhost:8181/eureka
server.port= 8182
spring.application.name=mmb-eureka-client2
eureka.instance.prefer-ip-address=true
mmb-eureka-subject.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
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.0modelVersion>
<groupId>com.examplegroupId>
<artifactId>demo-client2artifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>jarpackaging>
<name>demo-client2name>
<description>Demo project for Spring Bootdescription>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>1.5.4.RELEASEversion>
<relativePath/>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
<spring-cloud.version>Dalston.SR1spring-cloud.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-feignartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
<version>2.6.1version>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger-uiartifactId>
<version>2.6.1version>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
ZuulDemoApplication.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
@SpringCloudApplication
@EnableZuulProxy
public class ZuulDemoApplication {
@Bean
public SimpleFilter simpleFilter() {
return new SimpleFilter();
}
public static void main(String[] args) {
SpringApplication.run(ZuulDemoApplication.class, args);
}
}
SimpleFilter.java
package com.example.demo;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* Created by zhaoenwei on 2017/6/30.
*/
public class SimpleFilter extends ZuulFilter {
private static Logger log = LoggerFactory.getLogger(SimpleFilter.class);
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
Map map=request.getParameterMap();
JSONObject jsonMap= JSONObject.fromObject(map);
log.info(String.format("%s request to %s ZEW==>params:%s", request.getMethod(), request.getRequestURL().toString(),jsonMap.toString()));
return null;
}
}
application.properties
#path的url是被替换掉的也示例
#zuulIP:zuulPort/sentence2/abc->serviceIP:servicePort/abc
zuul.routes.sentence.path=/sentence2/**
zuul.routes.sentence.serviceId=mmb-eureka-client2
#zuul.routes.sentence.url=http://localhost:8182/sentence
spring.application.name=mmb-zuul-server
#下面那个参数是去掉zuul-prefix参数产生的前缀的,跟path一毛钱关系都没有
zuul.strip-prefix=false
server.port=8081
eureka.client.service-url.defaultZone=http://localhost:8181/eureka
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.0modelVersion>
<groupId>com.examplegroupId>
<artifactId>zuul-demoartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>jarpackaging>
<name>zuul-demoname>
<description>Demo project for Spring Bootdescription>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>1.5.4.RELEASEversion>
<relativePath/>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
<spring-cloud.version>Dalston.SR1spring-cloud.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-ribbonartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-zuulartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>net.sf.json-libgroupId>
<artifactId>json-libartifactId>
<version>2.4version>
<classifier>jdk15classifier>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
该配置中心设置是支持在git的统一配置管理,并支持rabbitmq的消息传播。也就是说该服务需要rabbitmq的环境。
DemoConfigApplication.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication
@EnableConfigServer
@EnableDiscoveryClient
public class DemoConfigApplication {
public static void main(String[] args) {
SpringApplication.run(DemoConfigApplication.class, args);
}
}
application.properties
server.port=8888
spring.application.name=config-server
eureka.client.service-url.defaultZone=http://localhost:8181/eureka
eureka.instance.prefer-ip-address=true
bootstrap.properties
spring.cloud.config.server.git.uri=https://github.com/zhaoenweiex/config-repo.git
management.security.enabled=false
spring.cloud.config.server.git.basedir=target/config
spring.cloud.config.server.git.search-paths= configclient
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
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.0modelVersion>
<groupId>com.examplegroupId>
<artifactId>demo-configartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>jarpackaging>
<name>demo-configname>
<description>Demo project for Spring Bootdescription>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>1.5.4.RELEASEversion>
<relativePath/>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
<spring-cloud.version>Dalston.SR1spring-cloud.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-serverartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-bus-amqpartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
DemoConfigApplication.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class DemoClientApplication {
public static void main(String[] args) {
SpringApplication.run(DemoClientApplication.class, args);
}
}
DemoController.java
package com.example.demo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.net.Inet4Address;
import java.net.UnknownHostException;
/**
* Created by zhaoenwei on 2017/6/29.
*/
@RefreshScope
@RestController
public class DemoController {
private static long timeStample=System.currentTimeMillis();
// @Autowired
// private ConstantProperties constantProperties;
//从config server拿到的配置数据
@Value("${words}")
private String wordsStr;
@RequestMapping("/")
public String getWord() {
String[] wordArray = wordsStr.split(",");
String address= null;
try {
address = Inet4Address.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
int i = (int)Math.round(Math.random() * (wordArray.length - 1));
return wordArray[i]+timeStample+ address;
}
}
application.properties
eureka.client.service-url.defaultZone=http://192.168.31.238:8181/eureka
server.port= ${PORT:${SERVER_PORT:0}}
spring.application.name=mmb-eureka-subject
eureka.instance.prefer-ip-address=true
spring.cloud.config.uri=http://localhost:8888
spring.cloud.config.profile=dev
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
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.0modelVersion>
<groupId>com.examplegroupId>
<artifactId>demo-clientartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>jarpackaging>
<name>demo-clientname>
<description>Demo project for Spring Bootdescription>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>1.5.4.RELEASEversion>
<relativePath/>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
<spring-cloud.version>Dalston.SR1spring-cloud.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-bus-amqpartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
本文展示了如何搭建基于SpringCloud的微服务框架,包括了负责服务注册于发现的Spring Eureka,负责基于客户端负载均衡的组件Srping Ribbon,负责统一配置的Spring Config,负责智能路由和网关的Sping Zuul以及消息更新的消息总线Spring Bus,还有简化Rest连接的模板Feign。
最后补充两点:
1.在springCloud中最为复杂和容易出错的的并非是业务代码开发,而是依赖与配置,所以关于.properties或.yaml都是使用SpringCloud的必背基础,
2.支撑业务的SpringBoot开发框架也是必不可少的基础知识。
下面是Demo项目的Git地址: