我也是菜鸟, 刚刚入坑, 如有错误还请批评指教, 不要打我^^
本博客基于spring boot 2.0.5.RELEASE 版本
Eureka
Euraka用来实现服务的注册与发现, 基于Netfix Eureka做了二次封装
注册中心实现
核心依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
代码
@SpringBootApplication
@EnableEurekaServer //核心注解
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
配置文件
eureka:
instance:
hostname: localhost
# client:
# service-url:
# defaultZone: http://localhost:8768/eureka #多个eureka实现高可用, 相互注册, defaultZone 可接受多个链接, 用逗号间隔
register-with-eureka: false #不会被eureka本身注册
fetch-registry: false
server:
enable-self-preservation: false #表示在此eureka服务器中关闭自我保护模式,所谓自我保护模式是指,出现网络分区、eureka在短时间内丢失过多客户端时,会进入自我保护模式,即一个服务长时间没有发送心跳,eureka也不会将其删除。默认为true
server:
port: 8768
spring:
application:
name: euraka
客户端:
核心依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
代码
@SpringBootApplication
@EnableDiscoveryClient //核心注解
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
配置
eureka:
client:
service-url:
defaultZone: http://localhost:8768/eureka/ #通过url向对应开放的服务端进行注册
数据库
核心依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> // 工具类
<scope>provided</scope>
</dependency>
dao层接口
//集成jpa ProductInfo 封装类, String 主键类型
public interface ProductInfoReposity extends JpaRepository<ProductInfo, String> {
//findBy** jpa自带的书写方式, 另行百度详细查看
List<ProductInfo> findByProductStatus (Integer productStatus);
@Query(value = "select a.* from product_info a where a.product_id in ?1 and a.product_status = 0",nativeQuery=true)
List<ProductInfo> findByProductId (List<String> productIdList);
}
封装类常用注解
@Data // lombok提供的注解 自动生成get set方法
@Entity //类上 表示数据库封装类
@Id //属性上 表示该字段位主键
@generatedvalue //主键生成策略
@Table(name="表名") //可添加表名前缀
@JsonProperty("name") //api响应数据名与封装类属性名不同时, 可使用
配置数据源
spring:
application:
name: XXX #应用名称
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: mysql
url: jdbc:mysql://localhost:3306/spring_cloud?characterEncoding=utf-8&useSSL=false
jpa:
show-sql: true
应用通信
可以使用RestTemplate 进行服务间数据交互
方式1
RestTemplate restTemplate = new RestTemplate(); //创建对象
restTemplate.getForObject("服务的url", "返回值类型") //集群下不可用
方式2
@Autowired
private LoadBalancerClient loadBalancerClient;
ServiceInstance service = loadBalancerClient.choose("服务名"); //通过服务名称获的任意一个服务实例对象
String url = String.format("http://%s:%s", service.getHost(), service.getPort(), ""); //获取服务的url
restTemplate.getForObject(url, "返回值类型") //请求获取结果
方式3
@Component
public class RestTemplateConfig {
@Bean
@LoadBalanced // 基于某种规则实现负载均衡, 默认为轮询
public RestTemplate restTemplate() {
return new RestTemplate(); //使用注解方式, 获取restTemplate对象
}
}
//应用
@Autowired
private RestTemplate restTemplate;
restTemplate.getForObject(url, "返回值类型") //请求获取结果
客户端负载均衡器 Ribben , 默认为轮询, 可通过配置更改
应用名称:
ribben:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule //随机规则
使用feign完成应用间通信(伪RPC)
main方法上打 @EnableFeignClients 注解
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
接口代码
@FeignClient(name = "product") // 填写应用名称 spring.application.name
public interface Product {
@PostMapping("/product/listOrder") //填写访问的映射
public ResultVo<List<ProductInfo>> listOrder(@RequestBody List<String> list); // 方法名可随意指定 返回值 与 api 返回值相同, 参数与 api 接受请求参数相同
}
@Autowired
private Product product; //注入接口类
product.listOrder(集合); //直接调用接口方法即可
统一配置中心
服务端配置
核心依赖
<dependency>
<groupId>org.springframework.cloud</groupId> //配置中心依赖
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId> //自动更新配置依赖
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
代码
@SpringBootApplication
@EnableDiscoveryClient //注册服务
@EnableConfigServer // 配置中心核心注解
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}
配置
spring:
cloud:
server:
git:
uri: https://github.com/XXX/springcloud/ //github 库地址
username: XXX //用户名
password: XXX //密码
basedir: D:/workplace/ideawork/springcloud/config/basedir/ // 本地拉下来的配置文件路径
application:
name: CONFIG //应用名称
rabbitmq: // mq配置, 通过mq使应用不重启, 自动刷新配置信息
host: 192.168.99.100
port: 5672
username: root
password: 123456
eureka:
client:
service-url:
defaultZone: http://localhost:8768/eureka/ //服务注册地址
server:
port: 9002 //端口号
management:
endpoints:
web:
exposure:
include: '*' //暴露所有bus api 接口
cloudfoundry:
enabled: false //
命名规则
/{name}-{profiles}.yml
/{label}/{name}-{profiles}.yml
name 应用名称
label 分支名称 branch
profiles 环境名称
例如:
order-dev.yml //生产环境配置
order-test.yml // 测试环境配置
order.yml //默认读取, 建议存放共用配置
若访问出现空白error页面, 说明文件的数据格式有问题, 配置中心无法正常转换格式
客户端使用
核心依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
更改配置文件名称 application.yml -> bootstrap.yml
需要自动更新的类上打 @RefreshScope 注解
配置文件
spring:
application:
name: product //应用名称
rabbitmq: //mq地址
host: 192.168.99.100
port: 5672
username: root
password: 123456
cloud:
config:
discovery:
enabled: true //使用配置
service-id: CONFIG //配置服务名称
profile: dev //环境, 就是上面的profiles , -后面加的那个东东
eureka:
client:
service-url:
defaultZone: http://localhost:8768/eureka/ // 注册服务
server:
port: 8081 //端口
通过访问 http://localhost:9002/actuator/bus-refresh 即可手动刷新配置
通过配置github上的webhook实现自动刷新, 不过实现的时候出现了问题, 没能成功, 有大神成功了, 请评论一下, 不胜感激, 问题请详看 待解决
MQ的使用
普通方式调用mq 依赖
<dependency>
<groupId>org.springframework.boot</groupId> //使用普通mq方式
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
代码:
// 发送端
@Autowired
private AmqpTemplate amqpTemplate; //注入模板
amqpTemplate.convertAndSend("myQueue", "now:" + new Date()); // mq普通调用
amqpTemplate.convertAndSend("myExchange", "boy" , "now:" + new Date()); // mq分组调用
amqpTemplate.convertAndSend("myExchange", "girl" , "now:" + new Date()); // mq分组调用
//接收端
@Slf4j
@Component
public class MqReceiver {
@RabbitListener(queuesToDeclare = @Queue("myQueue")) //普通mq, 自动创建mq
public void process(Object message) {
log.info("mqReceiver : {}", message);
}
@RabbitListener(bindings = @QueueBinding( //分组方式, 通过key 路由不同的mq
exchange = @Exchange("myExchange"), //mq 组名称
key = "boy", // 通过key 区分不同的mq
value = @Queue("boyQueue") //mq名称
))
public void processGroup1(Object message) {
log.info("boyQueue : {}", message);
}
@RabbitListener(bindings = @QueueBinding( //分组方式, 通过key 路由不同的mq
exchange = @Exchange("myExchange"),
key = "girl", // 通过key 区分不同的mq
value = @Queue("girlQueue") //mq名称
))
public void processGroup2(Object message) {
log.info("girlQueue : {}", message);
}
}
配置见上文, 配置mq信息就可以了
使用 Stream 方式调用 mq 依赖
<dependency>
<groupId>org.springframework.cloud</groupId> // 使用stream 方式
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
代码
//发送端
@Autowired
private StreamClient streamClient;
streamClient.input().send(MessageBuilder.withPayload("hahahah").build());
// 接口
public interface StreamClient {
String INPUT = "input"; // mq的名称
String OUTPUT = "output";
@Input(INPUT) // 暂时不明白, 我的理解是通常call的mq地址
SubscribableChannel input();
@Output(OUTPUT) // 暂时不明白, 我的理解是转发的mq地址,
MessageChannel output();
}
//接收端
@Component
@Slf4j
@EnableBinding(StreamClient.class)
public class StreamReceiver {
@StreamListener(StreamClient.INPUT) //监听mq
@SendTo(StreamClient.OUTPUT) //转发到其他mq, 返回结果信息
public Object process(Object message) {
log.info("Stream input receive: {}", message);
return "ok";
}
@StreamListener(StreamClient.OUTPUT)
public void process2(String message) {
log.info("Stream output receive: {}", message);
}
}
分组mq使用场景, 当服务调用mq 对应多个不同的服务时
例如: 订单服务 -> mq -> (电器类(mq) -> 电器类服务, 文具类(mq) -> 文具类服务, 水果类(mq) -> 水果类服务)
网关
zuul 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
核心注解 @EnableZuulProxy
配置:
zuul:
routes:
# 可随意明明
a:
path: /myproduct/** #自定义路由路径
seviceId: product #目标服务路径
sensitiveHeaders: #敏感头的过滤
#简洁写法
# product: /myproduct/**
# 不被网关管理的路径
ignored-patterns:
- /product/env/print #网关会忽略改路径的路由
- /myproduct/env/print
# prefix: 统一前缀
若要实现动态配置, 需注册到eureka, 并且关联到配置中心, application.xml -> bootstrap.xml
zuul可以做校验, 限权, 限流等功能, 下面是限流的简单实现
@Component
public class RateLimitFilter extends ZuulFilter {
// 谷歌的限流工具类
private static final RateLimiter rateLimiter = RateLimiter.create(100);
/**
* to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
* "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.
* We also support a "static" type for static responses see StaticResponseFilter.
* Any filterType made be created or added and run by calling FilterProcessor.runFilters(type)
*
* @return A String representing that type
*/
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return SERVLET_DETECTION_FILTER_ORDER - 1; //过滤器的执行顺序, 数值越小越优先
}
// a "true" return from this method means that the run() method should be invoked
// 可以放在配置中进行动态更新
@Override
public boolean shouldFilter() {
return true;
}
// this method is the core method of a ZuulFilter
@Override
public Object run() throws ZuulException {
if (!rateLimiter.tryAcquire()) {
throw new RateLimitException();
}
return null;
}
}
跨域问题
- 在被调用的方法或类上, 添加@CrossOrigin 注解
- 在zuul中添加 CorsFilter过滤器
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true); //是否允许cookie 跨域
corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
corsConfiguration.setAllowedMethods(Arrays.asList("*"));
corsConfiguration.setMaxAge(300L);
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(urlBasedCorsConfigurationSource);
}
}
Spring Cloud Hsytrix
可以实现服务降级, 熔断
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
```@RestController
@RequestMapping("productOrder")
@DefaultProperties(defaultFallback = "defaultFallback") // 服务降级默认配置
public class ProductOrderController {
@HystrixCommand(
// fallbackMethod="fallback", // 自定义服务降级方法
commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="3000"), // 超时配置
@HystrixProperty(name="circuitBreaker.enabled", value="true"), //开启熔断设置
@HystrixProperty(name="circuitBreaker.requestVolumeThreshold", value="10"), // number of requests that must be made within a statisticalWindow before open/close decisions are made using stats
@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds", value="10000"), // 熔断时间
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage", value="60") // 超过60% 请求失败, 将会进入熔断状态
})
@GetMapping("hello")
public String getProductList(){
RestTemplate restTemplate = new RestTemplate();
String hello = restTemplate.getForObject("http://localhost:8081/env/hello", String.class);
return hello;
}
// 服务降级自定义方法
public String fallback() {
return "hystric feedback";
}
// 服务降级默认方法
public String defaultFallback() {
return "default hystrix fallback";
}
}
可通过配置方式对参数进行配置, 添加 @HystrixCommand 注解
hystrix:
command:
default: //默认配置
execution:
isolation:
thread:
timeoutInMilliseconds: 3000
getProductList: // 默认根据方法名进行配置, 可修改注解中 commandKey进行更改
execution:
isolation:
thread:
timeoutInMilliseconds: 1000
feign 方式
开启配置
feign:
hystrix:
enabled: true
@FeignClient(name = "product", fallback = ProductFallback.class) //注解上设置fallback 的 回调类
@Component
public class ProductFallback implements Product {
@Override
public ResultVo<List<ProductInfo>> listOrder(@RequestBody List<String> list) {
return null; // 写fallback的行为
}
}
Hystrix Dashboard
依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
核心注解 @EnableHystrixDashboard
配置:
management:
endpoints:
web:
exposure:
include: hystrix.stream
访问路径: http://localhost:8890/hystrix
参考: https://www.jianshu.com/p/4ad8acb15380
问题
1 向eureka注册的客户端启动后自动关闭
加入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
待解决
通过配置github上的webhook实现自动刷新, host为内网穿透生成的, 通过XXX/actuator/bus-refresh可以刷新 ,但是webhook不行, 错误404
回调结果
{"timestamp":"2018-10-11T16:53:51.083+0000","status":404,"error":"Not Found","message":"No message available","path":"/monitor"}