公司之前一直使用 WebService 作为各个服务之间的通信技术,在半个月前,终于下决心用 SpringCloud 取代了 WebService。以下是我在此之前根据 Demo 写的一些实践心得。
为什么选择了 SpringCloud
其实选择 SpringCloud 之前,我是比较倾向于 Dubbo 的,毕竟 Dubbo 的底层使用 RPC 协议,从某种程度上说性能自然是比 SpringCloud 这种基于 Http 协议的更高。可是我们公司的现有的业务规模,也远远没有到拼性能的地步,使用内网调用也是很快的,我们更看重 SpringCloud 的体系比较完整,就像一个全家桶,很多东西 SpringCloud 已经把我们安排的明明白白的了,而使用 Dubbo 可能还需要我们四处去集成,比如 监控就需要自己去定制,而SpringCloud 可以无缝整合 Zipkin ,实现链路监控。出于这些原因,所以选择了 SpringCloud。
服务治理和发现
SpringCloud 本身提供了多种服务发现组件的支持,比如我们熟悉的 Zookeeper,还有 Consul 和 Eureka。我们选择了 Eureka, 是Netflix 开源的服务发现组件,本身是基于 REST 服务,SpringCloud 将它集成在子项目 SpringCloud Netflix 中,从而实现微服务的注册和发现。
因为我们必须还要考虑一个比较重要的就是如果是单节点 Eureka 挂掉了,整个服务缺了注册中心而当掉了,是一个很恐怖的事情,所以为了保证 Eureka 的高可用,我们是使用多节点的 Eureka 集群。
- 代码流程
- 新建一个 maven 项目,名为 eureka ,pom 中引入
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>
- 新建一个主类 App.class
@SpringBootApplication
@EnableEurekaServer
public class App {
public static void main(String[] args) {
System.out.println("------------------- start -------------------");
SpringApplication.run(App.class, args);
System.out.println("------------------- end -------------------");
}
}
- application.yml 如下
app:
name: register
spring:
application:
name: eureka
---
spring:
profiles: peer1
server:
port: 9080
eureka:
instance:
hostname: localhost
client:
service-url:
defaultZone: http://localhost:9070/eureka/
---
spring:
profiles: peer2
server:
port: 9070
eureka:
instance:
hostname: localhost
client:
service-url:
defaultZone: http://localhost:9080/eureka/
这里就是使用通过多个实例相互注册的方式实现高可用部署,Eureka 实例会彼此增量的同步信息,从而确保所有节点数据一致。
使用 Spring 的多配置,在启动脚本上选择启动的配置。
-
启动步骤
- mvn package
- 将打包生成的 eureka-server.jar 复制到两个文件夹
- 在第一个文件夹 执行 java -jar eureka-server.jar --spring.profiles.active=peer1
- 在第二个文件夹 执行 java -jar eureka-server.jar --spring.profiles.active=peer2
- 浏览器打开 http://localhost:9080 或者 http://localhost:9070 查看
服务生产者
SpringCloud 的服务生产者比较朴素,说白了就是一个个接口,暴露出来,注册到 Eureka 中,不仅别的微服务可以调用,Web、Android 和 IOS 也是可以调用的。 我们的做法是把服务生产者的协议和项目分成两个 Module ,协议打包成 jar ,供消费者使用从而对生成者进行调用,而调用的方式则是 SpringCloud 推荐使用的 Feign,使用 Feign 最大的优势在于 SpringCloud 对Feign 实现了增强,使得 Feign 支持了 Spring MVC 注解,这样生产者可以直接实现 Feign 接口,使得生产者和消费者默认使用同一协议,节约代码成本。
- 代码流程
- 新建一个 maven 项目,名为 autumn ,pom 中引入
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
- 创建协议 Module autumnbase,pom引入
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>
-
创建一些 model 和 传输载体,略过
-
创建 FeignClient 类 UserFeignClient.class, UserFallback 是 SpringCloud 的熔断机制,在使用 SpringCloud 的过程中,如果出现异常(比如超时),会降级使用 UserFallback 里面的数据,这里展示两种调用方式,一种是 GET 请求,一种是POST 请求,请求的注解看起来和 Spring MVC 一模一样,这样使得消费者和生产者的代码都更加好写。
@FeignClient(name = "autumn", fallbackFactory = UserFallback.class)
public interface UserFeignClient {
@RequestMapping(value = "/user/get", method = RequestMethod.GET)
AutumnResponse findById(@RequestParam("id") Integer id);
@RequestMapping(value = "/user/add", method = RequestMethod.POST)
AutumnResponse addUser(@RequestBody User user);
}
- 创建项目主体 Module autumncore ,pom 引入 其中第一个就是 上面的 协议 Module
<dependencies>
<dependency>
<groupId>lindian</groupId>
<artifactId>autumn-base</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
- 新建 主类 App.class
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
@EnableHystrix
public class App {
public static void main(String[] args) {
System.out.println("------------------- start -------------------");
SpringApplication.run(App.class, args);
System.out.println("------------------- end -------------------");
}
}
- 新建 Controller,Controller 实现 协议中的 UserFeignClient ,然后 完成一个 简单的 get 和 post 请求
@RequestMapping(value = "/user")
@RestController
public class UserController implements UserFeignClient {
private static Map<Integer, User> map = new ConcurrentHashMap<Integer, User>();
static {
map.put(1, new User(1, "傅红雪"));
map.put(2, new User(2, "李寻欢"));
map.put(3, new User(3, "任我行"));
map.put(4, new User(4, "令狐冲"));
}
@RequestMapping(value = "/get", method = {RequestMethod.GET}, produces = {"application/json;charset=UTF-8"})
@Override
public AutumnResponse findById(Integer id) {
AutumnResponse response = new AutumnResponse();
User user = findUserById(id);
if (user == null) {
response.setStatus(AutumnResponse.STATUS_FAIL);
return response;
}
// try {
// Thread.sleep(500);
// } catch (Exception e) {
//
// }
response.setUser(user);
System.out.println(response.toString());
return response;
}
@RequestMapping(value = "/add", method = {RequestMethod.POST}, produces = {"application/json;charset=UTF-8"})
@Override
public AutumnResponse addUser(@RequestBody User user) {
int len = map.size();
int id = len + 1;
User iUser = new User(id, user.getName());
map.put(id, iUser);
AutumnResponse response = new AutumnResponse();
response.setUser(iUser);
return response;
}
private User findUserById(int id) {
return map.get(id);
}
}
- 项目配置,同Eureka 一样,为了保证项目的高可用,也是 使用了多节点注册
app:
name: autumn
eureka:
client:
service-url:
defaultZone: http://localhost:9080/eureka/,http://localhost:9070/eureka/
instance:
prefer-ip-address: true
spring:
application:
name: autumn
sleuth:
sampler:
percentage: 1.0
zipkin:
base-url: http://localhost:9411
logging:
level: info
---
spring:
profiles: peer1
server:
port: 9091
---
spring:
profiles: peer2
server:
port: 9092
-
启动步骤
- mvn clean install
- 进入 autumncore 执行 mvn package
- 将打包生成的 autumn.jar 复制到两个文件夹
- 在第一个文件夹 执行 java -jar autumn.jar --spring.profiles.active=peer1
- 在第二个文件夹 执行 java -jar autumn.jar --spring.profiles.active=peer2
至此,便完成了生产者的启动
(未完待续)










网友评论