第一章:
1. 项目构建:
springcloud-parent 项目名
1.1 要导入的依赖包:
spring-boot-starter-parent
spring-cloud-dependencies
1.2 product-common 模块名
1.2.1 准备实体类
@Data
public class Product {
private Long id;
private String name;
public Product() {}
public Product(Long id, String name) {
this.id = id;
this.name = name;
}
}
1.2.2 要导入的依赖包:
1.3 服务提供者:product-service-2010 模块名
1.3.1 yml:
server:
port: 2010
spring:
application:
name: product-service
1.3.2 测试类:
@RestController
@RequestMapping("/product")
public class ProductController {
@GetMapping("/{id}")
public Product getById(@PathVariable("id") Long id){
//正常请求应该调用service,并且service调用Mapper,现在直接返回
return new Product(id,"莫尔");
}
}
1.3.3 要导入的依赖包:
product-common
spring-boot-starter-web
spring-boot-starter-test
1.3.4 启动类:
@SpringBootApplication
public class ProductApplication2010 {
public static void main(String[] args) {
SpringApplication.run(ProductApplication2010.class,args);
}
}
1.3.5 注册到eureka:
1.3.5.1 导包:
spring-cloud-starter-netflix-eureka-client
1.3.5.2 yml配置:
eureka:
client:
service-url:
defaultZone: http://localhost:1010/eureka #告诉服务提供者要把服务注册到哪儿
instance:
prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
1.3.5.3 加注解:
@EnableEurekaClient //Eureka的客户端
1.3.6 集群:(修改yml配置就行了)
eureka:
client:
service-url:
defaultZone: http://eureka1:1010/eureka,http://eureka2:1011/eureka,http://eureka3:1012/eureka #告诉服务提供者要把服务注册到哪儿
instance:
prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
1.3.7 测试集群:http://localhost:2010/product/5
1.4 服务消费者:order-service-2020 模块名
1.4.1 yml:
server:
port: 2020
spring:
application:
name: order-service
1.4.2 controller:
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/{productId}")
public Product getByProductId(@PathVariable("productId") Long productId){
String url="http://localhost:2010/product/"+productId;
return restTemplate.getForObject(url, Product.class);
}
}
1.4.3 要导入的依赖包:
product-common
spring-boot-starter-web
spring-boot-starter-test
1.4.4 启动类:
@SpringBootApplication
public class OrderApplication2020 {
public static void main(String[] args) {
SpringApplication.run(OrderApplication2020.class,args);
}
}
1.4.5 配置类:
@Configuration
public class Confi {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
1.5.5 注册到eureka:
1.5.5.1 导包:
spring-cloud-starter-netflix-eureka-client
1.5.5.2 yml配置:
eureka:
client:
service-url:
defaultZone: http://localhost:1010/eureka #告诉服务提供者要把服务注册到哪儿
instance:
prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
1.5.5.3 加注解:
@EnableEurekaClient //Eureka的客户端
1.5.6 改造controller:
controller:
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/{productId}")
public Product getByProductId(@PathVariable("productId") Long productId){
List
//有可能同一个服务注册多次,但是现在只考虑一个
ServiceInstance instance = instances.get(0);
String ip = instance.getHost();
int port = instance.getPort();
String url = "http://"+ip+":"+port+"/product/"+productId;
return restTemplate.getForObject(url,Product.class);
}
}
1.5.7 测试:http://localhost:1010/
http://localhost:2020/order/5
1.5.8 集群:(修改yml配置就行了)
eureka:
client:
service-url:
defaultZone: http://eureka1:1010/eureka,http://eureka2:1011/eureka,http://eureka3:1012/eureka #告诉服务提供者要把服务注册到哪儿
instance:
prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
1.5.9 测试集群:http://localhost:2020/order/5
1.5 增加eureka-server-1010
1.5.1 导包:
spring-boot-starter-web
spring-boot-starter-test
spring-cloud-starter-netflix-eureka-server
1.5.2 yml配置:
server:
port: 1010
spring:
application:
name: eureka
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false #是否要注册到eureka
fetchRegistry: false #表示是否从Eureka Server获取注册信息
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #单机配置
1.5.3 启动类:
@SpringBootApplication
@EnableEurekaServer //标识是注册中心
public class EurekaServer {
public static void main(String[] args) {
SpringApplication.run(EurekaServer.class,args);
}
}
1.5.4 访问:
localhost:1010
1.5.5 eureka集群:
application.yml:
spring:
profiles:
active: eureka1
application-eureka1.yml:
server:
port: 1010
spring:
application:
name: eureka-server-1
eureka:
instance:
hostname: eureka1
client:
registerWithEureka: true #是否要注册到eureka
fetchRegistry: true #表示是否从Eureka Server获取注册信息
serviceUrl:
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #单机配置
defaultZone: http://eureka1:1010/eureka,http://eureka2:1011/eureka,http://eureka3:1012/eureka
application-eureka2.yml:
server:
port: 1011
spring:
application:
name: eureka-server-2
eureka:
instance:
hostname: eureka2
client:
registerWithEureka: true #是否要注册到eureka
fetchRegistry: true #表示是否从Eureka Server获取注册信息
serviceUrl:
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #单机配置
defaultZone: http://eureka1:1010/eureka,http://eureka2:1011/eureka,http://eureka3:1012/eureka
application-eureka3.yml:
server:
port: 1012
spring:
application:
name: eureka-server-3
eureka:
instance:
hostname: eureka3
client:
registerWithEureka: true #是否要注册到eureka
fetchRegistry: true #表示是否从Eureka Server获取注册信息
serviceUrl:
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #单机配置
defaultZone: http://eureka1:1010/eureka,http://eureka2:1011/eureka,http://eureka3:1012/eureka
1.5.6 测试:
eureka1:1010
eureka2:1011
eureka3:1012
1.6 多个服务提供者时的负载均衡:
ribbon的使用:
1.6.1 导包:
order-service-2020的pom中:
spring-cloud-starter-netflix-ribbon
1.6.2 配置类加注解:
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
1.6.3 order-service-2020的controller改造:
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/{productId}")
public Product getByProductId(@PathVariable("productId") Long productId){
String url = "http://PRODUCT-SERVICE/product/"+productId;
return restTemplate.getForObject(url,Product.class);
}
}
1.6.4 product-service-2010 的controller改造:
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ApplicationContext context;
@GetMapping("/{id}")
public Product getById(@PathVariable("id") Long id){
System.out.println("getById......");
String[] activeProfiles = context.getEnvironment().getActiveProfiles();
System.out.println(activeProfiles[0]);
//正常请求应该调用service,并且service调用Mapper,现在直接返回
return new Product(id,"莫尔"+activeProfiles[0]);
}
}
1.6.5 yml配置:
application.yml:
spring:
profiles:
active: server1
application-server1.yml:
server:
port: 2010
spring:
application:
name: product-service
eureka:
client:
service-url:
defaultZone: http://eureka1:1010/eureka,http://eureka2:1011/eureka,http://eureka3:1012/eureka #告诉服务提供者要把服务注册到哪儿
instance:
prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
application-server2.yml:
server:
port: 2011
spring:
application:
name: product-service
eureka:
client:
service-url:
defaultZone: http://eureka1:1010/eureka,http://eureka2:1011/eureka,http://eureka3:1012/eureka #告诉服务提供者要把服务注册到哪儿
instance:
prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
application-server3.yml:
server:
port: 2012
spring:
application:
name: product-service
eureka:
client:
service-url:
defaultZone: http://eureka1:1010/eureka,http://eureka2:1011/eureka,http://eureka3:1012/eureka #告诉服务提供者要把服务注册到哪儿
instance:
prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
1.5.6 配置轮询或权重:
配置类加方法:
配置类:
@Configuration
public class Confi {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
//配置内置负载均衡
@Bean
public IRule rule(){
return new WeightedResponseTimeRule();
}
}
1.5.7 优化配置:超时和饥饿加载
ribbon:
connectTimeout: 3000 #链接超时时间
readTimeout: 3000 #读取超时时间
maxAutoRetries: 1 #重试机制:同一台实例最大重试次数
maxAutoRetriesNextServer: 1 #重试负载均衡其他的实例最大重试次数
okToRetryOnAllOperations: false #是否所有操作都重试,因为针对post请求如果没做幂等处理可能会造成数据多次添加/修改
eager-load:
enabled: true
clients: product-service #如果不配置,所有都饥饿加载,当然可以一个一个配置
1.5.8 测试:
http://localhost:2020/order/5
1.7 多个服务提供者时使用feign的负载均衡:拷贝order-service
1.7.1 yml配置:
去掉ribbon相关。
ribbon:
ConnectTimeout: 3000
ReadTimeout: 6000
1.7.2 导包:
去掉ribbon相关。
spring-cloud-starter-openfeign
1.7.3 只要controller进行改造:
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private ProductClient productClient;
@GetMapping("/{productId}")
public Product getByProductId(@PathVariable("productId") Long productId){
System.out.println(productClient.getClass());
return productClient.getById(productId);
}
}
1.7.4 写client接口:
@RequestMapping("/product")
@FeignClient("PRODUCT-SERVICE")
public interface ProductClient {
@GetMapping("/{id}")
Product getById(@PathVariable("id") Long id);
}
1.7.5 启动类:
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
//扫描当前包及其子孙包下面加了@FeignClient这个注解的即可
//会为这些接口创建本地代码对象,代理对象的底层其实就是通过ribbon完成服务的远程调用
public class OrderApplication2020 {
public static void main(String[] args) {
SpringApplication.run(OrderApplication2020.class,args);
}
}
1.8 服务提供者使用feign时的熔断:拷贝order-service
1.8.1 创建一个client包:
类1:
@RequestMapping("/product")
@FeignClient(value = "PRODUCT-SERVICE",fallbackFactory = RroductClientFallbackFactory.class)
public interface ProductClient {
@GetMapping("/{id}")
Product getById(@PathVariable("id") Long id);
@GetMapping()
List
}
类2:
@Component
public class RroductClientFallbackFactory implements FallbackFactory
@Override
public ProductClient create(Throwable throwable) {
return new ProductClient() {
@Override
public Product getById(Long id) {
return new Product(id,"报错!");
}
@Override
public List
System.out.println("报错了!");
return new ArrayList<>();
}
};
}
}
1.8.2 controller中:
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private ProductClient productClient;
@GetMapping("/{productId}")
public Product getByProductId(@PathVariable("productId") Long productId){
System.out.println(productClient.getClass());
return productClient.getById(productId);
}
}
1.8.3 yml配置:
feign:
hystrix:
enabled: true #开启熔断支持
client:
config:
remote-service: #服务名,填写default为所有服务
connectTimeout: 3000
readTimeout: 3000
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 3000
1.9 histrx
1.9.1 拷贝服务提供者
1.9.2 Pom
spring-cloud-starter-netflix-hystrix
1.9.3 Yml
server:
port: 2015
spring:
application:
name: product-service
eureka:
client:
service-url:
defaultZone: http://eureka1:1010/eureka,http://eureka2:1011/eureka,http://eureka3:1012/eureka #告诉服务提供者要把服务注册到哪儿
instance:
prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
ribbon:
MaxAutoRetries: 1 #最大重试次数
MaxAutoRetriesNextServer: 1 #切换实例的重试次数
OkToRetryOnAllOperations: false # 对所有的操作请求都进行重试,
ConnectTimeout: 1000 #请求连接的超时时间
ReadTimeout: 1800 #请求处理的超时时间hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 3000 #hystrix超时,置thread和semaphore两种隔离策略的超时时间
1.9.4 代码
启动类:@EnableHystrix //开启断路支持
ProductController中:@HystrixCommand(fallbackMethod = "getByIdFallBack")
1.9.5 测试
2.0 zuul的使用:
2.0.1 导包:
spring-boot-starter-web
spring-boot-starter-test
spring-cloud-starter-netflix-eureka-client
spring-cloud-starter-netflix-zuul
2.0.2 yml配置:
server:
port: 1020
spring:
application:
name: zuul-gateway
eureka:
client:
service-url:
defaultZone: http://eureka1:1010/eureka,http://eureka2:1011/eureka,http://eureka3:1012/eureka #告诉服务提供者要把服务注册到哪儿
instance:
prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
zuul:
retryable: true #可重试
routes:
myProduct.serviceId: product-service #服务名称
myProduct.path: /myProduct/** # 把myProduct打头的所有请求都转发给product-service
myOrder.serviceId: order-service #服务名称
myOrder.path: /myOrder/** # 把myOrder打头的所有请求都转发给order-service
ignored-services: "*" #所有服务都不允许以服务名来访问
prefix: "/services" #统一前缀
ribbon:
ConnectTimeout: 250 # 连接超时时间(ms)
ReadTimeout: 2000 # 通信超时时间(ms)
OkToRetryOnAllOperations: true # 是否对所有操作重试
MaxAutoRetriesNextServer: 2 # 同一服务不同实例的重试次数
MaxAutoRetries: 1 # 同一实例的重试次数
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMillisecond: 3000 # 熔断超时时长:3000ms
2.0.3 启动类:
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy //启动zuul网关
public class ZuulGatewayApp1020 {
public static void main(String[] args) {
SpringApplication.run(ZuulGatewayApp1020.class,args);
}
2.0.4 测试:http://localhost:1020/services/myOrder/order/5
2.0.5 过滤器:
//交给Spring管理
@Component
public class LoginFilter extends ZuulFilter {
//过滤器类型-前置过滤器
@Override
public String filterType() {
return "pre";
}
//同一种过滤器执行顺序
@Override
public int filterOrder() {
return 1;
}
//时候过滤
@Override
public boolean shouldFilter() {
//现在认为所有的都要拦截
return true;
}
//真正业务逻辑
@Override
public Object run() throws ZuulException {
//1 获取请求上下文对象
RequestContext context = RequestContext.getCurrentContext();
//2 获取请求对象
HttpServletRequest request = context.getRequest();
//3 从请求头中获取token
String token = request.getHeader("token");
//4 登录判断-是否有token,并且redis可以获取, 现在模拟只需要判断null
if (StringUtils.isEmpty(token)){
context.setSendZuulResponse(false); //出错了
context.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); //401 未登录
}
//5 权限判断 //@TODO
//放行
return null;
}
}
2.0.6 用config的gitee改造zuul
2.0.6.1 导包:
spring-cloud-starter-config
2.0.6.2 bootstrap.yml配置:
spring:
cloud:
config:
name: zuul #gitee上面名称
profile: dev #环境
label: master #分支
uri: http://127.0.0.1:1030 #配置configserver
application.yml配置:
spring:
application:
name: zuul-gateway
2.1 config配置
2.1.1 创建项目config-server-1030
2.1.2 导包:
spring-boot-starter-web
spring-boot-starter-test
spring-cloud-config-server
2.1.3 yml配置:
server:
port: 1030
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://gitee.com/krator/test.git
username: xx
password: xx
2.1.4 启动类:
@SpringBootApplication
@EnableConfigServer //启动configserver
public class ConfigServerApp1030 {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApp1030.class,args);
}
}
2.1.5 测试:http://localhost:1030/zuul/dev
2.2 热更新
2.2.1(config-server中)
2.2.1.1 导包:
spring-cloud-starter-bus-amqp
spring-boot-starter-actuator
fastjson
2.2.1.2 yml配置增加:
rabbitmq: #集成RabbitMQ如果配置是默认,可以省略
host: localhost #mq连接地址
port: 5672 #mq端口
username: guest
password: guest
#actuator配置
management:
endpoint:
health:
show-details: always #打印日志
endpoints:
web:
exposure:
include: "*" #向外暴露的接口,这里*则表示所有的actuator接口都可以被访问
enabled-by-default: true #开启actuator监控
2.2.2 (zuul-gateway中yml)
2.2.2.1 spring:
cloud:
config:
name: application-zuul #gitee上面名称
profile: dev #环境
label: master #分支
uri: http://127.0.0.1:1030 #配置configserver
bus: #这行代码很重要,根据官方定义的标准来的 ,就是为了产生一个bus.id
id: ${spring.application.name}:${spring.cloud.config.profile}:${random.value}
rabbitmq: #集成RabbitMQ如果配置是默认,可以省略
host: localhost #mq连接地址
port: 5672 #mq端口
username: guest
password: guest
2.2.3 (eureka中yml)
2.2.3.1 spring:
cloud:
config:
name: application-eureka #gitee上面名称
profile: dev #环境
label: master #分支
uri: http://127.0.0.1:1030 #配置configserver
bus: #这行代码很重要,根据官方定义的标准来的 ,就是为了产生一个bus.id
id: ${spring.application.name}:${spring.cloud.config.profile}:${random.value}
rabbitmq: #集成RabbitMQ如果配置是默认,可以省略
host: localhost #mq连接地址
port: 5672 #mq端口
username: guest
password: guest
2.2.4 修改gitee的webhooks地址为内网穿透的修改地址
2.2.5 config-server中增加配置类:
@Component
public class WebHooksFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String url = new String(httpServletRequest.getRequestURI());
if(!url.endsWith("/bus-refresh")){
filterChain.doFilter(servletRequest,servletResponse);
return;
}
RequestWrapper requestWrapper = new RequestWrapper(httpServletRequest);
filterChain.doFilter(requestWrapper, servletResponse);
}
private class RequestWrapper extends HttpServletRequestWrapper {
public RequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public ServletInputStream getInputStream() throws IOException {
byte[] bytes = new byte[0];
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ServletInputStream servletInputStream = new ServletInputStream() {
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return byteArrayInputStream.read() == -1 ? true : false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
};
return servletInputStream;
}
}
}
2.2.6 启动类:加@ServletComponentScan("cn.itsource.config")



