Spring Cloud
Spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等操作提供了一种简单的开发方式。
Spring Cloud包含了多个子项目(针对分布式系统中涉及的多个不同开源产品),比如:Spring Cloud Config、Spring Cloud Netflix、Spring Cloud CloudFoundry、Spring Cloud AWS、Spring Cloud Security、Spring Cloud Commons、Spring Cloud Zookeeper、Spring Cloud CLI等项目。
以下所有的Spring Cloud文章Demo都基于父工程parent实现
新建一个基础Maven工程
pom.xml
文件如下:
org.springframework.boot
spring-boot-starter-parent
1.5.2.RELEASE
1.8
UTF-8
eureka
config
zuul
zipkin
service-producer
service-consumer
service-consumer-feign
service-consumer-ribbon
service-consumer-ribbon-hystrix
org.springframework.cloud
spring-cloud-dependencies
Edgware.SR5
pom
import
org.springframework.boot
spring-boot-starter-test
org.apache.maven.plugins
maven-compiler-plugin
3.5.1
${java.version}
${project.build.sourceEncoding}
org.apache.maven.plugins
maven-resources-plugin
${project.build.sourceEncoding}
这里使用的org.springframework.boot版本为1.5.2.RELEASE
依赖的org.springframework.cloud版本为Edgware.SR5
为了配合后续数据库使用,请先执行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', '测试地址');
服务注册中心(Spring Cloud Eureka)
Spring Cloud Eureka是Spring Cloud Netflix项目下的服务治理模块。而Spring Cloud Netflix项目是Spring Cloud的子项目之一,主要内容是对Netflix公司一系列开源产品的包装,它为Spring Boot应用提供了自配置的Netflix OSS整合。通过一些简单的注解,开发者就可以快速的在应用中配置一下常用模块并构建庞大的分布式系统。它主要提供的模块包括:服务发现(Eureka),断路器(Hystrix),智能路由(Zuul),客户端负载均衡(Ribbon)等。
创建一个"服务注册中心"工程
在父工程中新建一个基础Spring的Maven Moudle工程命名为eureka
pom.xml
文件如下:
com.wkedong.springcloud
parent
0.0.1-SNAPSHOT
org.springframework.cloud
spring-cloud-starter-eureka-server
org.springframework.boot
spring-boot-starter-actuator
org.springframework.boot
spring-boot-maven-plugin
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
申明一个eureka服务很简单,通过@EnableEurekaServer
注解启动一个应用作为服务注册中心,提供给其他应用进行注册访问。
/**
* @author wkedong
*/
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(EurekaApplication.class).web(true).run(args);
}
}
@EnableEurekaServer
:表示该应用注册为eureka服务注册中心,提供给其他应用使用
@SpringBootApplication
:spring boot提供了一个统一的注解@SpringBootApplication,作为应用标识
@SpringBootApplication = (默认属性)@Configuration + @EnableAutoConfiguration + @ComponentScan。
- @Configuration:提到@Configuration就要提到他的搭档@Bean。使用这两个注解就可以创建一个简单的spring配置类,可以用来替代相应的xml配置文件。
@Configuration的注解类标识这个类可以使用Spring IoC容器作为bean定义的来源。@Bean注解告诉Spring,一个带有@Bean的注解方法将返回一个对象,该对象应该被注册为在Spring应用程序上下文中的bean。 - @EnableAutoConfiguration:能够自动配置spring的上下文,试图猜测和配置你想要的bean类,通常会自动根据你的类路径和你的bean定义自动配置。
- @ComponentScan:会自动扫描指定包下的全部标有@Component的类,并注册成bean,当然包括@Component下的子注解@Service,@Repository,@Controller。
启动一个注册中心所有需要一些基础的配置,这里使用yml格式作为配置文件
application.yml
如下:
server:
port: 6060 #服务端口号为 6060
eureka:
instance:
hostname: localhost #主机名
client:
healthcheck:
enabled: true #健康检查开启
registerWithEureka: false #禁止注册中心注册自己
fetchRegistry: false #禁止检索服务
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #注册中心地址
此时我们启动应用并访问 http://localhost:6060/ 将会看到下面的页面,由于我们没有注册其他的服务,所以没有发现任何服务。
服务提供方(service-producer)
有了服务注册中心,下面来新建一个服务的提供方即
service-producer
,并向eureka
中注册自己
在父工程中新建一个基础Spring的Maven Moudle工程命名为service-producer
pom.xml
如下:
com.wkedong.springcloud
parent
0.0.1-SNAPSHOT
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-eureka
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-starter-config
org.springframework.cloud
spring-cloud-sleuth-zipkin
org.springframework.cloud
spring-cloud-starter-sleuth
org.springframework.boot
spring-boot-starter-jdbc
com.alibaba
fastjson
1.2.39
mysql
mysql-connector-java
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.2
net.logstash.logback
logstash-logback-encoder
4.6
org.springframework.boot
spring-boot-maven-plugin
由于demo中后续使用到了MyBatis和json的相关操作,所以在service-producer中我们事先依赖了阿里的fastjson和mybatis相应的jar包。
然后我们对工程做一些配置工作,
bootstrap.yml
如下:
eureka:
client:
healthcheck:
enabled: true #健康检查开启
serviceUrl:
defaultZone: http://localhost:6060/eureka/ #注册中心服务地址
spring:
## 从配置中心读取文件
cloud:
config:
uri: http://localhost:6010/
label: develop
profile: dev
name: service-producer
application:
name: service-producer #当前服务ID
zipkin:
base-url: http://localhost:6040 #zipkin服务地址
sleuth:
enabled: true #服务追踪开启
sampler:
percentage: 1 #zipkin收集率
datasource: #数据库信息
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实体对象所在的路径位置
通过spring:application:name属性,我们可以指定微服务的名称后续在调用的时候只需要使用该名称就可以进行服务的访问。
eureka:client:serviceUrl:defaultZone属性对应服务注册中心的配置内容,指定服务注册中心的位置。
多端口设置
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属性设置使用配置文件。
server:port属性设置不同的端口。
接下来我们来实现服务的提供,应用启动类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路径
新建一个controller类来实现/testGet
,/testPost
,/testFile
,/testConfig
,/testRibbon
,/testFeign
,/testHystrix
接口
ProducerController.java
:
/**
* @author wkedong
*
* 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("======");
return producerService.testGet();
}
@PostMapping(value = "/testPost")
public String testPost(@RequestBody JSONObject jsonRequest) {
logger.info("======");
return producerService.testPost(jsonRequest);
}
@PostMapping(value = "/testFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) {
logger.info("======");
return file.getOriginalFilename();
}
@GetMapping(value = "/testConfig")
public String testConfig() {
logger.info("======");
return producerService.testConfig();
}
@GetMapping(value = "/testRibbon")
public String testRibbon() {
logger.info("======");
return producerService.testRibbon();
}
@GetMapping(value = "/testFeign")
public String testFeign() {
logger.info("======");
return producerService.testFeign();
}
@GetMapping(value = "/testHystrix")
public String testHystrix() {
logger.info("======");
return producerService.testHystrix();
}
}
controller调用的Service,新建了ProducerService
接口类及ProducerServiceImpl
实现类,分别如下:
/**
* @author wkedong
*
* 2019/1/15
*/
public interface ProducerService {
String testGet();
String testPost(JSONObject jsonRequest);
String testConfig();
String testRibbon();
String testFeign();
String testHystrix();
}
/**
* @author wkedong
*
* 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;
}
}
接口实现类里实现了后续各消费者所调用的各个接口功能,所以事先放出来
数据库操作的mapper文件如下:
/**
* @author wkedong
*
* 2019/1/15
*/
public interface UserMapper {
@Select("SELECT * FROM user")
List 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);
}
分别实现了增删查改基础的实现方法
对应的实体类如下:
/**
* @author wkedong
* 测试实体
* 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();
}
启动该工程后,再次访问:http://localhost:6060/ 可以如下图内容,我们定义的服务被成功注册了。
访问 http://localhost:6070/testGet 得到以下返回值:
Hello, Spring Cloud! My port is 6070 Get info By Mybatis is {"address":"江苏南京","birthday":"1994-12-21","name":"wkedong"}
文章目录:
- Spring Cloud(一)服务注册与发现
- Spring Cloud(二)配置中心
- Spring Cloud(三)服务消费
- Spring Cloud(四)服务容错保护
- Spring Cloud(五)服务网关
- Spring-Cloud(六)服务追踪
整体demo的GitHub地址:Spring-Cloud