自学多日,写一个demo做个总结,从建项目开始,比较简单的实现spring cloud微服务,消费者对生产者调用,容错机制,供初学者参考,更快的理解spring cloud
一张图
引用课工场一张图,辅助理解
win10
Idea2019.3
jdk1.8.0_241
spring boot 2.2.7
spring cloud Hoxton.SR4
根据官网伦敦车站命名法提示,Hoxton站对应springboot 2.2.X,所以选择2.2.7
目标四个子项目,服务,消费者,生产者,公共实体类
首先创建,Eureka注册中心,和dubbo的Zookeeper差不多的功能
起个名
创建第一个模块,Eureka
点击加号NewModule
选择spring boot项目
起个名字
勾选Eureka Server,选2.2.7版本,Next->Finish
分布式注册中心,也就是管理的分布式的项目叫服务,其他具有实际功能的但是要注册到注册中心里的叫客户端
ok
建好了,处理一下pom
配置文件 application.properties
都有注释,自己看
注意:defaultZone这个东西,有的帖子写default-zone,default-Zone都会报连接错误,我也不知道为什么这么写,反正就是defaultZone就对了
server.port=4460
#注册中心集群defalutZone填除你之外的注册中心地址,如下
#eureka.client.service-url.defaultZone=http://localhost:4461/eureka,http://localhost:4462/eureka
eureka.client.service-url.defaultZone=http://localhost:4460/eureka
#是否把当前项目添加到注册中心
eureka.client.register-with-eureka=false
#注册中心分布式集群设true
eureka.client.fetch-registry=false
#开启用户名密码
#spring.security.user.name=root
#spring.security.user.password=demo
如果给注册中心加用户名和密码,打开“开启用户名密码”的配置,并在pom中加下面包,
客户端请求改为 http://root:demo@localhost:4460/eureka
我开启后,客户端不能连接到注册中心,不知道咋回事,所以注掉了
<!-- 开启用户名密码-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
启动类EurekaServerApplication
加入@EnableEurekaServer注解证明他是一个服务
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
Eureka服务完事,启动程序
浏览器访问http://localhost:4460
出现这个,服务搭建成功,Application模块为空,说明没有客户端注册进来
添加一个子模块
同样分方法,点加号,New Module,spring boot项目
勾选web,client,选2.2.7版本,完成
配置文件
注册到4460端口上
server.port=4461
spring.application.name=provider-user
eureka.client.service-url.defaultZone=http://localhost:4460/eureka
启动类
加注解@EnableDiscoveryClient
老版本的@EnableEurekaClient,效果一样
@SpringBootApplication
@EnableDiscoveryClient
public class ProviderUserApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderUserApplication.class, args);
}
}
其实现在启动已经可以注册到注册中心了,但是不要启动,再写一个Demo
创建一个controller包,建一个UserController类,做测试
代码,userp,表示生产者的user接口
package com.sunc.provideruser.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@RequestMapping(value = "userp",method = RequestMethod.GET)
public String getUser(@RequestParam("name") String name, @RequestParam("pass") String pass){
return "hello "+name+",this is provider";
}
}
启动项目
浏览器访问http://localhost:4461/userp?name=xiaohei&pass=123
成功,刷新Eureka服务页面,可以看到provider-user注册进来了
保证字段和类型在两个项目中不会出现不一致,生产者和消费者公用一个实体类
但是这个实体类不需要注册到Eureka,也不需要逻辑,所以只
创建maven项目
命名
创建User实体类
package com.sunc.bean;
public class Users {
private String user_name;
private String pass_word;
public String getUser_name() {
return user_name;
}
public void setUser_name(String user_name) {
this.user_name = user_name;
}
public String getPass_word() {
return pass_word;
}
public void setPass_word(String pass_word) {
this.pass_word = pass_word;
}
@Override
public String toString() {
return "User{" +
"user_name='" + user_name + '\'' +
", pass_word='" + pass_word + '\'' +
'}';
}
}
右边maven中,build一下
复制pom中地址给生产者
common中pom复制出来
<groupId>org.example</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
粘贴到provider的pom中
修改provider-user模块中controller
此处使用了Users实体,请求改为post,判断用户名密码是否正确。
如果是“xiaobai”且密码“123”,返回true
package com.sunc.provideruser.controller;
import com.sunc.bean.Users;
import org.springframework.web.bind.annotation.*;
@RestController
public class UserController {
@RequestMapping(value = "userp",method = RequestMethod.POST)
public Integer getUser(@RequestBody Users user){
Integer i = 0;
if ("xiaobai".equals(user.getUser_name()) && "123".equals(user.getPass_word())){
i = 1;
}
return i;
}
}
公共实体完事
同样方法创建新模块,springboot项目,起名consumer-user
注意此处勾选feign,调用其他成员
实体类地址复制过来
配置文件类似,改个端口,改个喜欢的名字,我这里全都和项目同名,方便看
创建controller
此处地址用userc,消费者的user接口
package com.sunc.consumeruser.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@RequestMapping(value = "/userc",method = RequestMethod.GET)
public String getUser(@RequestParam("name") String name, @RequestParam("pass") String pass){
return "hello "+name+",this is consumer";
}
}
启动类和生产者一样加@EnableDiscoveryClient
package com.sunc.consumeruser;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerUserApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerUserApplication.class, args);
}
}
启动…
此时,Eureka-server,provider-user均为启动状态,不要关闭
刷新浏览器
consumer-user也注册进来了,成功
浏览器访问
http://localhost:4462/userc?name=xiaobai&pass=123
成功
两个项目均注册进来了,也都能通过web访问,但是这完全是两个项目,现在要用consumer调用provider,这样才能将分开的项目联系起来。dubbo使用RPC连接,dubbox使用RPC和tcp,spring cloud使用tcp,三次握手,四次分手,客户端发送syn请求,服务端相应后返回syn,ack,客户端再发送。。。。。。此处省略925个字,tcp的好处就是各种语言之间都可以连接,springcloud 连接方式可以使用Ribbon+RestTemplate或者Feign,此处使用Feign
改造开始
consumeruser启动类加入@EnableFeignClients
package com.sunc.consumeruser;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
//调别人需要注解
@EnableFeignClients
public class ConsumerUserApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerUserApplication.class, args);
}
}
创建service
provider-user为前面给给生产者配置文件中的spring.application.name=provider-user
准备以post方式请求userp,传递参数Users的属性,@RequestBody这个我不太理解,不加请求不过去
package com.sunc.consumeruser.service;
import com.sunc.bean.Users;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@FeignClient(name = "provider-user")
public interface UserService {
@RequestMapping(value = "/userp",method = RequestMethod.POST)
public Integer getUser(@RequestBody Users user);
}
改造controller
如果返回true输出hello,如果false提示用户名或密码不对
package com.sunc.consumeruser.controller;
import com.sunc.bean.Users;
import com.sunc.consumeruser.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping(value = "/userc",method = RequestMethod.GET)
public String getUser(@RequestParam("name") String name, @RequestParam("pass") String pass){
Users users = new Users();
users.setUser_name(name);
users.setPass_word(pass);
String res = "";
if(userService.getUser(users).equals(1)){
res = "hello "+users.getUser_name()+", this is consumer";
}else if(userService.getUser(users).equals(2)){
res = "Sorry, user name or password is wrong ~~";
}
return res;
}
}
重启consumer-user
访问 http://localhost:4462/userc?name=xiaobai&pass=1236
提示用户名或密码不对
访问 http://localhost:4462/userc?name=xiaobai&pass=123
成功
此时已经证明consumer调用了provider中的方法,并判断了用户名和密码是否匹配,实现了用户登陆时的返回信息和判断登陆是否成功的分布式(可怜的demo)
作用就是,当服务端宕机,客户端请求不能实现,进入异常类做特殊处理
由于hystrix通属于feign包,所以不需要引入包直接建
配置文件
启用熔断器机制
server.port=4462
spring.application.name=consumer-user
eureka.client.service-url.defaultZone=http://localhost:4460/eureka
#启用熔断器机制
feign.hystrix.enabled=true
service包下建Impl实现类
如果provider宕机返回2
package com.sunc.consumeruser.service.Impl;
import com.sunc.bean.Users;
import com.sunc.consumeruser.service.UserService;
import org.springframework.stereotype.Component;
@Component
public class UserServiceFallback implements UserService {
@Override
public Integer getUser(Users user) {
//如果请求不能发送,进入UserServiceFallback,返回2
return 2;
}
}
改造service
fallback = UserServiceFallback.class
package com.sunc.consumeruser.service;
import com.sunc.bean.Users;
import com.sunc.consumeruser.service.Impl.UserServiceFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@FeignClient(name = "provider-user", fallback = UserServiceFallback.class)
public interface UserService {
@RequestMapping(value = "/userp",method = RequestMethod.POST)
public Integer getUser(@RequestBody Users user);
}
controller
添加一句话,对返回2判断
package com.sunc.consumeruser.controller;
import com.sunc.bean.Users;
import com.sunc.consumeruser.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping(value = "/userc",method = RequestMethod.GET)
public String getUser(@RequestParam("name") String name, @RequestParam("pass") String pass){
Users users = new Users();
users.setUser_name(name);
users.setPass_word(pass);
String res = "";
if(userService.getUser(users).equals(1)){
res = "hello "+users.getUser_name()+", this is consumer";
} else if (userService.getUser(users).equals(0)){
res = "Sorry, user name or password is wrong ~~";
} else if (userService.getUser(users).equals(2)){
res = "Sorry, 服务器挂了 ~~";
}
return res;
}
}
重启consumer服务
访问浏览器,一切正常
关掉provider服务,模拟宕机
再访问浏览器
<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
consumer-user的pom
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.sunc</groupId>
<artifactId>consumer-user</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>consumer-user</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR4</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
eureka-server的pom
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.sunc</groupId>
<artifactId>eureka-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka-server</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR4</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
provider-user的pom
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.sunc</groupId>
<artifactId>provider-user</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>provider-user</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR4</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
做到这实现了,eureka服务,生产者,消费者搭建,消费者对生产者的调用,共用同一个实体类,容错机制。
最后才发现consumer-user用的springboot2.3.0。。。貌似也能用
如有偏颇敬请斧正,本厮邮箱:suncch@63.com