SpringCloud高级阶段上 传送门
第一章 负载均衡Ribbon
第二章 声明式调用Feign
第三章 服务容错保护Hystrix
SpringCloud高级阶段中(当前所在位置)
第四章 如何设计微服务
第五章 服务网关Zuul
第六章 分布式配置中心
SpringCloud高级阶段下 传送门
第七章 消息总线Bus
第八章 消息驱动Stream
第九章 分布式服务跟踪Sleuth
如下图所示:
模拟电商项目中的订单模块, 因此需要添加模拟登陆 ,浏览商品 ,支付功能 ,订单功能等.
使用的最核心的设计模式为代理设计模式 ,通过Consumer对用户进行代理来进行相关的操作
用户表,存放用户的相关属性,手动添加相关数据
CREATE TABLE `user` (
`id` int(10) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`user_name` varchar(50) DEFAULT NULL COMMENT '用户名',
`password` varchar(50) NOT NULL COMMENT '密码',
`email` varchar(50) NOT NULL COMMENT 'email',
`deleted` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT ' 删除标志,默认 0 不删除,1 删除',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='用户表';
产品表,存放产品的相关属性 , 手动添加相关数据
CREATE TABLE `product` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL COMMENT '产品名称',
`status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '产品状态:0 待 审,1 上架,2 下架,3 停售,4 测试',
`price` int(10) NOT NULL COMMENT '产品价格 单位分',
`detail` text COMMENT '产品详情',
`deleted` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT ' 删除标志,默认 0 不删除,1 删除',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更 新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='产品信息';
订单表,存放订单的相关信息 , 手动添加相关数据
CREATE TABLE `orders` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`product_id` int(10) NOT NULL DEFAULT '0' COMMENT '产品 ID',
`price` int(10) DEFAULT '0' COMMENT '价格',
`user_id` int(10) DEFAULT '0' COMMENT '用户账号 ID',
`trade_id` int(10) DEFAULT '0' COMMENT '交易号 ID',
`trade_status` tinyint(1) DEFAULT '0' COMMENT '支付状态 0=未支付 \r\n\r\n \r\n \r\n1=已支付',
`deleted` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT ' 删除标志,默认 0 不删除,1 删除',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更 新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
4 Trade 表
交易表,存放交易的相关信息 , 手动添加相关数据
CREATE TABLE `trade` (
`id` int(10) NOT NULL AUTO_INCREMENT COMMENT 'IID',
`order_id` int(10) NOT NULL COMMENT '订单 ID',
`user_id` int(10) NOT NULL COMMENT '用户 ID',
`price` int(10) NOT NULL COMMENT '支付价',
`pay_status` tinyint(4) NOT NULL COMMENT '1 未付款 2 付款中 3 付款失败 4 付款完成',
`pay_type` tinyint(4) NOT NULL COMMENT '支付类型:1-支付宝支付, 2-网银在线,3-银联,4-微信支付',
`gateway_pay_num` varchar(30) DEFAULT NULL COMMENT '网关支 付流水号',
`gateway_pay_time` datetime DEFAULT NULL COMMENT '网关支付 时间',
`gateway_pay_price` int(10) NOT NULL DEFAULT '0' COMMENT '网关 实际支付金额',
`deleted` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT ' 删除标志,默认 0 不删除,1 删除',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更 新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='交易';
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.7.RELEASEversion>
parent>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Greenwich.SR2spring-cloud.version>
<maven-jar-plugin.version>2.6maven-jar-plugin.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
2.创建产品服务的提供者项目,添加相关坐标
勿忘添加服务接口项目的坐标
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.7.RELEASEversion>
parent>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Greenwich.SR2spring-cloud.version>
<maven-jar-plugin.version>2.6maven-jar-plugin.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>1.1.1version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.0.9version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>ah.szxy.springcloudgroupId>
<artifactId>E-Book-Trade-ServiceartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
dependencies>
3.修改全局配置文件application.yml的参数
项目名,
jdbc数据库连接参数,
该项目的端口号,
mybatis参数配置中实体类所在包
mapper.xml配置文件所在地址
Eureka注册中心地址,可以不配置,但需要将参数注释
spring:
application:
name: E-Book-Product-Provider
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/book-product?useUnicode=true&characterEncoding=gbk&useJDBCCompliantTimezoneShift=true&serverTimezone=UTC
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
server:
port: 8001
compression: #springboot的gzip配置
enabled: true #是否启用压缩
mime-types: #配置压缩支持的 MIME TYPE
- application/json,application/ xml,text/html,text/xml,text/plain
mybatis:
type-aliases-package: ah.szxy.product.pojo
mapper-locations:
- classpath:ah/szxy/product/mapper/*.xml
eureka:
client:
serviceUrl:
defaultZone: http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
4.使用Mybatis逆向工程生成实体类,mapper接口与mapper.xml文件
点击查看MyBatis Generator( 逆向工程以及源码分析 )
5.将逆向工程生成的实体类放入服务接口项目中,并编写业务层访问代码
/**
* 产品服务接口
* @author chy
*
*/
@RequestMapping("/product")
public interface ProductService {
@RequestMapping(value="/find",method=RequestMethod.GET)
public List<Product> findAll();
}
6.将逆向工程生成的Mapper接口与Mapper映射配置文件放入服务提供者项目下
7.业务层接口实现类,作用是调用Mapper接口,进而实现对数据库的查询操作
/**
* 这里通过对mapper的操作实现对数据库的操作-查询所有
* @author chy
*
*/
@Service
public class ProductServiceImpl {
@Autowired
private ProductMapper mapper;
public List<Product> findAll(){
ProductExample example = new ProductExample();
return this.mapper.selectByExampleWithBLOBs(example);//查询的条件中带有大字段
}
}
8.Controller
注意:
1.需要实现服务接口项目中的相关项目接口
2.@RestController ,会在前端页面返回Json串
@RestController
public class ProductController implements ProductService{
@Autowired
private ProductServiceImpl productServiceImpl;
@Override
public List<Product> findAll() {
return this.productServiceImpl.findAll();
}
}
1.创建项目,修改pom文件
因为继承服务接口项目中的接口,所以需要添加所有服务接口项目的坐标
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.7.RELEASEversion>
parent>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Greenwich.SR2spring-cloud.version>
<maven-jar-plugin.version>2.6maven-jar-plugin.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
<dependency>
<groupId>ah.szxy.springcloudgroupId>
<artifactId>E-Book-User-ServiceartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>ah.szxy.springcloudgroupId>
<artifactId>E-Book-Trade-ServiceartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>ah.szxy.springcloudgroupId>
<artifactId>E-Book-Product-ServiceartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>ah.szxy.springcloudgroupId>
<artifactId>E-Book-Order-ServiceartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<optional>trueoptional>
dependency>
dependencies>
2.修改全局配置文件application.yml
spring:
application:
name: E-Book-Consumer
server:
port: 8888
eureka:
client:
serviceUrl:
defaultZone: http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
3.创建四个消费者服务接口,继承服务接口项目中的接口
/**
* 调用订单服务
* @FeignClient 声明式调用服务,需要指明提供者的名称
* 继承服务接口项目的接口
*
* @author chy
*
*/
@FeignClient("E-Book-Order-Provider")
public interface ConsumerOrderService extends OrderService{
}
/**
* 调用产品服务
* @FeignClient 声明式调用服务,需要指明提供者的名称
* 继承服务接口项目的接口
*
* @author chy
*
*/
@FeignClient("E-Book-Product-Provider")
public interface ConsumerProductService extends ProductService{
}
/**
* 调用交易服务
* @FeignClient 声明式调用服务,需要指明提供者的名称
* 继承服务接口项目的接口
*
* @author chy
*
*/
@FeignClient("E-Book-Trade-Provider")
public interface ConsumerTradeService extends TradeService {
}
/**
* 调用用户服务
* @FeignClient 声明式调用服务,需要指明提供者的名称
* 继承服务接口项目的接口
*
* @author chy
*
*/
@FeignClient("E-Book-User-Provider")
public interface ConsumerUserService extends UserService {
}
4.将所有的业务逻辑提升到Controller ,实现相关的业务逻辑
/**
* 创建订单
* @author chy
*
*/
@RestController
public class ConsumerController {
@Autowired
private ConsumerOrderService orderService;
@Autowired
private ConsumerProductService productService;
@Autowired
private ConsumerUserService userService;
@Autowired
private ConsumerTradeService tradeService;
/**
* 模拟内容: 登录 查看产品 下订单
* 1. 测试登录 账号 admin admin
* 2. 查看所有产品列表
* 3. 选第一款产品,下订单
* 4. 实现订单交易支付
* 5. 查看所有的订单信息
*/
@RequestMapping(value="/create",method=RequestMethod.GET)
public List<Orders> createOrder() {
System.out.println("++++++++++创建订单是测试是否登录++++++++");
//创建订单是测试是否登录
Integer userid = this.login();
System.out.println(userid);
System.out.println("++++++++++++++++++");
System.out.println("++++++++++登陆成功后,查看所有商品+++++++++");
//登陆成功后,查看所有商品
List<Product> list = this.productService.findAll();
for(Product product:list) {
System.out.println(product.getName());
}
System.out.println("+++++++++++++++++++");
System.out.println("+++++++++下单++++++++++");
//选择第一款商品下单
Product product = list.get(0);
Integer orderid=1004;
//创建订单
Orders order = new Orders();
order.setId(orderid);
order.setUserId(userid);
order.setProductId(product.getId());
order.setPrice(product.getPrice());
order.setDeleted((byte) 0);//0不删除,1删除
//调用Order服务完成持久化订单
Integer result = this.orderService.addOrder(order);
System.out.println("交易订单结果"+result);
System.out.println("++++++++++++++++++++++++");
System.out.println("+++++++++++创建交易订单+++++++++++++");
Trade trade = new Trade();
trade.setOrderId(orderid);
trade.setUserId(userid);
trade.setPrice(order.getPrice());
trade.setPayStatus((byte) 4);//'1 未付款 2 付款中 3 付款失败 4 付款完成',
trade.setPayType((byte) 4);//'支付类型:1-支付宝支付, 2-网银在线,3-银联,4-微信支付',
trade.setGatewayPayNum(new Date().getTime()+"");
trade.setGatewayPayPrice(order.getPrice());
trade.setGatewayPayTime(new Date());
trade.setDeleted((byte) 0);
this.tradeService.addTrade(trade);
//查询所有订单并返回
List<Orders> orderlist = this.orderService.findAll();
return orderlist;
}
/**
* 用户登陆
* @return
*/
private Integer login() {
String userName="admin";
String password="admin";
User user = this.userService.userLogin(userName, password);
if (user !=null && user.getUserName().length() >=0) {
System.err.println("登陆成功");
return user.getId();
}
System.out.println("登陆失败");
return null;
}
}
5.启动类
因为使用声明式调用,需要添加@EnableFeignClients
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class StartClass {
public static void main(String[] args) {
SpringApplication.run(StartClass.class, args);
}
}
1.修改Trade的pom文件
添加Order接口项目的坐标
添加Feign的坐标
<dependency>
<groupId>ah.szxy.springcloudgroupId>
<artifactId>E-Book-Order-ServiceartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
2.添加提供者的代理消费接口
@FeignClient("E-Book-Order-Provider") //接口不允许多类继承,而接口可以多继承接口
public interface ProviderOrderService extends OrderService{
}
3.添加消费者添加的方法,在这里调用Order的服务,查询并且修改Order
需要在Order接口项目中添加接口,提供者项目中创建实现类以及Controller方法(查询,更新)
@Override
public void addTrade(@RequestBody Trade trade) {
//添加交易信息
this.tradeServiceImpl.addTrade(trade);
//根据id查询订单
Orders order = orderService.findOrderById(trade.getOrderId());
//order.setId(trade.getId());
order.setTradeId(trade.getId());
//更新订单(修改了订单id)
this.orderService.updateOrder(order);
}
查询和更新接口,其他省略
//根据订单id查询订单
@RequestMapping(value="/findById",method=RequestMethod.GET)
public Orders findOrderById(@RequestParam("orderid")Integer orderid);
//更新订单
@RequestMapping(value="update",method=RequestMethod.POST,consumes=MediaType.APPLICATION_JSON_VALUE)
public void updateOrder(@RequestBody Orders order);
3.启动类,需要额外添加@EnableFeignClients ,表示将要调用其他服务
@SpringBootApplication
@EnableEurekaClient
@MapperScan("ah.szxy.trade.mapper")//只能添加Mapper所在的包,而不是具体的类
@EnableFeignClients
public class TradeProviderApp {
public static void main(String[] args) {
SpringApplication.run(TradeProviderApp.class, args);
}
}
功能思路
什么是API网关
API网关是一个反向路由:屏蔽内部细节,为调用者提供统一入口,接收所有调用者请求,通过路由机制转发到服务实例。
API网关是一组“过滤器”集合:可以实现一系列与核心业务无关的横切面功能,如安全认证、限流熔断、日志监控。
1.创建项目,修改pom文件
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.7.RELEASEversion>
parent>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Greenwich.SR2spring-cloud.version>
<maven-jar-plugin.version>2.6maven-jar-plugin.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-zuulartifactId>
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>
2.修改全局配置文件
spring:
application:
name: Zuul-Gateway
server:
port: 9999
eureka:
client:
serviceUrl:
defaultZone: http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
# URL 匹配关键字,如果包含path就跳转到指定的 URL 中
zuul: #配置路由规则
routes:
E-Book-Product-Provider: #此处为网关代理的服务名称
path: /E-Book-Product-Provider/**
url: http://localhost:8001/
3.启动类
添加@EnableZuulProxy注解
@SpringCloudApplication
@EnableZuulProxy
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
访问路径
http:// 网关服务地址 :网关服务端口 /访问的服务名称 /访问服务的接口地址
符号 | 含义 |
---|---|
/? | 匹配一个字符 |
/* | 匹配任意字符,但只能匹配单层路径 |
/ ** | 匹配任意字符,可以匹配多层路径 |
zuul.routs.name.path中的name可以变更,通常使用服务名。
1.通过url:
http://localhost:9999/suibian/product/find
zuul:
routes:
E-Book-Product-Provider:
path: /suibian/**
url: http://localhost:8001/
2.通过服务名, service-id为服务名
http://localhost:9999/suibian/product/find
zuul:
routes:
E-Book-Product-Provider:
path: /suibian/**
service-id: E-Book-Product-Provider
3.通过服务名(简化)
http://localhost:9999/suibian/product/find
zuul:
routes:
E-Book-Product-Provider:
path: /suibian/**
不指定service-id,但要求zuul.routs.name.path中的name为服务名。
4.路由排除法
zuul:
ignored-services: #排除知道服务。也可以排除所有服务/*,可以同routes添加不排除的服务
- /* # E-Book-Product-Provider
ignored-patterns: #排除指定路径 使用符号统配
- /*/findAll
5.添加前缀 , 访问时服务路径前必须添加前缀
http://localhost:9999/sui/bian/product/find
zuul:
prefix: /sui
routes:
E-Book-Product-Provider: #必须是服务名
path: /bian/**
1.复制上方案例,添加日志配置文件logback.xml
<configuration>
<property name="LOG_HOME" value="${catalina.base}/logs/" />
<appender name="Stdout" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
pattern>
layout>
appender>
<appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/server.%d{yyyy-MM-dd}.logFileNamePattern>
<MaxHistory>30MaxHistory>
rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
pattern>
layout>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MBMaxFileSize>
triggeringPolicy>
appender>
<root level="info">
<appender-ref ref="Stdout" />
<appender-ref ref="RollingFile" />
root>
configuration>
2.修改全局配置文件
spring:
application:
name: Zuul-Gateway-Filter
server:
port: 9999
eureka:
client:
serviceUrl:
defaultZone: http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
# URL 匹配关键字,如果包含path就跳转到指定的 URL中
zuul: #配置路由规则
routes:
E-Book-Product-Provider:
path: /E-Book-Product-Provider/**
url: http://localhost:8001/
3.创建过滤器
注意所导的包
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
@Component
public class MyFilter extends ZuulFilter{
private Logger log=LoggerFactory.getLogger(MyFilter.class);
/**
* 过滤逻辑
*/
@Override
public Object run() throws ZuulException {
RequestContext rc=RequestContext.getCurrentContext();
HttpServletRequest req = rc.getRequest();
log.info("Filter:method:{}+url:{}",req.getMethod(),req.getRequestURL().toString() );
return null;
}
/**
* 是否开启过滤
*/
@Override
public boolean shouldFilter() {
// TODO Auto-generated method stub
return true;
}
/**
* 拦截优先级 数字越小越高
*/
@Override
public int filterOrder() {
// TODO Auto-generated method stub
return 0;
}
/**
* 拦截器类型
* PRE过滤器:是在请求路由到具体服务之前执行的,可以做安全验证,如身份验证,参数验证。
*ROUTING过滤器:它用于将请求 路由到具体的微服务实例。默认使用Http Client进行网络请求。
*POST过滤器:在请求已被路由到微服务后执行的。可用作收集统计信息、指标,以及将响应传输到客户端。
*ERROR过滤器:在其他过滤器发生错误时执行。
*只能小写
*/
@Override
public String filterType() {
// TODO Auto-generated method stub
return "pre";
}
}
Zuul请求的声明周期(结合上图来看)
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import io.micrometer.core.instrument.util.StringUtils;
/**
* 简单的登录权限校验
* @author Administrator
*
*/
@Component
public class TokenFilter extends ZuulFilter{
private static final Logger log=LoggerFactory.getLogger(TokenFilter.class);
/**
* 过滤逻辑
*/
@Override
public Object run() throws ZuulException {
RequestContext rc=RequestContext.getCurrentContext();
HttpServletRequest req = rc.getRequest();
String token = req.getParameter("token");
if(StringUtils.isEmpty(token)) {
//未登录
log.warn("未登录!!!!");
rc.setSendZuulResponse(false);//设置不向下传递,拦截不通过
rc.setResponseStatusCode(401);
rc.setResponseBody("未登录!!请先登录");
rc.getResponse().setContentType("text/html;charset=utf-8");
}else {
log.info("已登录,通过过滤器!!!");
}
return null;
}
/**
* 是否开启过滤
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 拦截优先级 数字越小越高
*/
@Override
public int filterOrder() {
return 0;
}
/**
* 拦截器类型
* PRE过滤器:是在请求路由到具体服务之前执行的,可以做安全验证,如身份验证,参数验证。
*ROUTING过滤器:它用于将请求 路由到具体的微服务实例。默认使用Http Client进行网络请求。
*POST过滤器:在请求已被路由到微服务后执行的。可用作收集统计信息、指标,以及将响应传输到客户端。
*ERROR过滤器:在其他过滤器发生错误时执行。
*/
@Override
public String filterType() {
//只能是小写
return "pre";
}
}
@Component
public class ErrorMyFilter extends ZuulFilter{
private Logger log=LoggerFactory.getLogger(ErrorMyFilter.class);
/**
* 过滤逻辑
*/
@Override
public Object run() throws ZuulException {
RequestContext rc=RequestContext.getCurrentContext();
HttpServletRequest req = rc.getRequest();
//http://localhost:9999/E-Book-Product-Provider/product/find?token=suibian
log.info("------网关过滤器拦截异常------");
throw new RuntimeException("运行异常");
}
/**
* 是否开启过滤
*/
@Override
public boolean shouldFilter() {
// TODO Auto-generated method stub
return true;
}
/**
* 拦截优先级 数字越小越高
*/
@Override
public int filterOrder() {
// TODO Auto-generated method stub
return 0;
}
/**
* 拦截器类型
* PRE过滤器:是在请求路由到具体服务之前执行的,可以做安全验证,如身份验证,参数验证。
*ROUTING过滤器:它用于将请求 路由到具体的微服务实例。默认使用Http Client进行网络请求。
*POST过滤器:在请求已被路由到微服务后执行的。可用作收集统计信息、指标,以及将响应传输到客户端。
*ERROR过滤器:在其他过滤器发生错误时执行。
*只能小写
*/
@Override
public String filterType() {
// TODO Auto-generated method stub
return "error";
}
}
2.创建异常处理的controller
@RestController
public class ExceptionHandler implements ErrorController {
@Override
public String getErrorPath() {
return "/error";
}
@RequestMapping("/error")
public String handler() {
return "出错了!!!!";
}
}
一. zuul 和 hystrix 无缝结合
在 zuul 的 jar 包中包含了 hystrix 的 jar 包。所以我们不需要在项目中添加 Hystrix 的坐标 (SpringBoot2.0以上版本需要在配置文件中启用hystrix.stream)
management:
endpoints:
web:
exposure:
include: hystrix.stream
启动并访问一个服务,然后使用网关访问这个服务
访问网关服务的数据监控流
http://localhost:9999/actuator/hystrix.stream
二. 在网关中实现对服务降级处理
spring:
application:
name: Zuul-Gateway-FallBack
server:
port: 9020
eureka:
client:
serviceUrl:
defaultZone: http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
zuul: #配置路由规则
routes:
E-Book-Product-Provider:
path: /E-Book-Product-Provider/**
url: http://localhost:8001/
/**
* 实现FallbackProvider接口,实现服务降级
*
* 1.添加@Component注解
* 2.getRoute方法返回监控的服务名称
* 3.FallbackProvider
*
* @author Administrator
*
*/
@Component
public class ProductProviderFallback implements FallbackProvider {
/**
* 对指定的服务提供降级处理
*/
@Override
public String getRoute() {
return "E-Book-Product-Provider";
}
/**
* 设置降级后返回的响应
*/
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
/**
* 响应头
*/
@Override
public HttpHeaders getHeaders() {
HttpHeaders hander=new HttpHeaders();
hander.setContentType(MediaType.APPLICATION_JSON_UTF8);
return hander;
}
/**
* 响应体
*/
@Override
public InputStream getBody() throws IOException {
String content="请求服务不可达!请联系管理员!";
return new ByteArrayInputStream(content.getBytes());
}
/**
* 状态码:String类型
*/
@Override
public String getStatusText() throws IOException {
// TODO Auto-generated method stub
return getStatusCode().getReasonPhrase();
}
/**
* 状态码:HttpStatus类型
*/
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
/**
* 状态码:int类型
*/
@Override
public int getRawStatusCode() throws IOException {
// TODO Auto-generated method stub
return getStatusCode().value();
}
/**
* 关闭
*/
@Override
public void close() {
}
};
}
}
启动类
@SpringCloudApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
测试结果
启动一个服务,然后通过网关服务进行访问 ,最后关闭服务, 刷新网关服务页面,查看页面输出结果
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Greenwich.SR2spring-cloud.version>
<maven-jar-plugin.version>2.6maven-jar-plugin.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-zuulartifactId>
dependency>
<dependency>
<groupId>com.marcosbarbero.cloudgroupId>
<artifactId>spring-cloud-zuul-ratelimitartifactId>
<version>2.2.3.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
dependencies>
2.修改全局配置值文件
spring:
application:
name: Zuul-getway-ratelimit
redis:
#redis单机配置
host: 47.98.169.238
port: 6379
#redis连接池配置
jedis:
pool:
max-idle: 10
min-idle: 5
max-active: 100
max-wait: 3000
server:
port: 8888
eureka:
client:
serviceUrl:
defaultZone: http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
#配置路由规则
zuul:
routes:
E-Book-Product-Provider:
path: /suibian/**
ratelimit:
enabled: true #开启限流
#全 局 限 流
default-policy-list: #全局配置
- limit: 10 #访问次数
quota: 30 #访问总时间
refresh-interval: 60 #时间间隔
# 即在时间间隔内 访问的次数 总时间小于 访问总时间 就会被限制
type:
- origin #对ip进行限制
repository: redis #数据存储方式,默认为内存存储
#指定服务限流(局部限流)
# policy-list:
# E-Book-Product-Provider: #被限流的服务名
# - limit: 10
# quota: 30
# refresh-interval: 60
# type:
# - url
# repository: redis #数据存储方式,默认为内存存储
#开启监控端点 http://ip:port/actuator/hystrix.stream 需要actuatorjar包
#management:
# endpoints:
# web:
# exposure:
# include: hystrix.stream
3.测试
在60s内访问10次,且十次访问在30s内,被限流,返回429错误
4.可以通过异常处理类处理限流异常(429),返回指定的页面,创建友好的交互
背景介绍
hystrix的超时时间为1s,而ribbon为5s,假设请求在1s内没有处理完,就会timeout。
我们将hystrix的超时时间调高,避免timeout。
但ribbon的超时时间应该小于hystrix,在集群环境下,给充足的时间让ribbon调用集群中其他服务。
情景模拟
被hystrix监控的服务在查询数据库时,由于数据量大而请求响应时间大于1s,导致返回超时问题
解决方案
1.创建网关项目 ,修改pom
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Greenwich.SR2spring-cloud.version>
<maven-jar-plugin.version>2.6maven-jar-plugin.version>
properties>
<dependencies>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-zuulartifactId>
dependency>
dependencies>
2.修改全局配置文件
设置Ribbo和hystrix的超时时限
spring:
application:
name: Zuul-Gateway-Timeout
server:
port: 9999
eureka:
client:
serviceUrl:
defaultZone: http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
#http://localhost:9999/suibian/product/find
zuul:
routes:
E-Book-Product-Provider:
path: /suibian/**
#hystrix的超时时限
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 8000
#ribbon的超时时限
ribbon:
ReadTimeout: 5000
ConnectTimeout: 5000
management:
endpoints:
web:
exposure:
include: hystrix.stream
3.启动类
@SpringCloudApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
测试结果
访问刚才出现超时的页面,发现Zuul超时问题得以解决
前提
使用码云作为git仓库。先注册码云账号,创建一个仓库即可,
下面会用到仓库的全网访问的url
1.创建SpringCloud项目 ,修改pom文件
最重要的就是配置中心server 的坐标
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.7.RELEASEversion>
parent>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Greenwich.SR2spring-cloud.version>
<maven-jar-plugin.version>2.6maven-jar-plugin.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-zuulartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-serverartifactId>
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>
2.修改全局配置文件 application.yml
注意在使用时, 应用名(spring.application.name)不能与Eureka注册中心的其他服务的应用名相同 ,否则会访问不到码云上的配置文件
spring:
application:
name: config-server-Git
cloud:
config:
server:
git: #Git 配置
uri: https://gitee.com/TimePause/SpringCloudConfig #码云仓库url
username: #码云账号
password: #码云密码
server:
port: 9090
eureka:
client:
serviceUrl:
defaultZone: #Eureka注册中心地址
3.创建4个临时配置文件 ,然后上传到码云仓库中 ,再把项目中的这些临时配置文件移除
4 .编写启动类并启动
主要添加了 @EnableConfigServer注解
@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
注意 :
application为即将对其配置的服务的服务名
主干访问正常访问 ,分支访问需要加上分支的名称
1.复制上个项目 ,修改pom文件
将原来配置中心的server的坐标换成client的坐标
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
dependency>
2.修改启动类
去掉了config-server所使用的注解 @EnableConfigServer
@SpringBootApplication
@EnableEurekaClient
public class ConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class, args);
}
}
3.修改全局配置文件 bootstrap.yml
因为配置了配置中心,配置文件从配置中心读取,不能再使用applocation.properties/yml,配置项目名,端口号注册中心等 .
必须在本地配置,所以这里使用优先级比application.properties/yml高的bootstrap.properties/yml文件。
spring:
application:
name: config-client
cloud:
config:
discovery:
service-id: config-server-Git #config-server的服务名
enabled: true #开关
profile: dev #使用什么环境 test/dev/prod/default
label: master #git标签
server:
port: 9091 #服务启动端口
eureka:
client:
serviceUrl:
defaultZone: http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
4.编写一个controller ,去访问码云中的配置文件config-client-dev.properties
获取哪个配置文件根据 bootstrap.yml配置
通过@value注解+el表达式获取相关配置文件的具体的值
@Controller
public class showMsgController {
@Value("${E-Book}")
private String msg;
@RequestMapping("showMsg")
@ResponseBody
public String showMsg() {
return msg;
}
}
当我们修改git端配置文件是需要重新启动项目才能重新加载配置文件。当然这样在实际生产中是不好。
在config-server中,每一次client请求时都会去git端同步版本,所以不需要从重启server端,只需要重启client端。
我们通过actuator中的/refresh完成client的重启操作。
1.修改配置中心Client端pom文件 (被同步数据的一端)
添加actuator的坐标
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-zuulartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
dependencies>
2.修改其全局配置文件 bootbootstrap.yml
spring:
application:
name: config-client
cloud:
config:
discovery:
service-id: config-server-Git #config-server的服务名
enabled: true #开关
profile: dev #使用什么环境 test/dev/prod/default
label: master #git标签
server:
port: 9091
eureka:
client:
serviceUrl:
defaultZone: http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
management:
# 2.0之前需要关闭防护
#security:
# enable: false
endpoints:
web:
exposure:
include: refresh
3.启动类
@SpringBootApplication
@EnableEurekaClient
public class ConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class, args);
}
}
4.控制层
在springioc容器中,bean为单例模式/原型模式,
其值配注入后不会改变,刷新后也不会变化,需要@RefreshScope注解刷新属性
/**
*在springioc容器中,bean为单例模式,其值配注入后不会改变,刷新后也不会变化,需要@RefreshScope注解刷新属性
* @author chy
*
*/
@Controller
@RefreshScope
public class showMsgController {
@Value("${E-Book}")
private String msg;
@RequestMapping("showMsg")
@ResponseBody
public String showMsg() {
return msg;
}
}
5.启动server、启动client,修改git端数据使其数据改变,
例如修改上面代码中msg引用的值
6.进入Windows命令行 , 使用 curl -X POST http://localhost:服务的端口号/actuator/refresh
刷新client
在刷新之前,可以先清除配置中心客户端的控制台 ,待输出如下数据说明刷新/重启数据完毕
对于一些配置文件中的内容不宜明文显示,需要进行加密处理
对称加密介绍
对称加密是最快速、最简单的一种加密方式,加密(encryption)与解密(decryption)用的是同样的密钥(secret key)。
检查加密环境 http://127.0.0.1:配置中心服务端端口号/encrypt/status
检查结果:
出现下列结果需要为加密服务安装密钥。
{"description":"No key was installed for encryption service","status":"NO_KEY"} (2.0版本以前)
{"description":"The encryption algorithm is not strong enough","status":"INVALID"} (2.0版本以后)
设置加密环境
步骤
1.设置秘钥 KEY
(必须在bootstrap.properties/yml文件中
encrypt:
key: chy #配置加密环境的秘钥
2.未配置 JCE
http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
下载解压后,把 jar 文件上传到需要安装 jce 机器上 JDK 或 JRE 的 security 目录下,覆盖源文件 即可。
JDK:将两个 jar 文件放到%JDK_HOME%\jre\lib\security 下
JRE:将两个 jar 文件放到%JRE_HOME%\lib\security 下
3.spring cloud bug
Dalston.SR4、Dalston.SR3、Dalston.SR2 版本不能对配置文件加密,若需要调整到 Dalston.SR1
https://github.com/spring-cloud/spring-cloud-config/issues/767
如果使用官网生成的2.17版本的SpringBoot项目,则无需修改第3步,pom文件如下
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Greenwich.SR2spring-cloud.version>
<maven-jar-plugin.version>2.6maven-jar-plugin.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
加密演示
加密(post 请求):http://ip:port/encrypt
解密(post 请求):http://ip:port/decrypt
通过Postman工具发送post请求
主要添加的坐标
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-serverartifactId>
dependency>
配置文件 bootstrap.yml
spring:
application:
name: config-server-encryption-sym
cloud:
config:
server:
git: #Git 配置
uri: https://gitee.com/TimePause/SpringCloudConfig #码云/git仓库地址
username: 账号
password: 密码
skip-ssl-validation: true #解决Cannot clone or checkout repository 的问题
server:
port: 9999
eureka:
client:
serviceUrl:
defaultZone: http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
encrypt:
key: chy #配置加密环境的秘钥
环境搭建
创建SpringCloud项目,修改pom文件
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.7.RELEASEversion>
parent>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Greenwich.SR2spring-cloud.version>
<maven-jar-plugin.version>2.6maven-jar-plugin.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
dependency>
<dependency>
<groupId>commons-logginggroupId>
<artifactId>commons-loggingartifactId>
<version>1.2version>
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>
创建HTTPClient工具类,向其中发送需要加密的信息
向数据加密的服务中心配置发送请求
a. 例如发送一个需要加密字符串 “root”
返回一个加密的字符串 “baa42d01c077be2d26c7511d97845cf341427e7e9aefb726e941df7866816128”
b. 然后重新发送解密字符串 “baa42d01c077be2d26c7511d97845cf341427e7e9aefb726e941df7866816128”
验证是否返回root
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
public class HttpClientUtil {
public static String doGet(String url, Map<String, String> param) {
// 创建Httpclient对象
CloseableHttpClient httpclient = HttpClients.createDefault();
String resultString = "";
CloseableHttpResponse response = null;
try {
// 创建uri
URIBuilder builder = new URIBuilder(url);
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, param.get(key));
}
}
URI uri = builder.build();
// 创建http GET请求
HttpGet httpGet = new HttpGet(uri);
// 执行请求
response = httpclient.execute(httpGet);
// 判断返回状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String doGet(String url) {
return doGet(url, null);
}
public static String doPost(String url, Map<String, String> param) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建参数列表
if (param != null) {
List<NameValuePair> paramList = new ArrayList<>();
for (String key : param.keySet()) {
paramList.add(new BasicNameValuePair(key, param.get(key)));
}
// 模拟表单
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList,"utf-8");
httpPost.setEntity(entity);
}
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return resultString;
}
public static String doPost(String url) {
return doPost(url, null);
}
public static String doPostJson(String url, String json) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建请求内容
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return resultString;
}
public static void main(String[] args) {
//刷新服务请求
//String url ="http://localhost:9091/actuator/refresh";
//加密请求
//String url ="http://localhost:9999/encrypt";//baa42d01c077be2d26c7511d97845cf341427e7e9aefb726e941df7866816128
//解密请求
String url ="http://localhost:9999/decrypt";
//该url必须要使用dopost方式来发送
//加密
//String doPostJson = HttpClientUtil.doPostJson(url, "root");
//解密
String doPostJson = HttpClientUtil.doPostJson(url, "baa42d01c077be2d26c7511d97845cf341427e7e9aefb726e941df7866816128");
System.out.println(doPostJson);
}
}
主需要额外添加的坐标
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
dependency>
bootstrap.yml
保留的参数 应用名,端口号,不重启服务刷新配置文件的配置 , 注册中心配置
spring:
application:
name: config-E-Book-Product-Provider
#配置中心服务端的连接信息
cloud:
config:
discovery:
service-id: config-server-encryption-sym #configserver的服务名
enabled: true #开关
label: master #git标签
server:
port: 8001
management:
endpoints:
web:
exposure:
include: refresh
eureka:
instance:
prefer-ip-address: true
client:
serviceUrl:
defaultZone: http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
注意:
配置refresh属性后并且添加actuator的坐标,以后在码云中更改配置文件后在服务中生效
只需要在命令行输入 curl -X POST http://localhost:8001这个服务端口号/actuator/refresh
例如: curl -X POST http://localhost:8001/actuator/refresh
在比较长的字符串的前后最好加上单引号,不然可能会报错,
这里推荐url,加密后的用户名和密码的前后需要加上单引号
注册中心的文件不能放到配置中心/码云上 ,这是我们区分是否将配置文件上传的一个依据
spring:
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
url: 'jdbc:mysql://localhost:3306/book-product?useUnicode=true&characterEncoding=gbk&useJDBCCompliantTimezoneShift=true&serverTimezone=UTC'
username: '{cipher}baa42d01c077be2d26c7511d97845cf341427e7e9aefb726e941df7866816128'
password: '{cipher}baa42d01c077be2d26c7511d97845cf341427e7e9aefb726e941df7866816128'
type: com.alibaba.druid.pool.DruidDataSource
server:
compression: #springboot的gzip配置
enabled: true #是否启用压缩
mime-types: #配置压缩支持的 MIME TYPE
- application/json,application/ xml,text/html,text/xml,text/plain
mybatis:
type-aliases-package: ah.szxy.product.pojo
mapper-locations:
- classpath:ah/szxy/product/mapper/*.xml
注意 :
a.将需要加密的数据进行通过config-server加密(每次加密结果不一样,但都能解密出相同的结果)
b.在密码前需要加上{cipher},这样config-server才能解密出明文密码。
Java-keytool 使用说明
Keytool 介绍
Keytool 是一个 Java 数据证书的管理工具 。 它的作用是帮助配置服务实现64位非对称秘钥的生成
Keytool 将密钥(key)和证书(certificates)存在一个称为 keystore 的文件中。
keystore 文件,包含两种数据:密钥实体(Key entity)-密钥(secret key)或者是私钥和配对公钥(采用非对称加密)可信任的证书实体(trusted certificate entries)-只包含公钥.
位于JAVA_HOME/jdk/bin/keytool.exe
使用keytool创建证书
使用有管理员权限的命令行,在keytool.exe目录 shift+鼠标右键
打开power shell(或使用cmd跳转到该目录)
后执行 keytool -genkeypair -alias "test1" -keyalg "RSA" -keystore "test.keystore"
keytool -genkey -alias andro eyalg RSA -validity 30000 -keystore
生成的test.keystire文件位于JAVA_HOME/jdk/bin/。
使用非对称加密算法实现配置文件加密与解密
前提 : 需要有keytool创建的证书
pom文件如下
<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>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.7.RELEASEversion>
<relativePath />
parent>
<groupId>ah.szxy.springcloudgroupId>
<version>0.0.1-SNAPSHOTversion>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Greenwich.SR2spring-cloud.version>
<maven-jar-plugin.version>2.6maven-jar-plugin.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-starter-netflix-eureka-clientartifactId>
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>
<name>Config-Server-Encryptionname>
<artifactId>Config-Server-EncryptionartifactId>
project>
修改配置文件 bootstrap.yml
spring:
application:
name: config-server-rsa-sym
cloud:
config:
server:
git: #Git 配置
uri: https://gitee.com/TimePause/SpringCloudConfig
username: 码云账号
password: 码云密码
skip-ssl-validation: true #解决Cannot clone or checkout repository 的问题
server:
port: 9999
eureka:
client:
serviceUrl:
defaultZone: http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
#使用非对称加密
#keytool -genkeypair -alias "config-info" -keyalg "RSA" -keystore "encrypt-info.keystore"
encrypt:
key-store:
# keystore 文件的路径
location: classpath:encrypt-info.keystore
# alias 指定密钥对的别名,该别名是公开的;
alias: config-info
# storepass 密钥仓库
password: caohaiyang
# keypass 用来保护所生成密钥对中的私钥
secret: caohaiyang
将2.2生成好的加密文件放到src目录下
@SpringBootApplication
@EnableConfigServer
@EnableEurekaClient
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
对相关数据进行加密(如数据库的用户名和密码)
得到的是一个64位的字符串
复制对称加密使用的项目,修改其全局配置文件 bootstrap
spring:
application:
name: config-E-Book-Product-Provider-RSA
#配置中心服务端的连接信息
cloud:
config:
discovery:
service-id: config-server-rsa-sym #configs-erver的服务名
enabled: true #开关
label: master #git标签
server:
port: 8011
management:
endpoints:
web:
exposure:
include: refresh
eureka:
instance:
prefer-ip-address: true
client:
serviceUrl:
defaultZone: http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
文件名称是依据 bootstrap中服务的应用名而起的
这里可以看到密码已经被解密
注意:本次模拟主要是对数据库的用户名和密码进行的加密
以非对称加密为例 ,对称加密安全认证同上
添加security坐标
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
<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-starter-netflix-eureka-clientartifactId>
dependency>
修改全局配置文件
spring:
application:
name: config-server-rsa-sym-security
cloud:
config:
server:
git: #Git 配置
uri: https://gitee.com/TimePause/SpringCloudConfig
username: 码云账号
password: 码云密码
skip-ssl-validation: true #解决Cannot clone or checkout repository 的问题
# 安全认证
#开启基于 http basic 的安全认证
security:
user:
name: root
password: root
server:
port: 9999
eureka:
client:
serviceUrl:
defaultZone: http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
#使用非对称加密
#keytool -genkeypair -alias "config-info" -keyalg "RSA" -keystore "encrypt-info.keystore"
encrypt:
key-store:
# keystore 文件的路径
location: classpath:encrypt-info.keystore
# alias 指定密钥对的别名,该别名是公开的;
alias: config-info
# storepass 密钥仓库
password: caohaiyang
# keypass 用来保护所生成密钥对中的私钥
secret: caohaiyang
再次通过[配置中心服务端访问码云上的配置文件时,需要输入配置文件设置的用户名和密码才能访问
pom文件无需修改,但是如果直接访问,会出现如下错误:
解决方式:
在全局配置文件中添加安全认证的用户名密码
spring:
application:
name: config-E-Book-Product-Provider-RSA
#配置中心服务端的连接信息
cloud:
config:
discovery:
service-id: config-server-rsa-sym-security #configs-erver的服务名
enabled: true #开关
label: master #git标签
username: root
password: root
server:
port: 8011
management:
endpoints:
web:
exposure:
include: refresh
eureka:
instance:
prefer-ip-address: true
client:
serviceUrl:
defaultZone: http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/