从我开始接触javaweb开发的时间来(很短),也接触了几个架构,有jsp+servlet+mysql这种最原始的架构,代码耦合度非常高,比如jdbc连接都得写好几次,后来开始用框架了,接触ssm,又接触了springboot,springcloud等等,下面就实现一个简单的分布式项目
分布式,就是要在客户和服务端间建立一个注册中心,这个注册中心可以用springcloud来实现,
有三个模块
首先建立一个服务端
在开发springboot的时候,遇到一个问题,就是,springboot的实现类,ServiceProvideApplication该类要与其他的包(controller包,mapper包…)在同一级目录,不然就不能成功访问
用springboot来创建该模块,因为要连接到数据库,所以记得勾选jdbc,mybatis,mysql,还有web的,没勾也没关系,直接到pom.xml中添加依赖
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>service_provide</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>service_provide</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- 通用mapper,Mybatis使用mapper接口方式来实现,自己引入 -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</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>
<!-- Eureka客户端,这是后面要用到的 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<!-- SpringCloud的依赖 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
这里省略了头尾,不要直接引用
server.port=8081
spring.datasource.url=jdbc:mysql:///springboot
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#配置包扫描
mybatis.type-aliases-package=com.sb.pojo
#以下是在springcloud中心注册的配置,可以先关注上面的配置
eureka.client.service-url.defaultZone=http://127.0.0.1:10086/eureka
spring.application.name=service-provide
#如果有重名,开启覆盖
spring.main.allow-bean-definition-overriding=true
表的字段如下,大家可以自己建,或者用已有的数据库代替
要实现通过页面访问后,返回数据库中的数据,用json来返回数据,这样一个rest风格的接口,
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
public UserService userService;
@GetMapping("{id}")
public User queryUserById(@PathVariable("id") long id){
return this.userService.queryUserById(id);
}
}
这个控制器,可以直接在http中输入id值来得到想要的数据,@PathVariable注解用来映射URL绑定的占位符,可以直接用url地址如localhost:8080/user/1来获取id为1的值,在以前学习ssm的时候,好像没有这样的方法,这应该也是springmvc的rest的风格的体现,
@Service
public class UserService {
@Autowired
public UserMapper userMapper;
public User queryUserById(long id){
User user = userMapper.selectByPrimaryKey(id);
return user;
}
}
上面有提到过,mybatis用接口的方式来实现数据库操作,但是这里的mapper类,只要继承tk.mybatis.mapper.common.Mapper;就可以实现了,
public interface UserMapper extends Mapper<User> {
}
直接映射到user实体类,可以实现selectByPrimaryKey(id);返回查询到的user数据了。这个依赖要自己引的,上面的pom.xml中已指出
但是现在的mapper和springboot项目还没有关联,要在ServiceProvideApplication中加入
@MapperScan("com.sb.mapper")
@EnableDiscoveryClient
扫描mapper包,下面的那个注解作用是使注册中心能够发现自己(当前的服务端)
这时候大家就可以启动服务器访问下数据了。
建客户端
看图就可以发现,客户端只管拿数据就行了,不用再做那些查数据库的操作,
@Controller
@RequestMapping("consumer/user")
public class UserController {
@Autowired
public RestTemplate restTemplate;
@Autowired
public DiscoveryClient discoveryClient;
@GetMapping
@ResponseBody
public User queryUserById(@RequestParam("id")Long id){
// |
// \/
//根据服务器名称,获取服务实例,有可能是集群,所以是service集合
List<ServiceInstance> instances = discoveryClient.getInstances("service-provide");
//因为只有一个service-provide所以获取第一个
ServiceInstance instance = instances.get(0);
String baseUrl = "http://"+instance.getHost()+":"+instance.getPort()+"/user/"+id;
// /\
// |
User u = restTemplate.getForObject(baseUrl, User.class);
return u;
}
}
代码中的注释是和注册中心相关的,可以先不关注,baseUrl就是服务器的地址,这时的服务器是provide,注册中心还没做,直接连上provide,
RestTemplate是Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。
server.port=8082
#注册中心相关
eureka.client.service-url.defaultZone=http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka
spring.application.name=service-consumer
配置也只要配个端口
启动服务器,然后再启动客户端,这是就可以通过localhost:8082/consumer/user/1来访问服务器,获取数据了。当然了,这时的服务器和客户端是放在同一个电脑上的,所以有些奇怪。
eureka服务器(注册中心)
在创建springboot项目时勾选eureka服务
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com</groupId>
<artifactId>eureka</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR3</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>
<!-- SpringCloud的依赖 -->
<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>
不要忘了加springcloud的统一依赖管理↑
配置信息↓
server.port=10086
#eureka服务的名字
spring.application.name=eureka-server
#默认的地址
eureka.client.service-url.defaultZone=http://127.0.0.1:10086/eureka
在springboot的启动器加上注解@EnableEurekaServer,开启eureka服务
启动服务,并访问:http://127.0.0.1:10086
这时候eureka服务就开启了,接下来就要将服务端和客户端加入这个服务器了
但是在现实生活中,只有一台服务器来处理请求就会有问题,一般都是多台服务器,这里就涉及到eureka的高可用,就是注册多台eureka服务器,这里注册两台,使用相互注册的方式,两台的端口号这里设置的是10086,10087
先将服务端和客户端的配置信息写好
service_provide的配置文件
spring.application.name=service-provide
#如果有重名,开启覆盖
spring.main.allow-bean-definition-overriding=true
#eureka客户端的配置,连接到注册中心的地址
eureka.client.service-url.defaultZone=http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka
在启动服务端时,启动失败,
Description:The bean ‘dataSource’, defined in BeanDefinition defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration H i k a r i . c l a s s ] , c o u l d n o t b e r e g i s t e r e d . A b e a n w i t h t h a t n a m e h a s a l r e a d y b e e n d e f i n e d i n c l a s s p a t h r e s o u r c e [ o r g / s p r i n g f r a m e w o r k / b o o t / a u t o c o n f i g u r e / j d b c / D a t a S o u r c e C o n f i g u r a t i o n Hikari.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration Hikari.class],couldnotberegistered.Abeanwiththatnamehasalreadybeendefinedinclasspathresource[org/springframework/boot/autoconfigure/jdbc/DataSourceConfigurationHikari.class] and overriding is disabled.
应该是datasource定义了两次,找不到到底在哪定义了两次,直接在配置文件中配置覆盖,就能启动了。
客户端service_consumer也要加上
spring.application.name=service-consumer
eureka.client.service-url.defaultZone=http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka
高可用
这里要先打开run dashboard窗口,便于操作
在你的项目中,D:…\myData.idea,我的项目名是myData,.idea中找workspace.xml文件,找到RunDashBoard字段,在component便签下添加
<option name="configurationTypes">
<set>
<option value="SpringBootApplicationConfigurationType" />
</set>
</option>
之后启动项目就会弹出dashboard窗口了,
右键EurekaApplication,点击复制配置,弹出编辑配置的窗口,直接点击确定,就会生成一个EurekaApplication2的复制项,
server.port=10087
spring.application.name=eureka-server
eureka.client.service-url.defaultZone=http://127.0.0.1:10086/eureka
把Eureka模块的配置文件的defaultZone的端口改为10087,因为相互注册,将10086注册给10087,启动原eureka的启动器,这时候会报错,但是dashboard中原eureka服务器后面已经跟着个10086的端口了,注册成功了,之后把配置文件(两个服务共用一个配置,但是加载完原项目后,把配置文件改了),
server.port=10086
spring.application.name=eureka-server
eureka.client.service-url.defaultZone=http://127.0.0.1:10087/eureka
把10087注册给10086,然后启动那个复制的eureka,在我启动时有时候会失败,但是照着上面的那个配置文件的修改顺序来,应该是没错的,
这时候,两个eureka就都启动了,在把服务端和客户端启动,可以访问http://127.0.0.1:10086来查看
这时候高可用就完成了
这篇blog只是大体的论述下springcloud的使用,里面很多的细节并没有去细扣,也有许多的错误和不解。