为什么要用Spring Cloud Alibaba
Spring Cloud是当下最为流行的微服务套件。它的技术组件集众家之长,Eureka、Zuul等都是依赖于Netflix的。这也导致了它受制于第三方厂商。比如Zuul目前宣布停止维护,Eureka2.x宣布闭源不允许使用,而且原生的Spring Cloud引入国内后,产生了一些水土不服的情况,比如config默认将配置文件存储在Github上,且没有维护界面。这都为国内使用带来了一些麻烦。而Spring Cloud Alibaba就结合国内的一些实际使用情况,解决了这些麻烦,同时扩展了功能,实现了更加符合国内使用习惯的微服务架构。
我要如何入门?
我们上面提到,Eureka和Config两个官方服务,都出现了一些问题,导致目前使用困难。所以,我们就先从解决这两个痛点的Nacos入手。Nacos结合了Eureka作为服务发现的作用以及Config作为配置中心的作用,同时也提供了一个可视化的界面用于管理。所以将Nacos作为学习Spring Cloud Alibaba的学习切入点是比较合适的。
来写个小Demo吧
说得再多,也不如一个小Demo直白。下面我们要来写一个简单的Demo,一个生产者,一个消费者,两个微服务。然后用Nacos作为服务发现,下面我们就开始动手。
先建个父工程
首先我们在idea中建立一个空的Maven工程,命名为spring-cloud-nacos。然后我们需要去修改一下pom文件,添加一些依赖管理内容。这里我们先将它作为一个普通的父工程来处理,后面我们再来改造成Spring Cloud Alibaba的形式。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${springbootstart.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
</dependencies>
</dependencyManagement>
这里我们先只引入基础的Spring Boot依赖,后面再进行改造
再来个生产者
在这个父工程下,新建一个Module,就叫nacos-provider-8010,作为生产者。新建好后,我们同样去修改pom文件,引入一些基础的依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
然后我们来写一个简单的接口,模拟提供一个查询服务
首先是实体类
package com.akitsuki.entity;
import lombok.Data;
/**
* @author ziling.wang@hand-china.com
* @date 2022/9/27 15:22
*/
@Data
public class EntityInfo {
private String entityId;
private String info;
private String sendTime;
}
然后是服务类
package com.akitsuki.service;
import com.akitsuki.entity.EntityInfo;
import org.springframework.stereotype.Service;
/**
* @author ziling.wang@hand-china.com
* @date 2022/9/27 15:23
*/
@Service
public class EntityInfoService {
public EntityInfo queryEntityInfo(String entityId) {
EntityInfo entityInfo = new EntityInfo();
entityInfo.setEntityId(entityId);
entityInfo.setInfo("Hello Nacos");
entityInfo.setSendTime(String.valueOf(System.currentTimeMillis()));
return entityInfo;
}
}
最后是Controller
package com.akitsuki.controller;
import com.akitsuki.entity.EntityInfo;
import com.akitsuki.service.EntityInfoService;
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.RestController;
/**
* @author ziling.wang@hand-china.com
* @date 2022/9/27 15:26
*/
@RestController
public class NacosProviderController {
@Autowired
private EntityInfoService entityInfoService;
@GetMapping(value = "/entityInfo/{entityId}")
public EntityInfo queryEntityInfo(@PathVariable("entityId") String entityId) {
return entityInfoService.queryEntityInfo(entityId);
}
}
最后别忘了启动类
package com.akitsuki;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author ziling.wang@hand-china.com
* @date 2022/9/27 15:16
*/
@SpringBootApplication
public class NacosProviderApplication {
public static void main(String[] args) {
SpringApplication.run(NacosProviderApplication.class);
}
}
还有配置文件application.yml中指定启动端口
server:
port: 8010
这样我们的简易生产者就做好了。启动起来试着访问一下
没问题,接口可以调用成功,说明这个简单的生产者可以正常工作。
接下来是消费者
有了生产者,还得有个消费者,用来模拟微服务之间的相互调用。我们在父工程下面再建立一个Module,就叫nacos-consumer-9000。同样的,按照生产者的方式,引入基本的依赖。这里我们就只写一个Controller用来调用就好
package com.akitsuki.controller;
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.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* @author ziling.wang@hand-china.com
* @date 2022/9/27 16:43
*/
@RestController
public class EntityInfoController {
@Autowired
private RestTemplate restTemplate;
@GetMapping(value = "/entityInfo/{entityId}")
public String getEntityInfo(@PathVariable("entityId") String entityId) {
String url = "http://localhost:8010/entityInfo/" + entityId;
return restTemplate.getForEntity(url, String.class).getBody();
}
}
试着启动消费者调用一下
看起来也没有问题,那么实际上到这里我们做的事情和Spring Cloud Alibaba半毛钱关系都没有,只是搭了两个架子而已。
接下来,我们就要着手改造了,让它真的成为一个Spring Cloud Alibaba的Demo。
来引入Nacos吧
终于要进入正题了,先来引入我们的Nacos作为注册中心吧。我们如果进入Nacos的官网,可以看到它会指引我们去Github进行下载。但由于众所周知的原因,这个下载实际很难达成。但我们依然有办法,那就是去Gitee进行下载。Gitee也有一份Nacos的代码,每天同步一次。所以我们可以认为基本和Github上面是一致的。
我们访问 https://gitee.com/mirrors/Nacos
由于我们是写小Demo,也不用考虑稳定性之类的东西,所以直接下载默认的develop分支代码,然后打开
看起来非常复杂,但其实我们可以不去管它。按照一些步骤来启动即可。
首先是maven打包
mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U
打完包后,我们cd到 distribution/target/nacos-server-$version/nacos/bin
目录下
然后我们以单机模式启动(这里我是windows系统所以用startup.cmd)
Nacos默认的端口是8848,我们可以在conf目录下的application.properties中修改server.port来进行修改,我这里修改成了8858。
startup.cmd -m standalone
nice,看到Nacos的字符画,心里顿时有了底。
由于Nacos自带了一个管理界面,所以我们试着访问看看 http://localhost:8858/nacos
我们用默认账号密码nacos/nacos登入
看起来非常管理系统的一个管理系统。里面大部分功能我们以后再来探究,现在主要看看服务管理。这里就是用来管理注册到Nacos的服务的
自然,现在是空的。既然Nacos也启动起来了,我们也要着手改造我们前面的生产者和消费者了。
开始改造吧
首先是我们的父工程,在依赖管理中,要加入spring-cloud的依赖,和spring-cloud-alibaba的依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2020.0.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2021.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
然后是我们的生产者,要引入服务发现的依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
既然有了服务发现,那我们的服务也要有个名字。以及前面我们修改了Nacos的默认端口,所以我们这里也要重新指定服务发现服务的端口
server:
port: 8010
spring:
application:
name: nacos-provider
cloud:
nacos:
discovery:
server-addr: localhost:8858
config:
server-addr: localhost:8858
对于生产者来说,这么多改造就足够了。可以把自己注册到Nacos就行
下面是消费者,同样,也要引入服务发现的依赖和负载均衡的依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
然后,我们需要对RestTemplate进行一些改造,对其加上负载均衡的注解,这样它才能识别到我们用服务名的url。
package com.akitsuki.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
/**
* @author ziling.wang@hand-china.com
* @date 2022/9/27 16:11
*/
@Component
public class BeanManagerConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
然后我们的请求url也要改一下,用服务名进行请求,而不是用ip+端口的方式进行请求。
package com.akitsuki.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* @author ziling.wang@hand-china.com
* @date 2022/9/27 16:43
*/
@RestController
public class EntityInfoController {
@Autowired
private RestTemplate restTemplate;
@GetMapping(value = "/entityInfo/{entityId}")
public String getEntityInfo(@PathVariable("entityId") String entityId) {
String url = "http://nacos-provider/entityInfo/" + entityId;
return restTemplate.getForEntity(url, String.class).getBody();
}
}
同样,这里的配置文件也要加上相应的内容
server:
port: 9000
spring:
application:
name: nacos-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8858
config:
server-addr: localhost:8858
好了,改造完毕!让我们启动服务看看吧
nacos的反应十分迅速,服务列表里面已经有我们的两个服务了
接下来我们试着访问一下订阅者的接口
很完美,能请求到数据,这就代表我们可以通过服务名来调用生产者的服务了。
到这里,我们的小Demo就算完成了,也算是开启了Spring Cloud Alibaba的学习之路。期间其实并没有这个文章看起来那么顺利,还是遇到了许多小问题。随着问题的不断解决,也是加深理解的一个过程。那么,就到这里吧。