传统网站
三个模块,api,consumer,provider
新建maven项目
外部pom文件,注意对应springcloud和springboot版本
4.0.0
com.suda
springcloud
1.0-SNAPSHOT
springcloud-api
springcould-provider
springcloud-consumer
springcloud-eureka
pom
4.12
1.16.10
Hoxton.SR9
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
org.springframework.boot
spring-boot-dependencies
2.3.5.RELEASE
pom
import
mysql
mysql-connector-java
5.1.47
com.alibaba
druid
1.2.3
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.3
junit
junit
${junit.version}
org.projectlombok
lombok
${lombok.version}
log4j
log4j
1.2.17
ch.qos.logback
logback-core
1.3.0-alpha5
仅包含一个实体类
pom.xml
springcloud
com.suda
1.0-SNAPSHOT
4.0.0
com.suda
springcloud-api
1.0-SNAPSHOT
org.projectlombok
lombok
Department.class
package com.suda.springcloud.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true) //链式写法
public class Department implements Serializable {
private Long departmentId;
private String departmentName;
private String dbSource;
//这个数据存在哪个数据库,微服务中,一个服务对应一个数据库,同一个信息可能在不同数据库
public Department(String departmentName){
this.departmentName=departmentName;
}
}
提供所有服务
pom.xml
springcloud
com.suda
1.0-SNAPSHOT
4.0.0
som.suda
springcould-provider
com.suda
springcloud-api
1.0-SNAPSHOT
junit
junit
com.alibaba
druid
ch.qos.logback
logback-core
org.mybatis.spring.boot
mybatis-spring-boot-starter
org.springframework.boot
spring-boot-test
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-jetty
org.springframework.boot
spring-boot-devtools
mysql
mysql-connector-java
5.1.47
application.yml
server:
port: 8001
mybatis:
type-aliases-package: com.suda.springcloud.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
config-location: classpath:mybatis/mybatis-config.xml
# configuration:
# map-underscore-to-camel-case: true
spring:
application:
name: springcloud-provider
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8&useSSL=true&useLegacyDatetimeCode=false&serverTimezone=America/New_York
username: spark
password: 123456
mybatis-config.xml
dao/IDepartmentDao
package com.suda.springcloud.controller;
import com.suda.springcloud.pojo.Department;
import com.suda.springcloud.service.IDepartmentService;
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.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class DepartmentController {
@Autowired
private IDepartmentService departmentService;
@PostMapping("/department/add")
public boolean addDepartment(Department department){
return departmentService.addDepartment(department);
}
@GetMapping("/department/get/{id}")
public Department addDepartmentById(@PathVariable("id") Long id){
return departmentService.queryById(id);
}
@GetMapping("/department/list")
public List queryDepartmentById ( ){
return departmentService.queryAll();
}
}
mapper/departmentMapper.xml
insert into department(department_name, db_source) values (#{departmentName}, DATABASE())
service/IDepartmentService
package com.suda.springcloud.service;
import com.suda.springcloud.pojo.Department;
import java.util.List;
public interface IDepartmentService {
public boolean addDepartment(Department department);
public Department queryById(Long id);
public List queryAll();
}
service/DepartmentServiceImpl
package com.suda.springcloud.service;
import com.suda.springcloud.dao.IDepartmentDao;
import com.suda.springcloud.pojo.Department;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class DepartmentServiceImpl implements IDepartmentService{
@Autowired
private IDepartmentDao departmentDao;
public boolean addDepartment(Department department) {
return departmentDao.addDepartment(department);
}
public Department queryById(Long id) {
return departmentDao.queryById(id);
}
public List queryAll() {
return departmentDao.queryAll();
}
}
controller/DepartmentController
package com.suda.springcloud.controller;
import com.suda.springcloud.pojo.Department;
import com.suda.springcloud.service.IDepartmentService;
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.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class DepartmentController {
@Autowired
private IDepartmentService departmentService;
@PostMapping("/department/add")
public boolean addDepartment(Department department){
return departmentService.addDepartment(department);
}
@GetMapping("/department/get/{id}")
public Department addDepartmentById(@PathVariable("id") Long id){
return departmentService.queryById(id);
}
@GetMapping("/department/list")
public List queryDepartmentById ( ){
return departmentService.queryAll();
}
}
负责请求provider
pom.xml
springcloud
com.suda
1.0-SNAPSHOT
4.0.0
com.suda
springcloud-consumer
com.suda
springcloud-api
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-web
config/ConfigBean
package com.suda.springcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ConfigBean {
//注册 RestTemplate
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
conftoller/DepartmentConsumerController
package com.suda.springcloud.controller;
import com.suda.springcloud.pojo.Department;
import com.sun.org.apache.xpath.internal.operations.Bool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class DepartmentConsumerController {
//RestTemplate ... register it in spring,
//使用restful风格,从消费端访问生产端,获取数据。
@Autowired
private RestTemplate restTemplate;
private static final String rest_url_prefix = "http://localhost:8001";
@RequestMapping("/consumer/department/get/{id}")
public Department get(@PathVariable("id")Long id){
return restTemplate.getForObject(rest_url_prefix+"/department/get/"+id, Department.class);
}
@RequestMapping("/consumer/department/add")
public boolean add(Department department){
return restTemplate.postForObject(rest_url_prefix+"/department/add",department, Boolean.class);
}
@RequestMapping("/consumer/department/getall")
public List getList(){
return restTemplate.getForObject(rest_url_prefix+"/department/list",List.class);
}
}
application.yml
server:
port: 80
至此可以实现访问consumer来获得provider的服务,精髓在于使用restful风格,实现跨服务数据传输。
写一个服务注册模块,这部分是server端
pom.xml
springcloud
com.suda
1.0-SNAPSHOT
4.0.0
com.suda
springcloud-eureka
org.springframework.cloud
spring-cloud-starter-eureka-server
1.4.6.RELEASE
org.springframework.boot
spring-boot-devtools
application.yml
server:
port: 7001
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false # 是否向注册中心注册自己
fetch-registry: false
service-url: # 监控页面
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# defaultZone: http://localhost:7001/eureka/
EurekaServer_7001.java主函数,注意注解
package com.suda.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer // 服务端的启动类
public class EurekaServer_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7001.class, args);
}
}
向pom.xml中添加erreka依赖
org.springframework.cloud
spring-cloud-starter-eureka
1.4.6.RELEASE
org.springframework.boot
spring-boot-starter-actuator
departmentController类中添加服务查看功能
@Autowired
private DiscoveryClient client;
// 获取注册的微服务信息
@GetMapping("/department/discovery")
public Object discovery(){
List services = client.getServices();
System.out.println(services);
// 得到一个具体的微服务信息
List instances = client.getInstances("springcloud-provider");
for (ServiceInstance instance : instances) {
System.out.println(instance.getHost()+"\t"+instance.getInstanceId()+"\t"+instance.getUri());
}
return client;
}
在主类上加注解
package com.suda.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient //在服务启动后自动注册
@EnableDiscoveryClient //服务发现
public class DepartmentProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DepartmentProvider_8001.class, args);
}
}
application.yml添加注解
server:
port: 8001
mybatis:
type-aliases-package: com.suda.springcloud.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
config-location: classpath:mybatis/mybatis-config.xml
# configuration:
# map-underscore-to-camel-case: true
spring:
application:
name: springcloud-provider
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8&useSSL=true&useLegacyDatetimeCode=false&serverTimezone=America/New_York
username: spark
password: 123456
#eureka
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
instance:
instance-id: springcloud-provider #修改eureka上默认描述信息
info:
app.name: springcloud-eureka
效果
可见spring单体项目到springcloud只需要几个注解和几行代码,体现出非侵入性。
制作三个注册中心模块并互相绑定
修改eureka对应application.yml配置
server:
port: 7001
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false # 是否向注册中心注册自己
fetch-registry: false
service-url: # 监控页面
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# defaultZone: http://localhost:7001/eureka/
defaultZone: http://localhost:7002/eureka/,http://localhost:7003/eureka/
修改provider/application.yml配置
#eureka
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
instance:
instance-id: springcloud-provider #修改eureka上默认描述信息
info:
app.name: springcloud-eureka
启动三个注册中心和provider服务
问题,发现使用三个中心都使用localhost,三个中心无法互相发现,而且只有1-2个注册中心能发现服务,猜测是三台主机都叫localhost导致,故修改如下
修改主机host
修改eureka的application.yml
server:
port: 7003
eureka:
instance:
hostname: eureka7003
client:
register-with-eureka: false # 是否向注册中心注册自己
fetch-registry: false
service-url: # 监控页面
defaultZone: http://eureka7001:7001/eureka/,http://eureka7002:7002/eureka/
# defaultZone: http://localhost:7001/eureka/
修改provider的application.yml
server:
port: 8001
mybatis:
type-aliases-package: com.suda.springcloud.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
config-location: classpath:mybatis/mybatis-config.xml
# configuration:
# map-underscore-to-camel-case: true
spring:
application:
name: springcloud-provider
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8&useSSL=true&useLegacyDatetimeCode=false&serverTimezone=America/New_York
username: spark
password: 123456
#eureka
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: springcloud-provider #修改eureka上默认描述信息
info:
app.name: springcloud-eureka
展示,注册中心可以互相发现,服务也可以发现
zookeeper保证的cp,容忍服务可用性差,但一致性必须高,
在消费端修改
pom.xml新增依赖
org.springframework.cloud
spring-cloud-starter-ribbon
1.4.6.RELEASE
org.springframework.cloud
spring-cloud-starter-eureka
1.4.6.RELEASE
application.yml 配置eureka
server:
port: 80
#eureka config
eureka:
client:
register-with-eureka: false # 不需要注册消费端
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
configBean加注解
@Configuration
public class ConfigBean {
//注册 RestTemplate
@Bean
// 配置实现负载均衡
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
departmentConsumer.java 加注解
@SpringBootApplication
@EnableEurekaClient
public class DepartmentConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DepartmentConsumer_80.class,args);
}
}
controller/DepartmentConsumerController.java 修改访问地址
package com.suda.springcloud.controller;
import com.suda.springcloud.pojo.Department;
import com.sun.org.apache.xpath.internal.operations.Bool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class DepartmentConsumerController {
//RestTemplate ... register it in spring,
//使用restful风格,从消费端访问生产端,获取数据。
@Autowired
private RestTemplate restTemplate;
//单机用这个访问地址
// private static final String rest_url_prefix = "http://localhost:8001";
// 通过ribbon实现负载均衡,通过服务名来访问,实现动态访问
private static final String rest_url_prefix = "http://springcloud-provider";
@RequestMapping("/consumer/department/get/{id}")
public Department get(@PathVariable("id")Long id){
return restTemplate.getForObject(rest_url_prefix+"/department/get/"+id, Department.class);
}
@RequestMapping("/consumer/department/add")
public boolean add(Department department){
return restTemplate.postForObject(rest_url_prefix+"/department/add",department, Boolean.class);
}
@RequestMapping("/consumer/department/getall")
public List getList(){
return restTemplate.getForObject(rest_url_prefix+"/department/list",List.class);
}
}
再造两个生产者,对应数据库db01,db02
复制目录出现问题,导致8002,8003运行找不到主类
启动七个服务:
结果:
默认轮询查找,且找不到三
注意:在mapper中写增删查改的时候,不要加上数据库名,否则查找的数据库单一。
不能喝主类在一个目录,不然会被springscan到,所以可以放上一级目录
1.拷贝一份provider,新建hystrix
2.导入pom依赖
org.springframework.cloud
spring-cloud-starter-hystrix
1.4.6.RELEASE
3.controller/DepartmentController.java只写两个方法测试
package com.suda.springcloud.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.suda.springcloud.pojo.Department;
import com.suda.springcloud.service.IDepartmentService;
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.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class DepartmentController {
@Autowired
private IDepartmentService departmentService;
@GetMapping("/department/get/{id}")
@HystrixCommand(fallbackMethod = "hystrixGetById")
public Department getDepartmentById(@PathVariable("id") Long id) {
Department department = departmentService.queryById(id);
if(department == null){
throw new RuntimeException("the user is not exist");
}
return department;
}
public Department hystrixGetById(@PathVariable("id") Long id) {
return new Department().setDepartmentId(id)
.setDepartmentName("without the user information_hystrix")
.setDbSource("no this database in MySql");
}
// @PostMapping("/department/add")
// public boolean addDepartment(Department department){
// return departmentService.addDepartment(department);
// }
//
// @GetMapping("/department/get/{id}")
// public Department addDepartmentById(@PathVariable("id") Long id){
// return departmentService.queryById(id);
// }
//
// @GetMapping("/department/list")
// public List queryDepartmentById ( ){
// return departmentService.queryAll();
// }
@Autowired
private DiscoveryClient client;
// 获取注册的微服务信息
@GetMapping("/department/discovery")
public Object discovery(){
List services = client.getServices();
System.out.println(services);
// 得到一个具体的微服务信息
List instances = client.getInstances("springcloud-provider");
for (ServiceInstance instance : instances) {
System.out.println(instance.getHost()+"\t"+instance.getInstanceId()+"\t"+instance.getUri());
}
return client;
}
}
4.主类+熔断注解
package com.suda.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;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
@SpringBootApplication
@EnableEurekaClient //在服务启动后自动注册
@EnableDiscoveryClient //服务发现
@EnableCircuitBreaker //开启熔断
public class DepartmentProvider_hystrix {
public static void main(String[] args) {
SpringApplication.run(DepartmentProvider_hystrix.class, args);
}
}
结果:
服务降级是指某个服务不可用后,返回较友好的提示界面,并且不影响用户访问其他服务,需要自定义类,并实现之前写的IDepartmentClientService接口
package com.suda.springcloud.service;
import com.suda.springcloud.pojo.Department;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.List;
//服务降级
@Component
public class DepartmentClientServiceFallbackFactory implements FallbackFactory {
public IDepartmentClientService create(Throwable throwable) {
return new IDepartmentClientService() {
public Department queryById(Long id) {
return new Department().setDepartmentId(id)
.setDepartmentName("客户端提供了降级操作,这个服务已关闭")
.setDbSource("没有数据");
}
public List queryAll() {
return (List) new Department().setDepartmentId((long) 0)
.setDepartmentName("客户端提供了降级操作,这个服务已关闭")
.setDbSource("没有数据");
}
public Department addDepartment(Department department) {
return new Department().setDepartmentId((long) 0)
.setDepartmentName("客户端提供了降级操作,这个服务已关闭")
.setDbSource("没有数据");
}
};
}
}
配置监控客户端
pom
springcloud
com.suda
1.0-SNAPSHOT
4.0.0
com.suda
springcloud-consumer-dashboard
com.suda
springcloud-api
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-web
2.3.5.RELEASE
org.springframework.cloud
spring-cloud-starter-ribbon
1.4.6.RELEASE
org.springframework.cloud
spring-cloud-starter-eureka
1.4.6.RELEASE
org.springframework.cloud
spring-cloud-starter-hystrix
1.4.6.RELEASE
org.springframework.cloud
spring-cloud-starter-hystrix-dashboard
1.4.6.RELEASE
org.springframework.boot
spring-boot-starter-actuator
application.xml
server:
port: 9001
Dashboard9001
package com.suda.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableHystrixDashboard
@EnableCircuitBreaker
public class Dashboard9001 {
public static void main(String[] args) {
SpringApplication.run(Dashboard9001.class, args);
}
}
provider端
pom新增
org.springframework.cloud
spring-cloud-starter-hystrix
1.4.6.RELEASE
org.springframework.cloud
spring-cloud-starter-hystrix-dashboard
1.4.6.RELEASE
启动函数
@SpringBootApplication
@EnableEurekaClient //在服务启动后自动注册
@EnableDiscoveryClient //服务发现
@EnableCircuitBreaker
public class DepartmentProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DepartmentProvider_8001.class, args);
}
@Bean
public ServletRegistrationBean HystrixMetricsStreamServlet(){
ServletRegistrationBean bean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
bean.addUrlMappings("/actuator/hystrix.stream");
return bean;
}
}
新建路由模块
pom新增配置
org.springframework.cloud
spring-cloud-starter-zuul
1.4.6.RELEASE
application。yml
server:
port: 9527
spring:
application:
name: springcloud-zuul
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: zuul9527.com
prefer-ip-address: true
info:
app.name: springcloud
company.name: com.suda
zuul:
routes:
departments:
serviceId: springcloud-provider
path: /mydept/* #修改url路径
# ignored-services: springcloud-provider #不能用这个路径访问
# ignored-services: "*"
启动类
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
参考自狂神