之前一直在使用nginx + spring boot + dubbo 的一个分布式框架,用过spring cloud发现当服务完全细分之后才能发挥出cloud的优势,在大多数情况下dubbo是够用,后来听说spring cloud alibaba可以兼容spring cloud和dubbo,所以这才来学习一下。首先贴上spring cloud alibaba源码github地址
首先建议看一下nacos的官方介绍,是阿里开源的一个服务注册、配置、管理中心。支持Kubernetes,dubbo,spring cloud的服务注册。相比zookeeper和eureka单纯的服务治理来说,多了配置的功能。
两种安装方式:
git clone https://github.com/alibaba/nacos.git
cd nacos/
mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U
ls -al distribution/target/
// change the $version to your actual path
cd distribution/target/nacos-server-$version/nacos/bin
unzip nacos-server-$version.zip 或者 tar -xvf nacos-server-$version.tar.gz
cd nacos/bin
下载完成后,新建一个mysql数据库,执行conf目录下的nacos-mysql.sql文件,
然后在application.properties文件中配置数据库信息
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://ip地址:端口/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=数据库用户名
db.password=数据库密码
配好之后就准备启动啦
windows环境下
双击 startup.cmd 或者cmd startup.cmd
linux/mac/unix
sh startup.sh -m standalone
如果使用的是ubuntu系统,或者运行脚本报错提示[[符号找不到,执行
bash startup.sh -m standalone
或者是将startup.sh文件中的#!/bin/sh
替换成 #!/bin/bash
启动好了之后访问http://ip:8848就可以了,会出现如下界面,账号密码默认都是nacos,登入之后可以修改密码。
这边nacos就好啦,接下来就是java代码了。
首先创建一个工程作为父工程,引入相关依赖。
说一下,spring boot 和 spring cloud的版本兼容相当重要,参考一下
下面是需要引入的依赖
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Finchley.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>0.2.2.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
dependencies>
#使用配置中心时,必须使用bootstrap.properties/yml作为本地配置文件,优先加载bootstrap
spring.application.name=service-provider-sys
#nacos配置中心
spring.cloud.nacos.config.server-addr=ip:8848
#nacos配置文件类型
spring.cloud.nacos.config.file-extension=yaml
#nacos服务注册中心
spring.cloud.nacos.discovery.server-addr=ip:8848
启动类加上开启cloud服务注册注解
@SpringBootApplication
@EnableDiscoveryClient
public class ServiceProviderSysApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceProviderSysApplication.class, args);
}
}
control层,这里获取一下从nacos配置的端口号
@RestController
@RequestMapping("/sys")
public class SysController {
@Value("${server.port}")
String port;
@GetMapping("/say")
public String say(@RequestParam("name") String name) {
return "Hello " + name + ", I'm from port " + port;
}
}
这里的消费和生产只是一个相对的概念,在微服务中其实每一个服务都是消费者和生产者,所以才能形成微服务网。
引入依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
启动类,这里注入一个RestTemplate ,实现两种服务调用的方式
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ServiceConsumerSysApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceConsumerSysApplication.class, args);
}
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
control层
@RestController
public class SysController {
@Autowired
private SysService sysService;
@Autowired
private RestTemplate restTemplate;
@GetMapping("/hello-feign/{name}")
public String hello(@PathVariable String name) {
return sysService.say(name);
}
@GetMapping("/hello/{name}")
public String helloFeign(@PathVariable String name) {
return restTemplate.getForObject("http://service-provider-sys/sys/say?name=" + name, String.class);
}
}
基于feign实现的服务调用service
@FeignClient(value = "service-provider-sys", fallback = SysServiceFallback.class)
public interface SysService {
@RequestMapping(value = "/sys/say", method = RequestMethod.GET)
String say(@RequestParam("name") String name);
}
然后是feign调用失败的回调类,这里实现对应的接口,配置好feign的配置类和实现类
@Slf4j
@Component
public class SysServiceFallback implements SysService {
@Override
public String say(String name) {
log.error("消费降级{}", name);
return "Sentinel {由于你的访问次数太多,已为你限流、您已进入保护模式,请稍后再试!}>>>熔断处理函数";
}
}
最后是bootstrap.properties的配置
#使用配置中心时,必须使用bootstrap.properties作为本地配置文件
#nacos 配置文件的data-id前缀
spring.application.name=service-consumer-sys
#nacos配置中心配置
spring.cloud.nacos.config.server-addr=ip:8848
spring.cloud.nacos.discovery.server-addr=ip:8848
spring.cloud.nacos.config.file-extension=yaml
#如果加上这个配置,会读取该文件名的配置
#spring.cloud.nacos.config.name=name-test
然后回到刚才nacos的界面,新增一个配置文件
这里是给应用设置一个端口,这里需要注意的是,注册中心和配置中心是需要在配置文件中配置的,不用我解释吧~
接下来启动啦
这边看到本地是没有配置端口的,但是启动之后没有使用默认的8080端口,而是使用了nacos上配置的8000端口。
首先provider的访问是没有问题的~
然后是consumer访问依然没有问题
我们来看一下nacos的注册信息
到这里基本上简单的注册和配置基本上就ok了。
spring cloud最开始使用的组件是zuul,但是之前发现一个是不稳定,另外性能相比gateway和nginx差了很多,后来的zuul2还没有尝试过,我们这次使用的是gateway。
依赖gateway
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
启动类
@SpringBootApplication
@EnableDiscoveryClient
public class ServiceGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceGatewayApplication.class, args);
}
}
配置文件
spring:
application:
name: service-gateway
cloud:
nacos:
config:
server-addr: ip:8848
file-extension: yaml
discovery:
server-addr: ip:8848
sentinel:
transport:
dashboard: 127.0.0.1:8849
eager: true
gateway:
discovery:
locator:
enabled: true
routes:
- id: service-consumer-sys #id随便,不冲突就行
uri: lb://service-consumer-sys # 服务注册的name,一定要和服务名一样
predicates:
- Method=GET,POST # 允许的请求
- Path=/api-c/** #这里加上下面的StripPrefix类似nginx的一个转发
filters:
- StripPrefix=1
- id: service-provider-sys
uri: lb://service-provider-sys
predicates:
- Method=GET,POST
- Path=/api-s/**
filters:
- StripPrefix=1
server:
port: 9000
logging:
level:
org.springframework.cloud.gateway: debug
添加一个拦截器
@Slf4j
@Component
public class AuthFilter implements GlobalFilter, Ordered {
private static final String HEAR_AUTH = "Authorization";
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String ip = getIpAddress(request);
String token = request.getHeaders().getFirst(HEAR_AUTH);
if (token == null || token.isEmpty()) {
ServerHttpResponse response = exchange.getResponse();
Map<Object, Object> map = Maps.newHashMap();
map.put("code", 401);
map.put("message", "非法请求!");
map.put("cause", "Token is null");
map.put("ip", ip);
ObjectMapper mapper = new ObjectMapper();
// 输出错误信息到页面
DataBuffer buffer = response.bufferFactory().wrap(JSON.toJSONString(map).getBytes());
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
return response.writeWith(Mono.just(buffer));
}
return chain.filter(exchange);
}
private String getIpAddress(ServerHttpRequest request) {
//HttpHeaders headers = request.getHeaders();
String ip;
try {
//根据header获取相应校验
//logger.info(headers.getFirst("Authorization"));
//List list = headers.get("x-forwarded-for");
InetSocketAddress address = request.getRemoteAddress();
assert address != null;
InetAddress inetAddress = address.getAddress();
ip = inetAddress.getHostAddress();
log.info("inetAddress host name :" + inetAddress.getHostName());
} catch (Exception e) {
log.error(e.getMessage());
ip = "";
}
return ip;
}
}
这里是一个简单的拦截器,校验做一个简单的header校验。
然后说一下yml的配置,注册中心和配置中心先不说了,spring cloud alibaba 对sentinel有着很好的支持,优秀的服务降级,流量监控,配置持久化,如果项目体量不够大其实可以用到cloud自身的hystrix熔断就够了,。
启动网关。
没有header的情况下,我们看到返回了自己配置的401.
加上之后我们看到转发成功。
然后我又启动了一个端口为8002的provider,可以看到多次请求的时候,不管是feign还是resttemplate的请求方式都是轮询请求,也就是LoadBalanced。
今天的代码虽然比较简单,其实还有些问题
feign.hystrix.enabled=true
之后会启用的,我之前使用springcloud的时候也是没问题的,但这次不知道为什么没有执行feign的fallback方法,这个我研究一下再更新。feign.hystrix.enabled=true
一定要加上