├── LICENSE ├── README.md ├── docs ├── 0-推荐资料.md ├── 1-基础语法.md ├── 2-文件读取.md ├── 3-数据库.md ├── 4-单人作业.md ├── 4-合作作业.md ├── 5-即时通信.md ├── 6-微服务.md ├── 6-微服务后端合作.md ├── 7-合作项目或底层实现.md └── README.md ├── etc ├── README.md ├── blog │ ├── Docker.md │ ├── MySQL.md │ ├── Mybatis.md │ ├── Redis.md │ ├── RocketMQ入门.md │ ├── SSO.md │ ├── docker-compose简易部署.md │ ├── gRPC.md │ ├── grpc & springboot.md │ ├── swagger使用.md │ ├── 分布式定时任务.md │ ├── 单元测试.md │ ├── 编程规范.md │ └── 阿里巴巴Java开发手册.pdf └── etc.md └── img └── 20230703142015.png /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # west2-online Java考核指南 2 | 3 | 这里是西二在线工作室Java方向的考核指南,旨在为初学者提供一个循序渐进的Java学习路线 4 | 5 | ## 版权 6 | 7 | 本项目遵循GPL-3.0 License,转载请注明本项目仓库地址 8 | 9 | ## 概览 10 | 11 | | 阶段 | 学习内容 | 预期时长 | 是否需要答辩 | 12 | | ------------------------------------------ | ------------------------------------------------------------ | --------------- | ------------ | 13 | | [基础](docs/1-基础语法.md) | JavaSE基础语法、类与对象、面向对象三大特性(封装、继承、多态)、异常处理、接口与抽象类、多线程 | 30天(1个月) | × | 14 | | [文件读取](docs/2-文件读取.md) | 文件读取、数据分析、异常处理、单元测试 | 30天(1个月) | × | 15 | | [数据库](docs/3-数据库.md) | Github和Git使用、JDBC(或更高级框架)、Mysql使用、http请求/响应、Json | 30天(1个月) | √ | 16 | | [单人作业](docs/4-单人作业.md) | SpringBoot、Mybatis等 | 60天(2个月) | 待定 | 17 | | [合作作业](docs/4-合作作业.md) | SpringBoot、Mybatis等 | 60天(2个月) | 待定 | 18 | | [即时聊天](docs/5-即时通信.md) | Websocket通信,RabbitMQ等 | 45天(1.5个月) | √ | 19 | | [微服务](docs/6-微服务.md) | SpringCloud、rpc通信、服务注册与发现等 | 45天(1.5个月) | √ | 20 | | [微服务-合作](docs/6-微服务后端合作.md) |SpringCloud、rpc通信、服务注册与发现,分布式等 |45天(1.5个月)| √ | 21 | | [合作或其他](docs/7-合作项目或底层实现.md) | 与前端/客户端进行合作开发第一款相对成熟的产品,了解项目的对接、开发、测试;进行底层源码的学习;进行开源活动 | 60天(2个月) | √ | 22 | 23 | 预期时长以一名零基础为参考,如果是已经对其他语言有一定的了解所有预期时长都可以除以10 24 | 25 | ## 作业提交 26 | [作业提交仓库](https://github.com/west2-online-reserve/collection-java) 27 | 28 | 29 | ## 时间安排 30 | 31 | | 时间 | 完成内容 | 32 | | -------- | ------------------------------------ | 33 | | 第一学期 | 基础、高级特性与网络请求、备忘录 | 34 | | 寒假 | 合作项目(与前端/Android/IOS等客户端) | 35 | | 第二学期 | 聊天室、微服务、分布式、底层源码 | 36 | | 暑假 | 合作项目(产出较为成熟的产品) | 37 | 38 | ## 考核设计 39 | 40 | | 名称 | 解释 | 41 | | -------- | ---------------------------------------------- | 42 | | 参考资料 | 供给同学们进行参考学习的部分学习资料 | 43 | | 知识点 | 本轮要求掌握的知识内容 | 44 | | 背景 | (部分阶段有)增加部分趣味性的故事 | 45 | | 任务 | 任务的具体描述 | 46 | | Bonus | 在完成任务的基础上进行实现更加深入的功能/特性 | 47 | | 提示 | (部分阶段有)对考核,或者对语言学习的一些提示 | 48 | 49 | ## 考核目标 50 | 51 | 我们的目标是快速为初学者构建一套较为**完整的Java知识体系**。也就是经过完整的考核过程后,你将会有**独立开发一款服务**的能力,这意味着你可以通过这个赚取你的第一桶金了(指接外包)。 52 | 53 | 但是很明显,这样快速的学习并**不能满足现代企业对Java工程师的需求**。这要求我们在学习过程中更加注重对原理、源码的掌握,同时这也能在面试等方面提供更多的帮助。并且我们的考核更加**偏向业务**,因此需要自己学习更加深入的内容,我们只是提供一条学习道路并培养你一定的**源码阅读、文档阅读、独立学习**的能力。 54 | 55 | 如果你有意将Java作为你将来学习或工作的主力语言,我们建议认真负责的完成每一轮的`全部内容`,并能够简单了解一些框架的原理与设计模式、运维相关的知识。 56 | 57 | ## 项目结构 58 | 59 | ~~~shell 60 | . 61 | ├─docs // 考核文档 62 | ├─etc // 杂项文档 63 | │ └─blog // 优质文章/笔记 64 | ├─img // 文档图片 65 | ├─LICENSE 66 | └─README.md 67 | ~~~ 68 | 69 | 70 | 71 | ## 其他 72 | 73 | 处于Android开发特性原因(需要学习Java语言基本特性),第一轮考核与Android考核同时进行 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /docs/0-推荐资料.md: -------------------------------------------------------------------------------- 1 | 这里只列举综合性较强的站点、博客、个人主页等内容,如果只想看文章/细节的请移步到[etc](../etc/etc.md) 2 | 3 | ## 站点/系列 4 | 5 | * [KuangStudy-Java入门教程](https://www.kuangstudy.com/course?cid=1) 6 | * [Java教程 - 廖雪峰的官方网站 (liaoxuefeng.com)](https://www.liaoxuefeng.com/wiki/1252599548343744) 7 | * [编程狮](https://www.w3cschool.cn/java/) 8 | * [鱼皮 - 编程学习路线 (yupi.icu)](https://luxian.yupi.icu/#/roadmap/Java学习路线) 9 | * [Github](github.com) 10 | * etc(待补充) 11 | * (有关Java的有关站点和学习资料还是很多的,最好能有自己寻找学习资料的能力) 12 | 13 | ## 个人/团队 14 | 15 | * [遇见狂神说_bilibili](https://space.bilibili.com/95256449) 16 | * [黑马程序员_bilibili](https://space.bilibili.com/37974444) 17 | * [动力节点_bilibili](https://www.bilibili.com/video/BV1Rx411876f) 18 | * etc(待补充) 19 | 20 | ## 官方文档!!! 21 | 22 | 对于学习开发,最重要的事情是学会阅读文档。对于学习或使用一门框架,文档也是最好的老师。我们也希望在考核过程中,能够让大家培养好学习文档、编写文档、的能力。 23 | 24 | 以下列出一些常用官方文档 25 | 26 | * [Jdk1.8 官方文档 (oracle.com)](https://docs.oracle.com/javase/8/docs/api/index.html) 27 | * [Spring 中文文档](https://www.springcloud.cc/spring-reference.html) 28 | * [SpringBoot 官方文档 (spring.io)](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/) 29 | * [SpringCloud 官方文档 (spring.io)](https://spring.io/projects/spring-cloud/) 30 | * [Maven 官方文档 (apache.org)](https://maven.apache.org/guides/) 31 | * [Tomcat 官方文档 (apache.org)](https://tomcat.apache.org/tomcat-8.0-doc/index.html) 32 | * [Apache HTTP Server (apache.org)](https://httpd.apache.org/docs/) 33 | * [Dubbo 官方文档 (apache.org)](https://cn.dubbo.apache.org/zh-cn/docs/) 34 | * [RabbitMQ 官方文档](https://www.rabbitmq.com/documentation.html) 35 | * [Elasticsearch 官方文档](https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html) 36 | * [Docker 官方文档](https://docs.docker.com/get-started/) 37 | * [Kubernetes 官方文档](https://kubernetes.io/zh-cn/docs/home/) 38 | * [Nginx 中文文档](https://blog.redis.com.cn/doc/index.html) 39 | * [微信开发文档 (qq.com)](https://developers.weixin.qq.com/miniprogram/dev/framework/) 40 | 41 | 42 | 43 | ## 常用工具站 44 | 45 | * [Maven中央仓库 (mvnrepository.com)](https://mvnrepository.com/) 46 | * [Docker镜像仓库](https://hub.docker.com/) 47 | 48 | * [在线JSON校验格式化工具 (Be JSON)](https://www.bejson.com/) 49 | * [正则表达式在线编写校验工具 (regex101)](https://regex101.com/) 50 | * [Json/Yaml在线转换 (fly63.com)](https://www.fly63.com/tool/jsonyaml/) 51 | * [Unix时间戳转换 (zxgj.cn)](https://www.zxgj.cn/g/unix) 52 | 53 | ## 学习路线 54 | 55 | ![20230703142015](../img/20230703142015.png) -------------------------------------------------------------------------------- /docs/1-基础语法.md: -------------------------------------------------------------------------------- 1 | # Java & Android第一轮考核 2 | 3 | ## 参考资料 4 | 5 | ### 参考视频(建议刚接触编程语言的跟着视频走) 6 | 7 | * [狂神说Java]( https://www.bilibili.com/video/BV12J41137hu?share_source=copy_web&vd_source=7d2fd3963c594f890889ebd454ef8d1c) (通俗易懂,学习方法也讲的不错,后面也有一些内容是推荐狂神老师的视频教程) 8 | * [黑马程序员Java零基础视频教程](https://www.bilibili.com/video/BV17F411T7Ao?vd_source=e7a1a430689d9d09f914db65fcdea382) 9 | * [动力节点](https://www.bilibili.com/video/BV1Rx411876f?share_source=copy_web&vd_source=7d2fd3963c594f890889ebd454ef8d1c) 10 | * Android Kotlin: 11 | * [Kotlin 基础视频+文章 by 扔物线](https://rengwuxian.com/kotlin-basic-1/) 12 | * [Kotlin 从入门到实战](https://www.bilibili.com/video/BV1bZ4y1N7my) 13 | 14 | ### 基础参考教程 15 | 16 | * [廖雪峰Java入门教程 (liaoxuefeng.com)](https://www.liaoxuefeng.com/wiki/1252599548343744) **不建议跟着用Eclipse,关于IDE的推荐具体看下面的提示** 17 | * [编程狮](https://www.w3cschool.cn/java/) 18 | 19 | ### 推荐书籍 20 | 21 | * 《Head First Java》(图解多、讲的比较简单、内容不够全面,可以来读读提高兴趣) 22 | * 《Java核心技术卷Ⅰ》 23 | * 《Java核心技术卷Ⅱ》 24 | * 《Thinking In Java》 (人称Java圣经(可能会有些难懂)) 25 | * 对 Android 方向,可以提前通过以下资料熟悉下 Kotlin: 26 | * [Java to Kotlin](https://github.com/MindorksOpenSource/from-java-to-kotlin) 27 | * [Kotlin 官方指南](https://www.kotlincn.net/docs/reference/basic-syntax.html) 28 | 29 | 30 | 31 | ## 知识点 32 | 33 | * 配置编译环境 34 | * 输入输出 35 | * 选择结构、循环结构 36 | * 方法 37 | * 数组 38 | 39 | * 封装、继承、抽象、多态、接口 40 | * 异常处理 41 | * 集合 42 | * 泛型 43 | 44 | * Bonus 45 | * java.uitl以及java.math下的一些常用工具类 (如 Date / Math / Random / BigDemical / ArrayList 等等等等) 46 | * 多线程 (JUC) 47 | * 正则表达式 48 | 49 | ## 任务 50 | 51 | * 进行Java开发环境配置 52 | * 完成下面两项在线刷题网站的任务,当然写的越多越好,尽可能的熟悉java语言 53 | * 进行[基础编程题目集 (pintia.cn)](https://pintia.cn/problem-sets/14/exam/problems/type/7)练习,并达到至少一百分 54 | * 完成[27. 移除元素 - 力扣(LeetCode)](https://leetcode.cn/problems/remove-element/) 55 | > 注意:提交你的分数的截图或者可以其他证明你已经完成的截图 56 | * 宠物店 57 | 58 | > 用代码来写一个自己的宠物店,题目有点长耐心看~ 59 | > 完成下面的类 60 | > 61 | > **开一家宠物店,宠物店要有动物还要有顾客** 62 | 63 | **【强制】注意编程规范** 64 | [阿里巴巴开发规范](../etc/blog/阿里巴巴Java开发手册.pdf) 65 | 66 | **1. 一个Animal动物类 (抽象类 abstract )** 67 | 68 | * 变量: 69 | * 动物名(String) 70 | * 年龄(int) 71 | * 性别 72 | * 价格(double) 73 | * .... 74 | * 方法 75 | * 一个全参构造方法 76 | * 一个抽象的toString() 方法 77 | * ........ 78 | 79 | **2. 中华田园犬类** (extends Animal)img 80 | 81 | * 变量: isVaccineInjected(boolean 是否注射狂犬病疫苗) 82 | * 价格100元 83 | 84 | **3. 猫猫类 (extends Animal)**image-20221025220229577 85 | 86 | * 价格200元 87 | 88 | **4. 你喜欢的其他动物.....**image-20221025220257537 89 | 90 | * 自由选择 合理就行 91 | 92 | **5. 顾客类Customer** 93 | 94 | * 成员变量: 95 | * 顾客名字(String) 96 | * 到店次数(int) 97 | * 最新到店时间(LocalDate类) 98 | * 方法 99 | * 重写(@Override) toString() 方法, 要求按一定格式输出客户的所有信息 100 | 101 | **6. 宠物店接口AnimalShop(interface)** 102 | 103 | 你的宠物店需要有一些基础功能: 104 | 105 | * 买入新动物(需要的参数自己决定) 106 | * 招待客户(Customer) 107 | * 歇业() 108 | 109 | **7. MyAnimalShop自己的宠物店 (implements AnimalShop)** 110 | 111 | * 变量: 112 | 113 | * 店的余额double 114 | * 一个存放动物的列表 (使用ArrayList、LinkedList或其他你喜欢的List实现) 115 | * 一个顾客列表留作纪念 116 | * 是否正在营业 117 | * .... 118 | 119 | * 实现接口中的方法 120 | 121 | * 买入动物 -> 买入一只动物,记得在动物列表中添加, 122 | 123 | 如余额不足则抛出异常InsufficientBalanceException 124 | 125 | * 招待客户 -> 接受客户参数,在顾客列表中加入新顾客, 126 | 127 | 出售动物,如果店内没有动物,抛出AnimalNotFoundException。 128 | 129 | 通过toString输出动物信息,并把钱入账,将动物移除列表 130 | 131 | * 歇业 -> (LocalDate判断) 输出当天光顾的客户的列表信息,计算并输出一天的利润 132 | 133 | **8. 自定义异常类** 134 | 135 | * 异常类 (AnimalNotFountException) 没找到动物异常,店内没有动物可买时抛出 136 | * 异常类 (InsufficientBalanceException) 余额不足异常时抛出 137 | * 两个异常类均继承自RunTimeException, 异常中需要携带错误信息,方便捕获后处理和查看 138 | 139 | **9. 一个Test类, 用于测试你写的类功能是否正常** 140 | 141 | * 创建一个宠物店实例,给定余额,初始化动物列表,一个空的顾客列表 142 | * 测试买入动物,招待顾客,歇业 143 | * 建议多拿点例子测试,发现bug可以马上改,多考虑代码严谨性 144 | 145 | 146 | 147 | ## Bonus 148 | 149 | * 设计两个方法 150 | 151 | * 设计一个接收两个int数组的方法void, 使用多线程交替输出其中的元素 152 | * 如接收 arr1 = {1, 3, 5, 7, 9}, arr2 = {2, 4, 6, 8, 10}, 则输出 1 2 3 4 5 6 7 8 9 10 153 | * 设计一个接收邮箱String的方法boolean, 使用正则表达式或其他方法判断邮箱格式是否合法 154 | 155 | ## 156 | 157 | 158 | 159 | ## 提示 160 | 161 | * 学习资料择需学习即可,不需要全部学习 162 | 163 | * [ Java环境简单配置+IDEA](https://blog.csdn.net/qq_33215972/article/details/73693140) 164 | 165 | * 强烈建议使用[IntelliJ IDEA](https://www.jetbrains.com/zh-cn/idea/)进行编程 166 | 167 | * IntelliJ Idea(之后简称Idea)可以通过[福州大学邮件系统 (fzu.edu.cn)](https://fzu.edu.cn/coremail/index.jsp)进行[Idea学生认证](https://www.jetbrains.com/shop/eform/students/)后可免费使用一年(到期再次申请即可) 168 | * [2022 JetBrains 开发工具——学生授权免费申请指南 | Company Blog](https://blog.jetbrains.com/zh-hans/blog/2022/08/24/2022-jetbrains-student-program/) 169 | 170 | * 参数名请使用有意义的单词或短语,要求**见名知意** 171 | 172 | * 构造函数与get/set等自行斟酌 173 | * Bonus不做强制要求 174 | * 抽象类的成员变量均为protected 175 | * 部分变量选择尽量符合实际 176 | * 注意命名规范,使用驼峰命名法 177 | * 注意代码格式规范,可使用Idea自带的格式化快捷键 *ctrl+alt+L* 进行代码格式化 178 | * Test类的实现可用正则Regex或其他方法,写完后可在第一题中的main方法中测试功能是否正确 179 | * 除了面向对象,希望大家也能有面向 google/github/必应/csdn/博客园/百度 学习的能力 180 | * 如果有实在写不完或者不理解的部分,可以加一些注释写一点思路或者想法 (可以用`//TODO`来标记未完成的部分) 181 | 182 | * 大家刚开始写都会有些懵懵的,不知道在写啥,不过这次只要能写出来能跑就行了,不懂的以后会慢慢再接触了解的。 183 | 184 | 185 | -------------------------------------------------------------------------------- /docs/2-文件读取.md: -------------------------------------------------------------------------------- 1 | # Java第二轮考核 2 | 3 | > 该考核内容主要参考自[2024年福州大学软件工程实践第二次作业]([软件工程第二次作业--文件读取-CSDN社区](https://bbs.csdn.net/topics/618087255)) 4 | 5 | 6 | 7 | ## 任务 8 | 9 | 完成对**世界游泳锦标赛跳水项目**相关数据的收集,并实现一个能够对赛事数据进行统计的控制台程序,项目包括基本功能的实现、单元测试、性能分析与改进,以及README.md撰写。 10 | 11 | ### 需求 12 | 13 | 实现一个命令行程序,不妨称之为**DWASearch**。 14 | 本次作业所需数据均爬取自**世界游泳锦标赛官网的跳水项目**[比赛结果](https://www.worldaquatics.com/competitions/3337/66th-international-divers-day-rostock/results?event=108c795d-5e4f-4dc6-acea-0bc70bfd1928)和[参赛选手信息](https://www.worldaquatics.com/competitions/3337/66th-international-divers-day-rostock/athletes?gender=&countryId=)【该爬取行为仅用于课程教学】 15 | 16 | 数据收集部分可以参考往届优秀作业[[参考链接\]](https://blog.csdn.net/CowBoyHS/article/details/129327279?csdn_share_tail={"type"%3A"blog"%2C"rType"%3A"article"%2C"rId"%3A"129327279"%2C"source"%3A"CowBoyHS"}) 17 | 18 | > 如需http请求工具或者Json解析工具,可以使用**Maven**方式导入 19 | 20 | 21 | 22 | ### 1. 基本功能 23 | 24 | 假设有一个软件可以输出2024世界游泳锦标赛跳水项目的选手信息和比赛结果。 25 | 输入指令和输出文件以命令行参数传入。例如我们在命令行窗口(cmd)中输入: 26 | 27 | ```bash 28 | Java -jar DWASearch.jar input.txt output.txt 29 | ``` 30 | 31 | 32 | 33 | #### 1.1 输出所有选手信息 34 | 35 | 当input.txt的内容为 36 | 37 | ```javascript 38 | players 39 | ``` 40 | 41 | 则会输出**参与世界游泳锦标赛跳水项目的所有选手信息**到**output.txt**,输出格式如下: 42 | 43 | 1. 其中`Full Name`对应选手全名,`Gender`为选手性别, `Country`为国籍。 44 | 2. 换行使用'\n',编码统一使用UTF-8。 45 | 3. 顺序以国籍为首要关键字升序、选手的名(Last Name)为次要关键字升序排序(若爬取的数据已经排序完成则仅需要依次提取需要的信息,不必编写排序过程的代码)。 46 | 4. 每输出一个选手的相关信息后,以5个“-”单独成行作为分割线,最后一个选手信息输出后仍要输出一行分割线。 47 | 5. 输出的内容中除选手名称和选手国籍可能存在空格,其他信息不得增加多于的空格或者其他符号。 48 | 49 | 输出格式: 50 | 51 | ```javascript 52 | Full Name:string 53 | Gender:string 54 | Country:string 55 | ----- 56 | ... 57 | ----- 58 | Full Name:string 59 | Gender:string 60 | Country:string 61 | ----- 62 | ``` 63 | 64 | 输出样例: 65 | 66 | ```javascript 67 | Full Name:HART Alexander 68 | Gender:Male 69 | Country:Austria 70 | ----- 71 | Full Name:LOTFI Dariush 72 | Gender:Male 73 | Country:Austria 74 | ----- 75 | ... 76 | ----- 77 | Full Name:DICK Elaena 78 | Gender:Female 79 | Country:Canada 80 | ----- 81 | ``` 82 | 83 | --- 84 | 85 | 86 | 87 | #### 1.2 输出决赛每个运动项目结果 88 | 89 | 当input.txt的内容为 90 | 91 | ```javascript 92 | result women 1m springboard 93 | ``` 94 | 95 | 则会输出**女子1m跳板**的决赛结果到**output.txt**,输出格式如下: 96 | 97 | 1. `Full Name`对应选手姓名。 98 | 2. `Rank`为排名。格式如`'1'`。 99 | 3. `Score`表示决赛中该选手的成绩。格式如`'score1 + score2 + score3 + ··· + score7 = totalPoint'`。 100 | 101 | 输出格式: 102 | 103 | ```javascript 104 | Full Name:string 105 | Rank:string 106 | Score:string 107 | ----- 108 | ... 109 | ----- 110 | Full Name:string 111 | Rank:string 112 | Score:string 113 | ----- 114 | ``` 115 | 116 | 输出样例: 117 | 118 | ```javascript 119 | Full Name:MULLER Jette 120 | Rank:1 121 | Score:51.60 + 52.00 + 51.75 + 46.80 + 46.80 = 248.95 122 | ----- 123 | Full Name:ROLLINSON Amy 124 | Rank:2 125 | Score:46.00 + 42.90 + 50.70 + 54.00 + 46.80 = 240.40 126 | ----- 127 | ... 128 | ----- 129 | Full Name:SANTIAGO Dominique 130 | Rank:12 131 | Score:42.00 + 18.20 + 35.70 + 34.50 + 32.55 = 162.95 132 | ----- 133 | ``` 134 | 135 | --- 136 | 137 | 138 | 139 | #### 1.3 输出详细结果 140 | 141 | 当input.txt的内容为 142 | 143 | ``` 144 | result women 10m platform detail 145 | ``` 146 | 147 | 则会输出世界游泳锦标赛跳水项目**女子10m跳台**的所有比赛结果到**output.txt**,输出格式如下: 148 | 149 | 1. `Full Name`对应选手姓名。 150 | 2. `Rank`为排名。格式如`'1 | 2 | 3'`表示初赛排名为1,半决赛排名为2,决赛排名为3。部分比赛若无半决赛或初赛又或者选手未参加半决赛或决赛的则用`'*'`占位表示,如`'* | * | 1'`。 151 | 3. `Preliminary Score`表示初赛中该选手的成绩。格式如`'score1 + score2 + score3 + ··· + score7 = totalPoint'`。若无初赛则用`'*'`表示。 152 | 4. `Semifinal Score`表示半决赛中该选手的成绩。格式如`'score1 + score2 + score3 + ··· + score7 = totalPoint'`。若无半决赛或者选手未参加则用`'*'`表示。 153 | 5. `Final Score`表示决赛中该选手的成绩。格式如`'score1 + score2 + score3 + ··· + score7 = totalPoint'`。若选手未参加则用`'*'`表示。 154 | 6. 输出顺序按照选手**第一次比赛的排名顺序**输出。 155 | 7. 注意空格出现的位置,不能有多于的空格,否则会被判定为格式错误。 156 | 8. **[补充]** 如果遇到**双人项目**,选手姓名格式命名为`'A & B'`按照选手名(Last Name)从小到大排序,例如`'JACHIM Filip & LUKASZEWICZ Robert'` 157 | 158 | 输出格式: 159 | 160 | ```javascript 161 | Full Name:string 162 | Rank:string 163 | Preliminary Score:string 164 | Semifinal Score:string 165 | Final Score:string 166 | ----- 167 | ... 168 | ----- 169 | Full Name:string 170 | Rank:string 171 | Preliminary Score:string 172 | Semifinal Score:string 173 | Final Score:string 174 | ----- 175 | ``` 176 | 177 | 输出样例: 178 | 179 | ```javascript 180 | Full Name:FUNG Katelyn 181 | Rank:1 | 4 | 2 182 | Preliminary Score:60.20 + 56.00 + 57.60 + 54.00 + 70.40 = 298.20 183 | Semifinal Score:46.20 + 30.80 + 68.80 + 61.50 + 67.20 = 274.50 184 | Final Score:58.80 + 54.60 + 57.60 + 54.00 + 72.00 = 297.00 185 | ----- 186 | ... 187 | ----- 188 | Full Name:SANTIAGO Dominique 189 | Rank:14 | 11 | 11 190 | Preliminary Score:39.20 + 21.00 + 41.85 + 31.20 + 13.05 = 146.30 191 | Semifinal Score:42.00 + 22.50 + 17.55 + 42.90 + 39.15 = 164.10 192 | Final Score:42.00 + 28.50 + 20.25 + 41.60 + 44.95 = 177.30 193 | ----- 194 | ``` 195 | 196 | **补充说明:** 197 | 对于**input.txt**,有可能会出现多行输入的样例,例如: 198 | 199 | ```javascript 200 | players 201 | result women 1m springboard 202 | result women 1m springboard 203 | players 204 | ``` 205 | 206 | 此时的输出文件**output.txt**中的内容为: 207 | 208 | ```javascript 209 | Full Name:HART Alexander 210 | Gender:Male 211 | Country:Austria 212 | ----- 213 | Full Name:LOTFI Dariush 214 | Gender:Male 215 | Country:Austria 216 | ----- 217 | ... 218 | ----- 219 | Full Name:MULLER Jette 220 | Rank:1 221 | Score:51.60 + 52.00 + 51.75 + 46.80 + 46.80 = 248.95 222 | ----- 223 | Full Name:ROLLINSON Amy 224 | Rank:2 225 | Score:46.00 + 42.90 + 50.70 + 54.00 + 46.80 = 240.40 226 | ----- 227 | ... 228 | ----- 229 | Full Name:MULLER Jette 230 | Rank:1 231 | Score:51.60 + 52.00 + 51.75 + 46.80 + 46.80 = 248.95 232 | ----- 233 | Full Name:ROLLINSON Amy 234 | Rank:2 235 | Score:46.00 + 42.90 + 50.70 + 54.00 + 46.80 = 240.40 236 | ----- 237 | ... 238 | ----- 239 | Full Name:HART Alexander 240 | Gender:Male 241 | Country:Austria 242 | ----- 243 | Full Name:LOTFI Dariush 244 | Gender:Male 245 | Country:Austria 246 | ----- 247 | ... 248 | ----- 249 | Full Name:MYALIN Igor 250 | Gender:Male 251 | Country:Uzbekistan 252 | ----- 253 | ``` 254 | 255 | 其中,每个指令的输出紧贴上一个指令的输出,**无需空行**。 256 | 257 | **指令区分大小写**,指令中**所有的英文字母都采用小写的形式**。 258 | 假如输入无法处理的指令,例如: 259 | 260 | 1. 无法识别的指令,则输出`Error` 261 | 262 | 2. result后的比赛项目名称应为如下这些名称之一,如果不正确,则输出`N/A` 263 | 264 | ```js 265 | women 1m springboard 266 | women 3m springboard 267 | women 10m platform 268 | women 3m synchronised 269 | women 10m synchronised 270 | men 1m springboard 271 | men 3m springboard 272 | men 10m platform 273 | men 3m synchronised 274 | men 10m synchronised 275 | ``` 276 | 277 | 3. 在比赛项目名称后只能加上'detail'字符,如果字符不正确,则输出`N/A`。 278 | 279 | input.txt样例: 280 | 281 | ```javascript 282 | player 283 | Players 284 | resultwomen 1m springboard 285 | result women 10m springboard 286 | result sss 287 | result detail 288 | result women 1m springboard details 289 | result men 10m synchronised 290 | players 291 | ``` 292 | 293 | output.txt输出: 294 | 295 | ```javascript 296 | Error 297 | ----- 298 | Error 299 | ----- 300 | Error 301 | ----- 302 | N/A 303 | ----- 304 | N/A 305 | ----- 306 | N/A 307 | ----- 308 | N/A 309 | ----- 310 | N/A 311 | ----- 312 | Full Name:HART Alexander 313 | Gender:Male 314 | Country:Austria 315 | ----- 316 | Full Name:LOTFI Dariush 317 | Gender:Male 318 | Country:Austria 319 | ----- 320 | ... 321 | ----- 322 | Full Name:MYALIN Igor 323 | Gender:Male 324 | Country:Uzbekistan 325 | ----- 326 | ``` 327 | 328 | --- 329 | 330 | 331 | 332 | ### 2. 接口封装 333 | 334 | 你是否有发现上面的代码会有这样一个问题:代码散落在各个函数中,很难剥离出来作为一个独立的模块运行以满足不同的需求。 335 | 336 | 这些代码的种类不同,混杂在一起对于后期的维护扩展很不友好,所以它们的组织结构就需要精心的整理和优化。 337 | 338 | 为了提高代码的可维护性和可扩展性,需要将基本功能独立成一个模块,称之为"Core模块"。这个模块包括两个基本功能: 339 | 340 | 1. **输出所有选手信息** 341 | 2. **输出每个比赛项目的结果** 342 | 343 | 这样的设计使得命令行能够共享相同的代码,通过定义清晰的API,实现与其他模块的交流。 344 | 345 | API设计应考虑以下几点: 346 | 347 | - **清晰的函数命名:** 使函数名称表达其功能,方便其他开发者理解。 348 | - **参数设计:** 确定需要传递的参数类型和数量,使接口简洁而有用。 349 | - **错误处理:** 考虑可能的错误情况,设计良好的错误处理机制。 350 | 351 | 参考代码: 352 | 353 | ```java 354 | public class CoreModule { 355 | // 输出所有选手信息 356 | public void displayAllPlayersInfo() { 357 | // 实现代码... 358 | } 359 | 360 | // 输出每个比赛项目的结果 361 | public void displayResultsForEachEvent() { 362 | // 实现代码... 363 | } 364 | 365 | ....... 366 | } 367 | 368 | ``` 369 | 370 | 这个模块至少可以在几个地方使用: 371 | 372 | - 命令行测试程序使用 373 | - 在单元测试框架下使用 374 | 375 | 376 | 377 | ### 3. 单元测试和性能分析 378 | 379 | 要设计**至少10个测试用例**,确保你的程序能够正确处理各种情况。 380 | 381 | > 推荐使用junit实现单元测试 382 | 383 | 请阅读邹欣老师的博客:[单元测试和回归测试](https://www.cnblogs.com/xinz/archive/2011/11/20/2255830.html),编写程序的单元测试 384 | 385 | 386 | 387 | ### 4. 项目结构 388 | 389 | 1. 在src目录下必须有名为`DWASearch.java`文件,且DWASearch.java中包含 `public static void main(String[] args) `方法 390 | 2. 请将Java代码打包成jar 391 | 392 | ``` 393 | |- src 394 | |- DWASearch.java 主程序,可以从命令行接收参数;确保文件名一致、区分大小写 395 | |- Lib.java 包含其它自定义函数,可以有多个,对名字不做要求 396 | |- data 文件夹,存放程序的数据 397 | |- DWASearch.jar 398 | |- README.md 描述你的项目,包括如何运行、功能简介、作业链接等 399 | ``` 400 | 401 | **注意:** 402 | 403 | README.md 需要包括一下内容: 404 | 405 | 1. 记录**模块接口的设计与实现过程**。设计包括代码如何组织,比如会有几个类,几个函数,他们之间关系如何,关键函数是否需要画出流程图?说明你的算法的关键(不必列出源代码),以及独到之处。 406 | 2. 计算模块接口部分的**性能改进**。 407 | 3. 计算模块部分**单元测试展示**。 408 | 4. 计算模块部分**异常处理说明**。 409 | -------------------------------------------------------------------------------- /docs/3-数据库.md: -------------------------------------------------------------------------------- 1 | # Java 第三轮考核 2 | 3 | ## 参考资料 4 | 5 | * [MySQL[8.0] 解压版安装教程](https://blog.csdn.net/tyler880/article/details/109106093) 6 | * **推荐使用8.0版本**,当然5.7也是主流版本 7 | * 解压版相比安装版小很多,也可以使用安装版。具体安装教程可以百度。~~(安装版就无脑next就可以了罢)~~ 8 | * SQL教程 9 | * [SQL教程 - 廖雪峰的官方网站 (liaoxuefeng.com)](https://www.liaoxuefeng.com/wiki/1177760294764384),这个讲的没有很详细但是看着入门挺快 10 | * [狂神说MySQL](https://www.bilibili.com/video/BV1NJ411J79W/?share_source=copy_web&vd_source=7d2fd3963c594f890889ebd454ef8d1c)(个人很喜欢的一个up,学起来比较轻松入门快) 11 | * 持久层部分 (使用Java对数据库进行操作) 12 | * **JDBC**(最基础最底层的连接数据库方式) 13 | * [JDBC优质博客](https://blog.csdn.net/jungle_rao/article/details/81274720) 14 | * 上面狂神说MySQL视频最后部分 15 | * Mybatis (扩展提升) 16 | * [狂神说Mybatis](https://www.bilibili.com/video/BV1NE411Q7Nx/?share_source=copy_web&vd_source=7d2fd3963c594f890889ebd454ef8d1c) 17 | * 到这类框架学习掌握一定技巧之后其实可以尝试直接查看官方文档学习 18 | * 其他框架 19 | * 自行百度 20 | * [数据库设计三大范式](https://www.cnblogs.com/knowledgesea/p/3667395.html) 21 | 22 | ## 知识点 23 | 24 | * 数据库 (Mysql) 25 | * Sqlyog/Navicate 等图形化工具的使用 26 | * 数据库设计范式 27 | * jdbc/持久层框架 28 | * Bonus 29 | * Maven环境搭建 30 | * Json数据结构 31 | * 第三方接口调用 32 | * **Spring** 框架 33 | 34 | ## 任务 35 | 36 | 假设工作室近期需要置办一系列搭建,邀请各位编写一个简单的订单管理系统(用来记账) 37 | 38 | * 至少需要记录以下信息: 39 | 40 | * 商品: 商品编号、商品名、商品价格 41 | * 订单: 订单编号、**商品信息**(考虑如何合理存储关联信息)、下单时间、订单价格 42 | 43 | * 需要将信息保存至**数据库** 44 | 45 | * 编写JDBC工具类,功能包括但不限于: 46 | 47 | * 处理数据库连接 48 | * 执行增删查改操作 49 | 50 | * 解决**SQL注入问题** 51 | * 添加**事务管理** 52 | * 包含异常处理和资源释放 53 | 54 | * **使用编写的JDBC工具类实现商品和订单信息的增删改查、更新,商品和订单排序(价格、下单时间)等功能** 55 | 56 | * 在创建订单时,实施数据验证,确保订单信息的完整性和准确性。例如,检查商品是否存在,价格是否合法等等。 57 | * 如果想要删除已经存在在订单中的商品,你要怎么处理? 58 | * 避免使用SELECT * 59 | 60 | * **完整的测试** 61 | 62 | * 可以自己设计新功能,有需要也可以自行增加字段。 63 | 64 | * 在代码中加入必要的注释,说明每个方法的作用和功能,以及关键代码的解释。 65 | 66 | * 使用合适的代码风格和命名规范,确保代码的可读性和可维护性。 67 | 68 | * 编写README.md文档,内容可以是你的学习记录与心得,方便我们了解你们的学习状况 69 | 70 | 71 | ## Bonus 72 | 73 | ### 基础任务进阶需求 74 | 75 | * 实现分页查询。 76 | * 使用其他连接池,如Druid。 77 | * 使用mybatis等orm框架。 78 | * **使用spring框架暴露http路由(日后常用)** 79 | 80 | > 第四轮的难度有一定的提升,请在**保证第三轮作业的质量**的前提下,去提前看看第四轮 81 | 82 | ### 第三方Api调用 83 | 84 | > 你觉得福州天气变化无常img,想制作一个天气查询系统查询福州和其他部分城市的天气 85 | 86 | * 使用和风天气提供的免费 API 来完成任务 87 | 88 | * [和风开发者平台官网 (qweather.com)](https://dev.qweather.com/) 89 | 90 | * [和风天气WebApi使用教程](https://www.cnblogs.com/6543x1/p/15684812.html) 91 | 92 | * 使用的api 93 | 94 | * 城市查询 API,用于查询城市的 城市ID(id)、纬度(lat)、经度(lon)等城市信息 95 | 96 | https://geoapi.qweather.com/v2/city/lookup?key=这里填你的key&location=要查询的城市 名字 97 | 98 | * 三日天气查询 api ,用于查询某地今日、明日、后日的天气信息 99 | 100 | https://devapi.qweather.com/v7/weather/3d?key=这里填你的key&location=要查询的城市 的id 101 | 102 | * 以上 api 返回信息均为 json 格式,请选择合适的 json 工具进行处理并保存信息到数据库中。 103 | 104 | * 在Java中通过上述api获取信息,并保存到数据库中,数据库的结构请自行设计,注意合理性。 105 | 106 | * 至少需要以下三个城市的基本信息以及它们对应的天气信息:北京市、上海市、福州市。如果你有 其他想要的城市也可以加入到数据库中 107 | 108 | * 至少需要以下三个城市的基本信息以及它们对应的天气信息:北京市、上海市、福州市。如果你有 其他想要的城市也可以加入到数据库中 109 | 110 | * 城市的天气信息,需要:当日日期(fxDate),当日最高气温(tempMax)、当日最低气温 (tempMin)、白天天气情况(textDay),同时还需要记录这个天气属于哪个城市,以便于直接 查询某个城市的天气信息。注意明天和后天的天气信息也要保存到数据库中。 111 | 112 | * 注意:假设你在1月1日查询了福州市的未来三日(1号、2号、3号)天气信息, 又在1月2日查 询了福州市三日天气信息(2号、3号、4号),新查询的2号、3号信息要覆盖之前记录在数据 库中的信息,不要重复记录。 113 | 114 | * 数据库的结构、细节请自行设计。 115 | 116 | * 控制台中提供从数据库查询数据(比如查询福州市三日的天气信息)、以及更新数据的方法, 可以自行设计。功能设计较好可以加分 117 | 118 | ### Maven管理项目依赖 119 | 120 | 可以先了解有关maven的使用,后面会用上 121 | 122 | ## 提示 123 | 124 | * 数据库推荐使用 **mysql** (也可使用oracle database、sql server等等) 125 | * 数据库连接可以从 **JDBC** 写起,也可以使用框架,如 mybatis、hibernate、spring data、jpa 126 | * 推荐使用数据库可视化工具,如 SQLyog、Navicate 127 | * 了解数据库三大范式,多考虑如何将表设计的更加合理,减少冗余 128 | 129 | 130 | 131 | * 有关第三方Api调用 132 | 133 | * 天气查询api免费用户每日只能请求1000次,注意不要过度请求 134 | 135 | * 请合理设计数据库,减少冗余,注意表之间的联系 136 | 137 | * api请求可以使用第三方包如` HttpClient` ,或者Java原生的 `HttpURLConnection` 138 | 139 | * 本作业建议使用maven管理项目的包。可以自行百度所需包的依赖并引入。 140 | 141 | (注意版本问题,有时 候太老的版本可能不兼容,可以在 [[Maven Repository (mvnrepository.com)])](https://mvnrepository.com/)上搜索你的需要包,点进去看最新版本是啥,引入对应包即可(可以直接从 版本号点进去,里面写好了依赖,可以直接copy到你的 pom.xml 中)) 142 | 143 | * Java中直接获取数据会乱码,这是因为原api使用了gzip进行压缩,需要进行如下处理: 144 | 145 | ~~~java 146 | //获取请求结果的inputStream,根据你选择的工具决定,以下是几个常见http工具获取结果的 147 | inputstream方法 148 | //HTTPURLConnection: httpURLConnection.getInputStream() 149 | //Commons-httpclient: getMethod.getResponseBodyAsStream() 150 | //org.apache.http.client.HttpClient: response.getEntity().getContent() 151 | GZIPInputStream gzipInputStream =new GZIPInputStream(//这里填通过http工具请求 152 | api获取的inputstream); 153 | StringBuilder res=new StringBuilder(); 154 | String line; 155 | BufferedReader br = new BufferedReader(new 156 | InputStreamReader(gzipInputStream, StandardCharsets.UTF_8)); 157 | while ((line = br.readLine()) != null) { 158 | res.append(line); 159 | } 160 | System.out.println(res);//res就是请求的结果,注意res为StringBuilder 161 | ~~~ 162 | 163 | 164 | -------------------------------------------------------------------------------- /docs/4-单人作业.md: -------------------------------------------------------------------------------- 1 | # Java 第四轮单人考核 2 | 3 | ## 部分参考资料 4 | 5 | * 文档资料 6 | * [mybatis – MyBatis 3 | 简介](https://mybatis.org/mybatis-3/zh/index.html) 7 | * [Spring 中文文档](https://www.springcloud.cc/spring-reference.html) 8 | * [SpringBoot 官方文档 (spring.io)](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/) 9 | * 视频资料 10 | * [狂神说mybatis](https://www.bilibili.com/video/BV1NE411Q7Nx/?spm_id_from=333.999.0.0) 11 | * [狂神说spring](https://www.bilibili.com/video/BV1NE411Q7Nx/?spm_id_from=333.999.0.0) 12 | * [狂神说Springboot](https://www.bilibili.com/video/BV1PE411i7CV) 13 | * [黑马程序员spring](https://www.bilibili.com/video/BV1Fi4y1S7ix?vd_source=e7a1a430689d9d09f914db65fcdea382)(一条龙) 14 | 15 | 16 | 17 | ## 知识点 18 | 19 | * 数据库 (**Mysql**) 20 | * **Redis** 21 | * **Mybatis**、**MybatisPlus**数据持久层框架 22 | * 数据持久层: 将数据持久化保存在数据库或其他文件中的框架或中间件 23 | * **Spring** 框架 24 | * **SpringBoot / SpringMVC** 框架 25 | * 可以按顺序学习,也可以适当跳过一些有关Spring全家桶的内容。最好能了解一些原理,从Spring到MVC再到Boot学下来会有质的提升(最后会发现SpringBoot真的好用)。 26 | * **安全框架,如SpringSecurity、Shiro等** 27 | * **Docker的使用** 28 | 29 | 30 | 31 | ## 任务 32 | 33 | > 你的任务是编写一个弹幕视频网站的API接口,具体要求如下: 34 | 35 | 项目要求 36 | 37 | - 提供一份**接口文档**和**项目结构图(目录树**)。 38 | - 注意编程规范: 39 | * [编程规范](../etc/blog/编程规范.md) 40 | * [阿里巴巴开发规范](../etc/blog/阿里巴巴Java开发手册.pdf) 41 | - 完成Docker部署,编写Dockerfile并成功部署你的项目。 42 | - 日志打印 43 | 44 | 请遵照以下接口文档完成功能 45 | 46 | [https://apifox.com/apidoc/shared-ea00e6b2-382f-4df7-b488-d269314a9cb9](https://apifox.com/apidoc/shared-ea00e6b2-382f-4df7-b488-d269314a9cb9) 47 | 48 | 49 | 你不必完成以上的全部功能,以下完成本次作业的最低要求(共计 17 个接口,已经非常少了) 50 | 51 | | 模块名 | 最低需要完成的接口 | 数量 | 52 | | ------ | -------------------------------------------- | ---- | 53 | | 用户 | 注册、登录、用户信息、上传头像 | 4 | 54 | | 视频 | 投稿、发布列表、搜索视频、热门排行榜 | 4 | 55 | | 互动 | 点赞操作、点赞列表、评论、评论列表、删除评论 | 5 | 56 | | 社交 | 关注操作、关注列表、粉丝列表、好友列表 | 4 | 57 | 58 | 59 | 60 | #### 用户模块 61 | 62 | 1. 用户的登录注册(需要你返回token、结合安全框架) 63 | 2. 使用安全框架实现用户身份认证 64 | 3. 用户头像功能(上传头像、修改头像) 65 | 4. 获取用户信息 66 | 67 | #### 社交模块 68 | 69 | 1. 用户的关注、互关、取关 70 | 2. 获取单个用户的粉丝列表 71 | 3. 获取单个用户的关注列表 72 | 4. 获取单个用户的朋友列表(当两个人互相关注,我们认为这两个人属于朋友) 73 | 74 | #### 视频模块 75 | 76 | 1. 上传视频 77 | 2. 评论视频 (请注意,评论是可以有回复的) 78 | 3. 点击量排行榜(Redis) 79 | 80 | #### 搜索 81 | 82 | - 搜索视频,用户(搜索条件包括但不限于:年份,类别,发布时间等等) 83 | - 保留所有的历史搜索记录(Redis) 84 | - 排序视频(点击量,发布时间) 85 | 86 | 87 | 88 | ##### 注意: 89 | 90 | * ID的生成请使用雪花算法等(MybatisPlus有对应的ID生成策略) 91 | 92 | * 同时,希望你们并不是仅仅完成CRUD,而是对项目的**性能优化**与**并发控制**有一定的**实现**(加分加分!!) 93 | 94 | 以下给出几个考虑方面: 95 | 96 | * **数据库优化**:什么时候加索引?如何防止索引失效?如何避免大事务?为什么要避免使用select * ? 97 | * **异步处理**:上传视频这种耗时较长的任务是否可以用异步处理? 98 | * **并发控制**:是否可以用适当的锁和同步机制以避免并发问题?锁的粒度如何设置? 99 | * **池化思想**:为什么建议创建线程池? 100 | * **限流**:如何防止流量过大的问题?有哪些限流方案? 101 | * ...... 102 | 103 | 104 | 105 | ### Bonus 106 | 107 | **如果你想挑战更多,可以考虑以下Bonus任务:** 108 | 109 | 1. 使用对象存储服务(如阿里云、腾讯云、七牛云)来存储大文件。 110 | 2. 实现大视频的分片处理。 111 | 3. 添加管理员功能,以管理网站内容。(安全框架实现鉴权) 112 | 4. 使用Elasticsearch实现高效的搜索功能。 113 | -------------------------------------------------------------------------------- /docs/4-合作作业.md: -------------------------------------------------------------------------------- 1 | # Java 第四轮合作考核 2 | 3 | > 本轮为第一轮寒假轮考核。今年我们在正式考核前加入了预热环节。 4 | 5 | ## 目的 6 | 7 | - 与前端同学合作,实现前后端对接,实现项目的真正落地 8 | - 对SpringBoot的掌握 9 | - 熟悉开发流程与规范 10 | 11 | ## 知识点 12 | 13 | * 数据库 (**Mysql**) 14 | * **Redis** 15 | * **Mybatis**、**MybatisPlus**数据持久层框架 16 | * 数据持久层: 将数据持久化保存在数据库或其他文件中的框架或中间件 17 | * **Spring** 框架 18 | * **SpringBoot / SpringMVC** 框架 19 | * 可以按顺序学习,也可以适当跳过一些有关Spring全家桶的内容。最好能了解一些原理,从Spring到MVC再到Boot学下来会有质的提升(最后会发现SpringBoot真的好用)。 20 | * **安全框架,如SpringSecurity、Shiro等** 21 | * **Docker的使用** 22 | 23 | ## 任务 24 | 25 | ### Warm Up:实现一个简单的Todolist对接 26 | 27 | - 这一步旨在先熟悉一下前后端该如何合作与对接(后端写的接口,前端该如何调用,前端发送的数据,后端该如何接收),不需要写很复杂,页面简单,能实现基本的待办清单功能即可。 28 | - 也可以在第三轮的作业上进行修改,补充对接逻辑。 29 | - 这个部分**为期7天**。 30 | 31 | ### 寒假轮考核:仿一个社区平台——稀土掘金 32 | > 请完成部署 33 | > 34 | > 红线涂掉的就是不需要的 35 | 36 | 1. 首页 37 | 38 | ⽂章榜:按点击量降序 39 | 40 | 最新:最新发布的⽂章 41 | 42 | 写⽂章按钮:点击之后进入文章编辑界面 43 | 44 | 带头像的下拉菜单:有“我的主页”这一项就行 45 | 46 | [![img](https://github.com/west2-online-reserve/collection-frontends/raw/main/img/4-%E5%AF%92%E5%81%87%E5%90%88%E4%BD%9C%E8%BD%AE/image.png)](https://github.com/west2-online-reserve/collection-frontends/blob/main/img/4-寒假合作轮/image.png) 47 | 48 | [![img](https://github.com/west2-online-reserve/collection-frontends/raw/main/img/4-%E5%AF%92%E5%81%87%E5%90%88%E4%BD%9C%E8%BD%AE/image-1.png)](https://github.com/west2-online-reserve/collection-frontends/blob/main/img/4-寒假合作轮/image-1.png) 49 | 50 | 2. 文章页 51 | 52 | 内容展示:Markdown 解析与渲染 53 | 54 | 点赞按钮:点赞收藏文章 55 | 56 | 评论功能:普通⽂本评论就⾏,不⽤加表情/图⽚/链接等,要求带⼦评论 57 | 58 | [![img](https://github.com/west2-online-reserve/collection-frontends/raw/main/img/4-%E5%AF%92%E5%81%87%E5%90%88%E4%BD%9C%E8%BD%AE/image-2.png)](https://github.com/west2-online-reserve/collection-frontends/blob/main/img/4-寒假合作轮/image-2.png) 59 | 60 | [![img](https://github.com/west2-online-reserve/collection-frontends/raw/main/img/4-%E5%AF%92%E5%81%87%E5%90%88%E4%BD%9C%E8%BD%AE/image-3.png)](https://github.com/west2-online-reserve/collection-frontends/blob/main/img/4-寒假合作轮/image-3.png) 61 | 62 | 3. 我的主页 63 | 64 | 个人简介 65 | 66 | 我写的⽂章 67 | 68 | 我点赞的⽂章 69 | 70 | 进入设置页按钮 71 | 72 | [![img](https://github.com/west2-online-reserve/collection-frontends/raw/main/img/4-%E5%AF%92%E5%81%87%E5%90%88%E4%BD%9C%E8%BD%AE/image-4.png)](https://github.com/west2-online-reserve/collection-frontends/blob/main/img/4-寒假合作轮/image-4.png) 73 | 74 | 4. 设置页 75 | 76 | 支持修改用户名 77 | 78 | 支持修改密码 79 | 80 | 支持修改头像 81 | 82 | [![img](https://github.com/west2-online-reserve/collection-frontends/raw/main/img/4-%E5%AF%92%E5%81%87%E5%90%88%E4%BD%9C%E8%BD%AE/image-5.png)](https://github.com/west2-online-reserve/collection-frontends/blob/main/img/4-寒假合作轮/image-5.png) 83 | 84 | 5. 写文章界面 85 | 86 | 拥有文章标题 87 | 88 | 支持Markdown编写,且支持实时预览 89 | 90 | 发布按钮 91 | 92 | 其他都不需要 93 | 94 | [![img](https://github.com/west2-online-reserve/collection-frontends/raw/main/img/4-%E5%AF%92%E5%81%87%E5%90%88%E4%BD%9C%E8%BD%AE/image-6.png)](https://github.com/west2-online-reserve/collection-frontends/blob/main/img/4-寒假合作轮/image-6.png) 95 | 96 | ## Bonus 97 | 98 | > 这些内容和组队的前端同学讨论后,自行选择性完成 99 | 100 | 1. 除了可以写⽂章,还有草稿箱:暂存写到⼀半的⽂章 101 | 2. ⽀持⽂章⽬录功能(点击后可以跳转到同⼀⻚⾯的位置) 102 | 3. 允许多层嵌套评论(现有的⽹站⼤多数使⽤⼆级评论,没有做到真正的多级) 103 | 4. ⽀持搜索功能(模糊搜索) 104 | 5. 点赞使⽤缓存处理,不要求很难的逻辑 105 | -------------------------------------------------------------------------------- /docs/5-即时通信.md: -------------------------------------------------------------------------------- 1 | # Java 第五轮考核 2 | 3 | ## 知识点 4 | 5 | * 掌握Http协议和Web工作原理 6 | * 掌握 WebSocket 原理与实践 7 | * 掌握关系型或非关系型数据库的基本操作 8 | * 掌握消息队列的使用场景,使用方式(RabbitMQ、RocketMQ、Kafka等) 9 | 10 | ## 背景 11 | 12 | 众所周知,FanOne是个家喻户晓的**Aquaman**,她经常在社交软件上找小哥哥们聊天,以至于被多个平台封杀,请你写一个IM即时通信系统,让FanOne能聊天自由吧! 13 | 14 | ## 任务 15 | 16 | 在第四轮的视频网站上添加聊天室功能,和视频审核功能,并优化第四轮的Sql语句 17 | 18 | > 编写一个 IM 即时通信系统 支持单聊,群聊,并且支持查找一定时间内的聊天记录 19 | > 20 | > 除了优化重构第四轮的模块,你还需要编写以下模块 21 | > 22 | > websocket模块,联系人模块,会话模块,信息模块 23 | 24 | * 重构好友列表逻辑,即为联系人模块 25 | 26 | * 对于聊天内容,请使用Redis + Mysql的方式实现 27 | 28 | * 对于消息的存储,请使用消息队列异步提交 29 | 30 | * 会话列表,支持文字,图片交流,屏蔽功能 31 | * 请使用SpringSecurity的身份功能,完成视频的审核模块 32 | 33 | * 除此之外,本轮禁止使用mybatisPlus,请使用mybatis优化你的sql 34 | 35 | ## 请思考并回答以下问题 36 | 37 | 1. 请描述比起http协议,WebSocket协议在聊天模块里都有哪些优势 38 | 39 | 2. 使用消息队列带来的哪些好处,多角度回答 40 | 41 | 3. 由于网络问题、生产者重试机制等原因,可能会导致同一条消息被生产者重复发送。造成聊天室出现重复的信息,该如何解决 42 | 43 | 4. 为了保护用户被骚扰,bilibili和抖音在互相关注,或对方回复之前只允许发送一条信息,我们不需要执行这么复杂的逻辑,但请在一定程度上保护用户的权益 44 | 45 | 5. 比起网络上的在线聊天室,我们更希望你能思考如何处理离线消息的推送 46 | 47 | ### Bonus 48 | 49 | 1. 请考虑你的聊天系统的性能(例如使用Benchmark测试) 50 | 2. 使用netty自己实现一个websocket 51 | 3. 考虑聊天传输的安全性(可以学习一下Telegram是如何保证传输安全性的,但是现阶段是做不到的,可以尝试做一些小的安全性优化) 52 | 4. 敏感词功能 53 | 5. ip获取+归属地 54 | 6. 未读消息数 55 | 56 | ## 参考 57 | 58 | - `spring-boot-starter-websocket` 59 | - 使用SpringBoot作为基础框架暴露http路由 60 | - [WebSocket 的 6 种集成方式](https://juejin.cn/post/7111132777394733064) 61 | - [SpringBoot如何集成RabbitMQ](https://juejin.cn/post/7155754742113632293) 62 | -------------------------------------------------------------------------------- /docs/6-微服务.md: -------------------------------------------------------------------------------- 1 | # Java 第六轮考核 2 | 3 | ## 任务一 4 | 5 | ### 知识点 6 | 7 | - 掌握微服务架构和Web工作原理 8 | - 掌握HTTP协议和RPC调度方法 9 | - 掌握BASE、CAP理论 10 | 11 | ### 背景 12 | 13 | 由于疫情封校了很久,最近终于开放了,FanOne总算可以和小哥哥们外出Happy了,但FanOne准备出发前一天突然不知道能去哪里,白高兴了,请你写一个基于**微服务架构**的行程规划系统,帮助FanOne制定行程,完成FanOne的约会计划! 14 | 15 | ### 任务 16 | 17 | 你除了需要完成下面模块的开发(每个模块对应一个微服务),你还需要完成这些事情 18 | 19 | 1. 基于Spring Cloud进行开发 20 | 2. Spring Cloud Gateway等网关进行用户身份校验 21 | 3. 使用 Eureka、Nacos 等实现服务注册与发现,使服务可以动态寻找彼此 22 | 4. 学习Spring Cloud原生微服务通信方式 23 | 5. 使用Dubbo或gRPC等方式实现RPC通信请求 24 | 6. 使用docker-compose快速搭建起你的开发环境(例如使用docker-compose启动你的mysql、redis等) 25 | 7. 编写项目文档,其中包括接口、项目结构目录树 26 | 27 | ## **用户管理模块**: 28 | 29 | - 功能:用户注册、登录 30 | 31 | - 数据库表:用户表(Users)。 32 | 33 | ## **行程规划模块**: 34 | 35 | - 功能:用户创建和管理行程。 36 | - 添加行程需要查找目的地,并将对应的目的地加入行程中 37 | - 数据库表:行程表(Itineraries)。 38 | 39 | ## **目的地模块**: 40 | - 功能:浏览目的地信息。 41 | - 数据库表:目的地表(Destinations)。 42 | - 调用关系:行程规划模块调用此模块获取目的地信息 43 | 44 | ### Bonus 45 | 46 | 1. 对项目整合OpenTracing分布式链路追踪系统(例如[Jaeger](https://github.com/jaegertracing/jaeger)、[Skywalking](https://skywalking.apache.org/)) 47 | 48 | 2. 对项目使用服务注册发现(例如 [etcd](https://github.com/etcd-io/etcd) 、[Eureka](https://www.eurekanetwork.org/)、 [ZooKeeper](https://zookeeper.apache.org/)、[Nacos](https://nacos.io/zh-cn/docs/quick-start.html)) 49 | 50 | 3. 学习proto等跨语言rpc调用所用的dlc 51 | 52 | 4. 使用Makefile简化你的protoc生成、项目编译等内容 53 | 54 | 5. 项目支持负载均衡(Load Balance),实现轮询(Round-Robin)策略即可 55 | 56 | 6. 项目中集成熔断降级功能,推荐使用[Hystrix](https://github.com/Netflix/Hystrix) 57 | 58 | 59 | 60 | ## 任务二 61 | 62 | * 中间件设计 63 | * SpringBoot 配置加载、自定义注解、AOP 64 | * starter开发 65 | 66 | 67 | 68 | ### 背景 69 | 70 | 在构建高可用的系统时,限流和防刷是至关重要的安全措施。频率控制不仅可以用于业务功能的保护,还可以确保系统的监控运行正常。由于频率控制是一个通用的需求,在项目中需要在特定接口上进行频率控制。为了简化频率控制的实现,并使其可复用,我们可以将其设计为一个小组件,例如使用注解的形式。 71 | 72 | 73 | 74 | ### 任务 75 | 76 | 请使用SpringBoot + redis实现一个可以对接口进行频率控制的注解,至少需要以下内容: 77 | 78 | **1. starter工程** 79 | 80 | * AutoConfiguration 自动装配类 81 | 82 | * 频率控制注解 83 | 84 | 内部属性: 85 | 86 | * 标识前缀key 87 | * 频控时间范围 88 | * 频控时间单位 89 | * 单位频控时间范围内最大访问次数 90 | * ...... 91 | 92 | * 频率控制切面(对注解切入点进行增强实现频控,标识key赋值(可与IP结合)) 93 | 94 | * spring.factories 95 | 96 | * ...... 97 | 98 | **以上只给出了自定义一个starter最基本的部分,同学们可以根据自己的需求添加内容。** 99 | 100 | **2. test工程**(可以使用任务一的微服务项目) 101 | 102 | * 引入上述自定义starter依赖 103 | 104 | * 多个接口测试用例 105 | 106 | 107 | 108 | ### **Bonus** 109 | 110 | 1. 使用lua脚本保证redis的频率计数的原子性 111 | 2. 允许某个接口拥有多种频控策略(如允许5s内3次、30秒内10次) 112 | 3. 实现核心配置类,允许用户通过配置文件自定义默认频控时间范围、频控时间单位、单位频控时间范围内最大访问次数 113 | 4. 可通过配置文件的参数指定替换限流算法 114 | 5. 实现SPI机制,允许用户自定义实现限流算法 115 | -------------------------------------------------------------------------------- /docs/6-微服务后端合作.md: -------------------------------------------------------------------------------- 1 | # Java 第六轮考核-微服务-分布式(强烈推荐) 2 | 3 | > 此文档为第六轮微服务考核,Java后端合作分布式系统考核,该轮为我们今年新上线的考核,可以适当推迟考核时间。 4 | 5 | ## 目的 6 | 7 | * 理解分布式系统的核心概念 8 | * 提高解决分布式系统中常见问题的能力 9 | * 熟悉微服务的架构设计 10 | * 提高团队协作能力 11 | 12 | ## 任务一 13 | 14 | ### 知识点 15 | 16 | - 掌握微服务架构和Web工作原理 17 | - 掌握HTTP协议和RPC调度方法 18 | - 掌握BASE、CAP理论 19 | 20 | ### 背景 21 | 22 | 最近,FanOne喜欢的小哥哥发布的高数视频被限流了,FanOne希望能有一个视频网站来帮助学长推广视频,增强学长的信心,因为没有相关的技术,FanOne每天都失眠,FanOne的舍友很担心FanOne接下来的四级考试,想起了你们曾经开发的视频网站,希望你们用**微服务架构**重构原来的视频网站,帮助FanOne和小哥哥度过难关 23 | 24 | ### 任务(四到五人合作,选取其中第四轮完成的好的作业,一人重构一到两个微服务,并部署测试) 25 | 26 | 你除了需要完成下面模块的开发(每个模块对应一个微服务),你还需要完成这些事情 27 | 28 | 1. 小组讨论各自完成的微服务 29 | 30 | 2. 基于Spring Cloud进行开发 31 | 32 | 3. 学习服务路由(网关)Spring Cloud Gateway等 33 | 34 | 4. 服务注册发现:Eureka、Nacos、Zookeeper等 35 | 36 | 5. 分布式事务:Seata等 37 | 38 | 6. 学习Spring Cloud原生微服务通信方式 39 | 40 | 7. 使用Dubbo,openFeign,gRPC等方式实现RPC通信请求 41 | 42 | 8. 项目支持负载均衡(Load Balance),实现轮询(Round-Robin)策略即可 43 | 44 | 9. 使用docker-compose快速搭建起你的开发环境(例如使用docker-compose启动你的mysql、redis等) 45 | 46 | 10. 编写项目文档,其中包括接口、项目结构目录树 47 | 48 | | 模块名 | 最低需要完成的接口 | 数量 | 49 | | ------ | -------------------------------------------- | ---- | 50 | | 用户 | 注册、登录、用户信息、上传头像 | 4 | 51 | | 视频 | 投稿、发布列表、搜索视频、热门排行榜 | 4 | 52 | | 互动 | 点赞操作、点赞列表、评论、评论列表、删除评论 | 5 | 53 | | 社交 | 关注操作、关注列表、粉丝列表、好友列表 | 4 | 54 | | 聊天 | 单聊群聊、查询聊天记录 |2~3 | 55 | | 网关 | 请求分发、登录授权 |0| 56 | ## 项目部署 57 | 58 | 首先说明,请开放云服务的安全组,添加IP白名单保证其他同学可以正常使用你部署的接口 59 | 60 | nacos的占用内存会比较大,在限制使用内存大小的情况下服务器还是宕机,我们就需要购买新的大内存服务器,多人合作下,时间安排容易冲突,所以可考虑一起购买一台按量付费或包月8G内存(推荐火山云,新人一个月三四十,五个人就是一个月就几块)的服务器,剩下的白嫖阿里云的2G服务器,参考以下文章 61 | 62 | https://west2-online.feishu.cn/wiki/G1O2wQLYviyyLzkCnPjcBscRnDe 63 | 64 | 我们后期将召开会议,根据各位的学习进度等,来决定是否对nacos和redis,mysql等进行集群部署 65 | 66 | ### 部署建议(下面仅作为参考,可以自己部署) 67 | 68 | 假设我们有六台服务器(Server 1、Server 2、Server 3、Server 4、Server 5、Server 6),以下是一个优化的部署建议: 69 | 70 | #### Server 1(新购买的大内存服务器) 71 | 72 | - **API 网关(Gateway)**:作为所有请求的入口,负责路由和负载均衡。 73 | - **Nacos**:作为服务注册与发现中心。 74 | - **Redis**:作为缓存。 75 | - **Seata**:第二个 Seata 实例,保证分布式事务的高可用性。 76 | - **MySQL**:作为数据库,存储所有系统必须的数据。 77 | 78 | #### Server 2(白嫖的服务器) 79 | 80 | - **用户服务(User Service)**:处理用户相关的逻辑,如注册、登录、用户信息等。 81 | - **用户服务(User Service)**:处理用户相关的逻辑,如注册、登录、用户信息等。 82 | - **用户服务(User Service)**:处理用户相关的逻辑,如注册、登录、用户信息等。 83 | 84 | #### Server 3(白嫖的服务器) 85 | 86 | - **微服务 A**:处理特定业务逻辑。 87 | - **微服务 A**:处理特定业务逻辑。 88 | - **微服务 A**:处理特定业务逻辑。 89 | 90 | #### Server 4(白嫖的服务器) 91 | 92 | - **微服务 B**:处理特定业务逻辑。 93 | - **微服务 B**:处理特定业务逻辑。 94 | - **微服务 B**:处理特定业务逻辑。 95 | 96 | #### Server 5(白嫖的服务器) 97 | 98 | - **微服务 C**:处理特定业务逻辑。 99 | - **微服务 C**:处理特定业务逻辑。 100 | - **微服务 C**:处理特定业务逻辑。 101 | 102 | ## 合作交流 103 | 104 | ### 帮助 105 | 106 | 我们会召开一周一次的大会,了解你们的完成情况,并提供帮助,后期根据人数情况,我们也考虑亲自带你们完成此轮考核 107 | 108 | ### 组长 109 | 110 | 你们需要指定一名队长,队长需要完成总体的规划 111 | 112 | 包括以下任务 113 | 114 | * 技术选型,架构设计 115 | * 网关,用户模块设计 116 | * 数据库设计 117 | 118 | * 组织队内协调 119 | 120 | 组长需要在git上建立仓库,其他成员fork仓库并push自己的代码,组长作为仓库拥有者,负责审核并合并组内队员的push 121 | 122 | ### 组员 123 | 124 | 组员需要编写对应的模块并进行性能分析和编写测试,push代码 125 | 126 | **备注:**遇到无法解决的困难,我们会给你提供帮助 127 | 128 | ### 队内协调 129 | 130 | 1. 首先能决定好一份API文档来保证合作的顺利,文档需要更新请及时在群里通知你的队友 131 | 2. 了解每个同学学习的技术领域,比如不同的组员可能使用不同的mq,选择适合的mq,如果有必要,请教会你的组员该mq的使用方式 132 | 133 | 3. 每隔**三天**,进行一次组内会议,同步进度,解决已知的困难,预期之后一周的目标 134 | 135 | ## 考核 136 | 137 | 负责不同技术领域的组员我们会进行不同程度的提问,组长负责回答整体的方向,为防止出现一人懈怠,整组项目完成度不高而淘汰的情况,我们会对每个组员进行不同的评估,而不是以一个组都无法通过考核的方式进行,不要过分担心自己被分配任务简单而表现欠佳,做好分内的工作就可以通过考核,我们不会无情的pass你 138 | 139 | ## 仔细思考 140 | 141 | ### 网关 142 | 143 | * 为什么使用网关,没有网关会出现什么问题 144 | 145 | ### 服务调用 146 | 147 | * 不同的服务之间应该如何相互调用 148 | 149 | ### 用户信息 150 | 151 | * 思考如何正确的将认证信息传入下游模块 152 | 153 | ### 事务 154 | 155 | * 传统的Spring事务还可以用吗,如何进行技术选型 156 | 157 | ## Bonus 158 | 159 | 1. 对项目整合OpenTracing分布式链路追踪系统(例如[Jaeger](https://github.com/jaegertracing/jaeger)、[Skywalking](https://skywalking.apache.org/)) 160 | 6. 项目中集成熔断降级功能,推荐使用[Hystrix](https://github.com/Netflix/Hystrix) 161 | 162 | 3. 如果在写完这些还有余力,证明你是真正的佬,可以去写单人项目的限流组件,作为加分项 163 | -------------------------------------------------------------------------------- /docs/7-合作项目或底层实现.md: -------------------------------------------------------------------------------- 1 | # Java 第七轮考核 2 | 3 | 这一轮通常是与工作室的其他方向组队开发一款产品,但是也可以选择其他,例如 4 | 5 | - 精读源码并从底层实现一个简单框架 6 | - 参加开源活动,例如开源之夏、GSoC(Google Summer of Code)等 7 | 8 | ## 任务列表 9 | 10 | 以下任务任选一个即可 11 | 12 | ### 合作项目 13 | 14 | 与客户端成员(前端/Android/IOS/其他跨端应用)进行一定体量的项目开发 15 | 16 | > 采用分布式框架进行开发,如SpringBoot + SpringCloud/SpringCloudAlibaba + Dubbo/OpenFeign等 17 | 18 | 需要你有以下部分: 19 | 20 | 1. 服务路由(网关):Spring Cloud Gateway等 21 | 22 | 2. 负载均衡:LoadBalance等 23 | 24 | 3. 服务调用:Dubbo RPC、OpenFeign等 25 | 26 | 4. 服务注册发现:Eureka、Nacos、Zookeeper等 27 | 28 | 5. 流量控制、熔断降级:Hystrix、Sentinel等 29 | 30 | 6. 使用docker-compose等快速搭建起你的开发环境(例如使用docker-compose启动你的mysql、redis等) 31 | 32 | 7. 编写项目文档,其中包括接口、项目结构目录树 33 | 34 | 8. 项目架构关系图 [好用的画图工具Excalidraw ](https://excalidraw.com/) 35 | 36 | 37 | 38 | 基于dubbo/grpc,我们也能够较为轻松地实现跨语言开发的问题(相比大家在合作轮的时候或多或少都会遇到类似的问题) 39 | 40 | ### Tips 41 | 42 | 对于分布式,简单来说(按个人理解),基本上是由一个(或多个)网关层(网关: 可以理解为对外暴露的接口,提供http或其他协议的请求服务),还有许多业务模块(根据需求)组成,基本上模块之间通过某种通信协议进行通信(一般不是http,效率较低(这里可以去了解一下protobuf相关))。 43 | 44 | 跨语言开发指的是,这里的业务模块可以由不同的语言来开发,通过proto等跨语言的DAL,经过序列化之后,以及某种通信协议,可以转换为另一种语言可以接收的形式,从而做到跨语言通信。 45 | 46 | ### 框架实现 47 | 48 | #### 目的 49 | 50 | - 掌握Web底层工作原理 51 | - 掌握Orm库的底层工作原理 52 | - 掌握RPC的基本原理和工作流程 53 | 54 | #### 具体任务 55 | 56 | 下面两个选一个完成即可,当然选择完成实现Spring的话难度更高,自然分数也会更高。 57 | 58 | **注意:**必须进行**功能测试**,甚至打包成starter(答辩时可能需要你运行测试你的框架,请写好测试用例) 59 | 60 | 最后按完成的**功能完整性、合理性、安全性**进行综合评定。 61 | 62 | ##### 实现一个简单的orm Framework 63 | 64 | 该orm框架具备以下功能(可以参考mybatis或jpa等orm框架的设计) 65 | 66 | - 能进行表的结构映射 67 | - 实现简单的增删改查等等api接口 68 | - 支持事务 69 | - ... 70 | 71 | ##### 实现一个简单的Spring Framework 72 | 73 | 对于Java后端开发的同学来说,**Spring框架**已经是事实上的标准,要进一步理解Spring的设计思想,提升自己的架构能力,不如自己动手,从零开始编写一个Spring框架。 74 | 75 | Spring对于面试、工作等过程中也是**重中之重**,所以**强烈建议**以这项任务为目标来完成,并**尽可能**的还原更多的功能。 76 | 77 | - context模块:实现ApplicationContext容器与Bean的管理; 78 | - aop模块:实现AOP功能; 79 | - jdbc模块:实现JdbcTemplate,以及声明式事务管理; 80 | - web模块:实现Web MVC和REST API; 81 | - boot模块:实现一个简化版的“Spring Boot”,用于打包运行。 82 | 83 | 84 | 85 | ### 开源活动 86 | 87 | 我们这里推荐以下站点,可以自行关注以下: 88 | 89 | 1. [开源软件供应链点亮计划 (开源之夏)](https://summer-ospp.ac.cn/) 90 | 2. [Google Summber of Code (gsoc)](https://summerofcode.withgoogle.com/) 91 | 3. [GLCC开源夏令营](https://opensource.alibaba.com/) 92 | 4. [腾讯犀牛鸟开源人才培养计划](https://opensource.tencent.com/summer-of-code) 93 | 94 | 除此之外,可以关注一下一些大厂的开源网站 95 | 96 | 1. [阿里开源](https://opensource.alibaba.com/) 97 | 2. [腾讯开源](https://opensource.tencent.com/) 98 | 3. [Meta Open Source](https://opensource.fb.com/) 99 | 4. [Google Open Source](https://opensource.google/) 100 | 5. [Uber Open Source](https://uber.github.io/#/) 101 | 6. [开源 - 美团技术团队](https://tech.meituan.com/tags/开源.html) 102 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # docs 2 | 3 | 这里存放着我们的考核资料,除了推荐资料外,我们还在每一轮的考核中附赠了一定的资料。这些资料是为减轻你的学习负担而准备的。 4 | 5 | 除此之外,我们希望你可以明确以下几点 6 | 7 | 1. 不要过分追求CURD(增删改查)的实现,除了第三阶段(大作品)外,其他几轮我们的目的是让你熟悉对应的框架/代码/实现。当然,第三轮也并不完全侧重CURD。我们只是希望你有一个对中大型项目的基础认知 8 | 2. 我们从第三阶段(大作品)开始将会设置答辩环节,答辩时间与作品提交时间将会岔开至少1天 9 | 3. 如果有困难,可以联系考核寻求帮助,我们不是冰冷的Bot! 10 | 4. 如果你有时间上的困难,同样可以联系考核,我们会依据具体情况为你作出调整 11 | 12 | ## 关于答辩 13 | 14 | 对于每次答辩,你需要注意下面这些要点 15 | 16 | 1. 不要准备PPT,或者简单的准备一下,我们不会关注你的PPT有多好看 17 | 2. 请对你的代码有拍胸脯的保证,我们会侧重于询问代码实现细节 18 | 3. 答辩不会很长,同时氛围十分轻松 19 | 4. 答辩有时候会设置**考核自问自答**环节,目的是让你了解一些你尚未弄懂/其他细节 20 | 5. 请提前5分钟进入会议室 21 | 6. 我们不会要求你完成所有的内容,如果你没有完成要求,你必须指出你哪些没完成,并且说明原因(包括但不限于:没时间/比较忙/其他原因) 22 | 7. 保持自信! -------------------------------------------------------------------------------- /etc/README.md: -------------------------------------------------------------------------------- 1 | # etc 2 | 3 | 这里搜集了我们平时遇到的,认为比较有用的,或者说在考核群里分享的文章。 4 | 5 | 归类比较复杂,所以类型可能并不完整。 6 | 7 | 大家有想一起分享或者想看的文章可以提pr/issue,有空的话就会去找找资料整理出来嗷。 8 | 9 | ## 目录 10 | 11 | | 文章 | 作者 | 分享人 | 12 | | ------------------------------------------------------------ | ------- | ------ | 13 | | [**阿里巴巴开发规范**](./blog/阿里巴巴Java开发手册.pdf) | Alibaba | 李炎东 | 14 | | [Swagger在线文档使用](./blog/swagger使用.md) | 刘俊辉 | 刘俊辉 | 15 | | [Docker入门教程(更新到docker-compose)](./blog/Docker.md) | 李炎东 | 李炎东 | 16 | | [grpc RPC框架简单使用教程](./blog/gRPC.md) | 李炎东 | 李炎东 | 17 | | [单点登录SSO系统简单讲解](./blog/SSO.md) | 李炎东 | 李炎东 | 18 | | [分布式定时任务入门](./blog/分布式定时任务.md) | 李炎东 | 李炎东 | 19 | | [Typora+PicGo+Gitee搭建博客写作环境](https://cloud.tencent.com/developer/article/1762069) | 三分恶 | 翁鹏 | 20 | | [Mybatis入门教程](./blog/Mybatis.md) | 李炎东 | 李炎东 | 21 | | [RocketMQ入门](./blog/RocketMQ入门.md) | 翁鹏 | 翁鹏 | 22 | | [MySQL](./blog/MySQL.md) | 高毅飞 | 高毅飞 | 23 | | [docker-compose 简易编排部署](./blog/docker-compose简易部署.md) | 高毅飞 | 高毅飞 | 24 | | [Redis](./blog/Redis.md) | 高毅飞 | 高毅飞 | 25 | 26 | -------------------------------------------------------------------------------- /etc/blog/Docker.md: -------------------------------------------------------------------------------- 1 | ## Docker 2 | 3 | ![image-20230627231147837](https://gitee.com/sky-dog/note/raw/master/img/202306272311949.png) 4 | 5 | ## 前言 6 | 7 | * Docker概述 8 | * Docker安装 9 | * Docker命令 10 | * 镜像命令 11 | * 容器命令 12 | * 操作命令 13 | * etc..... 14 | * Docker镜像 15 | * 容器数据卷 16 | * DockerFile 17 | * Docker网络原理 18 | * Idea整合Docker 19 | * Docker Compose(集群) 20 | * Docker Swarm 21 | * CI\CD Jenkins 22 | 23 | 24 | 25 | ## 概述 26 | 27 | ### Docker是什么 28 | 29 | Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的[镜像](https://baike.baidu.com/item/镜像/1574)中,然后发布到任何流行的 [Linux](https://baike.baidu.com/item/Linux)或[Windows](https://baike.baidu.com/item/Windows/165458)操作系统的机器上,也可以实现[虚拟化](https://baike.baidu.com/item/虚拟化/547949)。容器是完全使用[沙箱](https://baike.baidu.com/item/沙箱/393318)机制,相互之间不会有任何接口。Docker 30 | 31 | 32 | 33 | ### Docker为什么出现 34 | 35 | 一款产品以前会有两个环境:开发环境,应用环境 36 | 37 | 由于环境配置不同、版本不同等问题 38 | 39 | 可能会出现换了环境之后无法运行项目的问题 40 | 41 | 而环境配置很麻烦,如果没有Docker,每一台及其都需要部署环境 42 | 43 | Docker:发布项目 (jar + 环境) 44 | 45 | ~~之前服务器被黑了之后重新配置环境真的痛苦,~~有了Docker就不会出现这样的问题 46 | 47 | 如果没有Docker,跨平台 48 | 49 | Windows -> Linux 容易出现问题 50 | 51 | 有了docker: 52 | 53 | java -> jar -> 镜像(带有环境的项目) -> Docker仓库 -> 拉取镜像即可直接运行 54 | 55 | Docker思想:集装箱思想 56 | 57 | 核心思想:**隔离** 58 | 59 | 每个集装箱都是相互隔离的 60 | 61 | 62 | 63 | ### Docker的历史 64 | 65 | 2010年,几个年轻人再美国成立了一家公司dotCloud 66 | 67 | 做一些pass的云计算服务和LXC有关的容器技术 68 | 69 | 他们将容器化技术命名为Docker 70 | 71 | 刚诞生的时候没那么火,后来在Docker开源后,逐渐引起了一些开发者的注意,发现了docker的优点 72 | 73 | 2014.4.9,Docker1.0发布 74 | 75 | docker和vm比起来十分轻巧 76 | 77 | docker容器技术也是一种虚拟化技术 78 | 79 | > 聊聊docker 80 | 81 | Docker是基于Go语言开发的开源项目 82 | 83 | 学习资料: 84 | 85 | * [官网](https://www.docker.com/) 86 | * [超级详细的官方文档](https://docs.docker.com/) 87 | * [Docker Hub 仓库](https://hub.docker.com/) (类似Git) 88 | 89 | 90 | 91 | ### Docker能做啥 92 | 93 | > 对比虚拟机技术 94 | 95 | 缺点: 96 | 97 | * 资源占用多 98 | * 冗余步骤多 99 | * 启动慢 100 | 101 | 102 | 103 | > 容器化技术 104 | 105 | * 容器化技术不是模拟一个完整的操作系统 106 | 107 | * 容器内的应用直接运行在 宿主机 的内核中,容器是没有自己的内核的,也没有硬件,十分轻便 108 | * 每个容器间相互隔离,每个容器内部有一个属于自己的文件系统,互不影响 109 | 110 | 111 | 112 | > DevOps(开发+运维) 113 | 114 | **更快速的交付和部署** 115 | 116 | 传统:一堆帮助文档,安装程序 117 | 118 | Docker:打包镜像发布测试,一键运行 119 | 120 | **更简便的升级和扩缩容** 121 | 122 | 使用Docker后,我们部署应用就和搭积木一样 123 | 124 | **更简单的系统运维** 125 | 126 | 容器化后,我们开发和测试环境高度一致 127 | 128 | 129 | 130 | ## Docker安装 131 | 132 | ### Docker的基本组成 133 | 134 | Docker架构图 135 | 136 | **镜像(image):** 137 | 138 | docker镜像好比一个模板,开源通过这个模板来创建容器服务,tomcat镜像 \=\=\=> run ===> tomcat01容器(提供服务器),通过这个镜像开源创建多个容器 139 | 140 | **容器(container):** 141 | 142 | Docker利用容器技术,独立运行一个或一个组应用,通过镜像来创建。 143 | 144 | 启动,停止,删除,基本命令 145 | 146 | 目前就可以把这个容器理解为一个建议的linux系统 147 | 148 | **仓库(repository):** 149 | 150 | 仓库就是存放镜像的地方 151 | 152 | 仓库分为公有仓库和私有仓库 153 | 154 | Docker Hub是国外的,需要配置镜像加速 155 | 156 | 157 | 158 | ### 安装Docker 159 | 160 | > 环境准备 161 | 162 | 1、需要一点linux基础 163 | 164 | 2、CentOs 7 165 | 166 | 3、使用SSH工具连接远程服务器 167 | 168 | > 环境查看 169 | 170 | ~~~shell 171 | # 系统内核 3.10以上 172 | [root@Lear /]# uname -r 173 | 3.10.0-1062.18.1.el7.x86_64 174 | ~~~ 175 | 176 | ~~~shell 177 | # 系统系统版本 178 | [root@Lear /]# cat /etc/os-release 179 | NAME="CentOS Linux" 180 | VERSION="7 (Core)" 181 | ID="centos" 182 | ID_LIKE="rhel fedora" 183 | VERSION_ID="7" 184 | PRETTY_NAME="CentOS Linux 7 (Core)" 185 | ANSI_COLOR="0;31" 186 | CPE_NAME="cpe:/o:centos:centos:7" 187 | HOME_URL="https://www.centos.org/" 188 | BUG_REPORT_URL="https://bugs.centos.org/" 189 | 190 | CENTOS_MANTISBT_PROJECT="CentOS-7" 191 | CENTOS_MANTISBT_PROJECT_VERSION="7" 192 | REDHAT_SUPPORT_PRODUCT="centos" 193 | REDHAT_SUPPORT_PRODUCT_VERSION="7" 194 | ~~~ 195 | 196 | > 安装 197 | 198 | 帮助文档 199 | 200 | ~~~shell 201 | # 1、卸载 202 | yum remove docker \ 203 | docker-client \ 204 | docker-client-latest \ 205 | docker-common \ 206 | docker-latest \ 207 | docker-latest-logrotate \ 208 | docker-logrotate \ 209 | docker-engine 210 | 211 | # 2、需要的安装包 212 | yum install -y yum-utils 213 | 214 | # 3、设置镜像仓库 (阿里云镜像) 215 | yum-config-manager \ 216 | --add-repo \ 217 | http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 218 | 219 | # 更新yum软件包索引 220 | yum makecache fast 221 | 222 | # 4、安装docker 223 | yum install docker-ce docker-ce-cli containerd.io 224 | 225 | # 5、启动docker 226 | systemctl start docker 227 | # 6、测试是否启动成功 228 | docker version 229 | ~~~ 230 | 231 | ~~~shell 232 | # 7、hello-world 233 | docker run hello-world 234 | ~~~ 235 | 236 | image-20220719193840268 237 | 238 | ~~~shell 239 | # 8、查看一下下载的这个 hello-world镜像 240 | [root@Lear /]# docker images 241 | REPOSITORY TAG IMAGE ID CREATED SIZE 242 | hello-world latest feb5d9fea6a5 9 months ago 13.3kB 243 | ~~~ 244 | 245 | 246 | 247 | 卸载docker 248 | 249 | ~~~shell 250 | # 卸载依赖 251 | yum remove docker-ce docker-ce-cli containerd.io 252 | # 删除资源 253 | rm -rf /var/lib/docker 254 | ~~~ 255 | 256 | 257 | 258 | > 默认工作路径 259 | 260 | /var/lib/docker 261 | 262 | 263 | 264 | ### 阿里云镜像加速 265 | 266 | #### 1、登录阿里云找到`容器镜像服务` 267 | 268 | 269 | 270 | #### 2、找到镜像加速地址 271 | 272 | ![image-20220719194507243](https://gitee.com/sky-dog/note/raw/master/img/202207191945356.png) 273 | 274 | #### 3、配置使用 275 | 276 | ~~~shell 277 | sudo mkdir -p /etc/docker 278 | 279 | sudo tee /etc/docker/daemon.json <<-'EOF' 280 | { 281 | "registry-mirrors": ["https://cikesdgr.mirror.aliyuncs.com"] 282 | } 283 | EOF 284 | 285 | sudo systemctl daemon-reload 286 | 287 | sudo systemctl restart docker 288 | ~~~ 289 | 290 | 291 | 292 | ### 回顾hello-world流程 293 | 294 | > run的运行流程图 295 | 296 | ![image-20220719195046922](https://gitee.com/sky-dog/note/raw/master/img/202207191950028.png) 297 | 298 | 299 | 300 | ### 底层原理 301 | 302 | Docker是一个Client - Server 结构的系统,Docker的守护进程(服务)运行在宿主机上。通过Socket从客户端访问 303 | 304 | DockerServer接受到Docker-Client的指令,就会执行这个命令 305 | 306 | ![image-20220719200703298](https://gitee.com/sky-dog/note/raw/master/img/202207192007376.png) 307 | 308 | > Docker为什么比VM快 309 | 310 | 1、Docker有比虚拟机更少的抽象层 311 | 312 | 2、Docker 利用的是宿主机的内核,VM需要 Guest OS 313 | 314 | image-20220719200749619 315 | 316 | 新建容器时,Docker不需要像VM一样重新加载一个操作系统 317 | 318 | image-20220719201006555 319 | 320 | 321 | 322 | 323 | 324 | ## Docker 常用命令 325 | 326 | ### 帮助命令 327 | 328 | ~~~shell 329 | docker version # 显示docker版本信息 330 | docker info # 显示docker的系统信息(包括镜像和容器信息) 331 | docker 命令 --help # 万能命令 332 | ~~~ 333 | 334 | 帮助文档:[Docker run reference | Docker Documentation](https://docs.docker.com/engine/reference/run/) 335 | 336 | 337 | 338 | ### 镜像命令 339 | 340 | **docker images** 查看所有本地主机上的镜像 341 | 342 | ~~~shell 343 | [root@Lear /]# docker images 344 | REPOSITORY TAG IMAGE ID CREATED SIZE 345 | hello-world latest feb5d9fea6a5 9 months ago 13.3kB 346 | 347 | # 名词解释 348 | REPOSITORY 镜像的仓库源 349 | TAG 镜像的标签 350 | IAMGE ID 镜像的ID 351 | CREATED 镜像的创建时间 352 | SIZE 镜像的大小 353 | 354 | # 可选参数 355 | -a, --all # 列出所有镜像 356 | -q, --quite # 只显示镜像的id 357 | ~~~ 358 | 359 | 360 | 361 | **docker search** 搜索镜像 362 | 363 | ~~~shell 364 | [root@Lear /]# docker search mysql 365 | NAME DESCRIPTION STARS OFFICIAL AUTOMATED 366 | mysql MySQL is a widely used, open-source relation… 12891 [OK] 367 | mariadb MariaDB Server is a high performing open sou… 4943 [OK] 368 | percona Percona Server is a fork of the MySQL relati… 582 [OK] 369 | phpmyadmin phpMyAdmin - A web interface for MySQL and M… 577 [OK] 370 | bitnami/mysql Bitnami MySQL Docker Image 72 [OK] 371 | 372 | # 可选参数 373 | --filter=STARS=3000 # 搜索出STARS大于3000的镜像 374 | 375 | ~~~ 376 | 377 | 378 | 379 | **docker pull** 下载镜像 380 | 381 | ~~~shell 382 | # 下载镜像 docker pull 镜像名[:tag] 383 | [root@Lear /]# docker pull mysql 384 | Using default tag: latest # 不写tag默认使用latest 385 | latest: Pulling from library/mysql 386 | 72a69066d2fe: Pull complete # 分层下载,docker image的核心 联合文件系统 387 | 93619dbc5b36: Pull complete 388 | 99da31dd6142: Pull complete 389 | 626033c43d70: Pull complete 390 | 37d5d7efb64e: Pull complete 391 | ac563158d721: Pull complete 392 | d2ba16033dad: Pull complete 393 | 688ba7d5c01a: Pull complete 394 | 00e060b6d11d: Pull complete 395 | 1c04857f594f: Pull complete 396 | 4d7cfa90e6ea: Pull complete 397 | e0431212d27d: Pull complete 398 | Digest: sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709 # 签名信息 399 | Status: Downloaded newer image for mysql:latest 400 | docker.io/library/mysql:latest # 真实地址 401 | 402 | # 拉取指定版本镜像 403 | [root@Lear /]# docker pull mysql:5.7 404 | 5.7: Pulling from library/mysql 405 | 72a69066d2fe: Already exists 406 | 93619dbc5b36: Already exists 407 | 99da31dd6142: Already exists 408 | 626033c43d70: Already exists 409 | 37d5d7efb64e: Already exists 410 | ac563158d721: Already exists 411 | d2ba16033dad: Already exists 412 | 0ceb82207cd7: Pull complete 413 | 37f2405cae96: Pull complete 414 | e2482e017e53: Pull complete 415 | 70deed891d42: Pull complete 416 | Digest: sha256:f2ad209efe9c67104167fc609cca6973c8422939491c9345270175a300419f94 417 | Status: Downloaded newer image for mysql:5.7 418 | docker.io/library/mysql:5.7 419 | ~~~ 420 | 421 | 422 | 423 | **docker rmi** 删除镜像 424 | 425 | ~~~shell 426 | [root@Lear /]# docker rmi -f 容器id [容器id 容器id 容器id .....] #删除指定镜像 427 | [root@Lear /]# docker rmi -f $(docker images -aq) #删除所有镜像 428 | ~~~ 429 | 430 | 431 | 432 | 433 | 434 | ### 容器命令 435 | 436 | tips:有了镜像才可以创建容器 437 | 438 | > 准备工作 439 | 440 | 下载个centos来测试学习 441 | 442 | ~~~shell 443 | docker pull centos 444 | ~~~ 445 | 446 | 新建容器并启动 447 | 448 | ~~~shell 449 | docker run [可选参数] image 450 | 451 | # 参数说明 452 | --name="Name" # 容器名字 453 | -d # 以后台方式运行 454 | -it # 使用交互方式运行(可进入容器查看) 455 | -p # 指定容器端口 -p 8080:8080 456 | -p ip:主机端口:容器端口 457 | -p 主机端口:容器端口 # 常用! 458 | -p 容器端口 459 | 容器端口 460 | -P # 随机指定端口 461 | 462 | # 测试 启动并进入容器 463 | [root@Lear /]# docker run -it centos /bin/bash 464 | [root@73efe585932d /]# ls # 查看容器内的centos,很多版本,内部命令不完善 465 | bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var 466 | 467 | # 从容器中退回主机 468 | [root@73efe585932d /]# exit 469 | exit 470 | [root@Lear /]# 471 | ~~~ 472 | 473 | **列出所有运行中的容器** 474 | 475 | ~~~shell 476 | # docker ps 命令 477 | -a # 列出当前正在运行的容器+历史运行过的容器 478 | -n=? # 显示最近创建的容器(显示个数) 479 | -q # 只显示容器id 480 | 481 | [root@Lear /]# docker ps 482 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 483 | [root@Lear /]# docker ps -a 484 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 485 | 73efe585932d centos "/bin/bash" 2 minutes ago Exited (0) 45 seconds ago magical_neumann 486 | 963913bef5b5 feb5d9fea6a5 "/hello" 20 hours ago Exited (0) 20 hours ago vigorous_elbakyan 487 | 488 | ~~~ 489 | 490 | **退出容器** 491 | 492 | ~~~shell 493 | exit # 停止容器并推出 494 | ctrl + P + Q # 退出但容器不停止运行 495 | ~~~ 496 | 497 | **删除容器** 498 | 499 | ~~~shell 500 | docker rm 容器id # 删除指定容器 501 | docker rm -f $(docker ps -aq) # 删除所有容器 502 | docker ps -a -q|xargs docker rm # 删除所有的容器 503 | ~~~ 504 | 505 | 506 | 507 | **启动和停止容器** 508 | 509 | ~~~shell 510 | docker start 容器id # 启动容器 511 | docker restart 容器id # 重启容器 512 | docker stop 容器id # 停止当前正在运行的容器 513 | docker kill 容器id # 强制停止当前容器 514 | ~~~ 515 | 516 | 517 | 518 | ### 常用其他命令 519 | 520 | **后台启动容器** 521 | 522 | ~~~shell 523 | # 命令 docker run -d 容器名 524 | 525 | # 问题:docker ps 发现容器已经停止了 526 | 527 | # 常见的坑:docker发现没有应用,就会自动停止 528 | # nginx: 容器启动后,发现自己没有提供服务,就会立刻停止 529 | ~~~ 530 | 531 | **查看日志** 532 | 533 | ~~~shell 534 | docker logs -f -t --tail 显示行数 容器id 535 | ~~~ 536 | 537 | **查看容器进程信息** 538 | 539 | ~~~shell 540 | docker top 容器id 541 | ~~~ 542 | 543 | **查看镜像元数据** 544 | 545 | ~~~shell 546 | docker inspect 容器id 547 | ~~~ 548 | 549 | 550 | 551 | **进入正在运行的容器!** 552 | 553 | ~~~shell 554 | docker exec -it 容器id /bin/bash 555 | 进入容器后开启一个新的终端 556 | 557 | docker attach 容器id /bin/bash 558 | 进入正在执行的命令行 559 | ~~~ 560 | 561 | **从容器拷贝文件** 562 | 563 | ~~~shell 564 | docker cp 容器id:容器内路径 目的主机路径 565 | ~~~ 566 | 567 | 568 | 569 | ### 小结 570 | 571 | ![image-20220720162815150](https://gitee.com/sky-dog/note/raw/master/img/202207201628318.png) 572 | 573 | 574 | 575 | ### 练习 576 | 577 | > 部署nginx 578 | 579 | ~~~shell 580 | 581 | # 1、搜索nginx镜像 582 | docker search nginx 583 | # 或去dockerhub上搜索(建议) 584 | # 2、拉去nginx镜像 585 | docker pull nginx 586 | docker images # 查看镜像 587 | # 3、启动容器 588 | docker run -d --name nginx01 -p 8000:80 # 后台启动、容器命名为nginx01、外网访问8000映射到容器内网80 589 | 590 | ~~~ 591 | 592 | 端口映射图解 593 | 594 | image-20220723161641406 595 | 596 | 597 | 598 | 思考: 599 | 600 | 如何在宿主机修改容器内部配置、启动应用等? 601 | 602 | 603 | 604 | 605 | 606 | ## Docker镜像详解 607 | 608 | ### 镜像是什么 609 | 610 | 镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量、配置文件。 611 | 612 | 所有的应用直接打包为镜像,即可直接跑起来 613 | 614 | 如何获取镜像: 615 | 616 | * 远程仓库拉取 617 | * 朋友拷贝来 618 | * 自己制作DockerFile 619 | 620 | 621 | 622 | ### Docker镜像加载原理 623 | 624 | > UnionFS(联合文件系统) 625 | 626 | 联合文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。联合文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。 627 | 特性:一次同时加载多个文件系统,但从外面看起来只能看到一个文件系统。联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。 628 | 629 | ![image-20220723172606514](https://gitee.com/sky-dog/note/raw/master/img/202207231747751.png) 630 | 631 | > 镜像加载原理 632 | 633 | Docker的镜像实际由一层一层的文件系统组成: 634 | 635 | bootfs(boot file system)主要包含bootloader和kernel。bootloader主要是引导加载kernel,完成后整个内核就都在内存中了。此时内存的使用权已由bootfs转交给内核,系统卸载bootfs。可以被不同的Linux发行版公用。 636 | rootfs(root file system),包含典型Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。rootfs就是各种不同操作系统发行版(Ubuntu,Centos等)。因为底层直接用Host的kernel,rootfs只包含最基本的命令,工具和程序就可以了。 637 | 638 | ### 分层理解 639 | 640 | 所有的Docker镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的容器层。 641 | 容器在启动时会在镜像最外层上建立一层可读写的容器层(R/W),而镜像层是只读的(R/O)。 642 | 643 | ![image-20220723174705516](https://gitee.com/sky-dog/note/raw/master/img/202207231747588.png) 644 | 645 | ![image-20220723173708823](https://gitee.com/sky-dog/note/raw/master/img/202207231737928.png) 646 | 647 | 648 | 649 | ### commit镜像 650 | 651 | ~~~shell 652 | docker commit # 提交容器为一个新的副本 653 | # 和git类似 654 | docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名:[TAG] 655 | ~~~ 656 | 657 | 可以将自己diy的容器打包为一个镜像 658 | 659 | 660 | 661 | 662 | 663 | ### 练习 664 | 665 | 在本地制作一个centos+jdk镜像 666 | 667 | ~~(自己做个jdk17的镜像用来开我的世界服务器)~~ 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | ## 容器数据卷 676 | 677 | ### 什么是容器数据卷 678 | 679 | 如果数据都在容器中,删除容器 -> 数据丢失 680 | 681 | ==需求:数据持久化== 682 | 683 | 容器数据卷:即一种容器间数据共享技术 684 | 685 | 简单来说,就是将容器内的目录挂载在宿主机上 686 | 687 | 也就是容器的持久化和同步操作,容器间也可以数据共享 688 | 689 | image-20220724124255538 690 | 691 | 692 | 693 | ### 使用数据卷 694 | 695 | > 直接使用命令挂载 -v 696 | 697 | ~~~shell 698 | docker run -it -v 主机目录:容器内目录 699 | 700 | # 测试 701 | docker run -it -v /home/ceshi:/home centos /bin/bash 702 | # 查看是否挂载成功 703 | docker inspect 容器id 704 | ~~~ 705 | 706 | 707 | 708 | ### 具名挂载/匿名挂载 709 | 710 | ~~~shell 711 | # 匿名挂载 712 | -v 容器内路径 713 | docker run -d -P --name nginx01 -v /etc/nginx nginx 714 | 715 | # 查看所有的 volume 情况 716 | docker volume ls 717 | 718 | # 具名挂载 719 | docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx 720 | 721 | ~~~ 722 | 723 | 所有的docker容器内的卷,在没有指定目录的情况下都是在`/var/lib/docker/volumes/xxxx/_data` 724 | 725 | 通过具名挂载可以方便的找到一个卷,大多数使用**具名挂载** 726 | 727 | ~~~shell 728 | # 如何确定是具名挂载还是匿名挂载,还是指定路径挂载 729 | 730 | ~~~ 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | ### 练习:Mysql同步数据 739 | 740 | ~~~shell 741 | # 获取镜像 742 | docker pull mysql:5.7 743 | 744 | # 运行容器(需要数据挂载) 745 | # 启动mysql需要配置密码! 746 | # 官方测试: docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag 747 | 748 | # 启动参数 749 | -d 后台运行 750 | -p 端口映射 751 | -v 卷挂载 752 | -e 环境配置 753 | --name 容器名 754 | docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7 755 | ~~~ 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | ## DockerFIle 770 | 771 | ### DockerFile介绍 772 | 773 | DockerFIle 就是构建 docker 镜像的构建文件!本质是一个命令脚本,用于生成镜像。 774 | 775 | 776 | 777 | 构建步骤: 778 | 779 | * 编写一个 dockerfile 文件 780 | * docker build 构建成为一个镜像 781 | * docker run 运行镜像 782 | * docker push 发布镜像 (DockerHub、阿里云镜像仓库) 783 | 784 | 785 | 786 | 查看centos官方镜像学习 787 | 788 | image-20220728160841386 789 | 790 | dockerhub上的镜像90%都源自 scratch 791 | 792 | image-20220728160909567 793 | 794 | 官方镜像都是基础包,缺少很多功能,我们通常构建自己的镜像使用 795 | 796 | ### DockerFile构建过程 797 | 798 | **基础知识:** 799 | 800 | 1、每个保留关键字(指令)都必须是大写字母 801 | 802 | 2、从上到下顺序执行 803 | 804 | 3、`#`表示注释 805 | 806 | 4、每一个指令都会创建提交一个新的镜像层并提交 807 | 808 | image-20220728161639633 809 | 810 | dockerfile是面向开发的,以后要发布项目,做镜像,就需要编写dockerfile 811 | 812 | docker镜像逐渐成为企业交付的标准,必须要掌握 813 | 814 | 步骤:开发、部署、上线、运维。。。 815 | 816 | DockerFile : 构建文件,定义了一切的步骤,源代码 817 | 818 | DockerImages : 通过 DockerFile 构建生成的镜像 (最终要发布和运行的产品) 819 | 820 | Docker容器 : 容器就是镜像运行起来提供服务的 821 | 822 | 823 | 824 | ### DockerFile指令 825 | 826 | ~~~shell 827 | FROM # 基础镜像 (一切从这里开始构建) (一般用centos) 828 | MAINTAINER # 镜像是谁写的 (姓名+邮箱) 829 | RUN # 镜像构建时需要运行的命令 830 | ADD # 步骤:添加内容(比如说jdk压缩包) 831 | WORKDIR # 镜像工作目录 832 | VOLUME # 挂载目录 833 | EXPOSE # 暴露端口 834 | CMD # 指定容器启动时运行的命令 (只有最后一个会生效,可被替代) 835 | ENTRYPOINT # 指定容器启动时运行的命令 (可以追加命令) 836 | ONBUILD # 当构建一个被继承 DockerFile 这个时候会执行 ONBUILD 的命令 837 | COPY # 类似ADD命令 将文件拷贝到镜像中 838 | ENV # 构建时设置环境变量 839 | 840 | ~~~ 841 | 842 | ![image-20220728162227803](https://gitee.com/sky-dog/note/raw/master/img/202207281622971.png) 843 | 844 | 845 | 846 | ### 实战测试 847 | 848 | > 自己的centos 849 | 850 | ~~~shell 851 | # 1、编写配置文件 852 | 853 | FROM centos 854 | MAINTAINER lear<362664609@qq.com> 855 | 856 | ENV MYPATH /usr/local 857 | WORKDIR $MYPATH 858 | 859 | RUN yum -y install vim 860 | RUN yum -y install net-tools 861 | 862 | EXPOSE 80 863 | 864 | CMD echo $MYPATH 865 | CMD echo "---build end---" 866 | CMD /bin/bash 867 | 868 | 869 | # 2、通过dockerfile文件构建镜像 870 | # 命令 docker build -f dockerfile文件路径 -t 镜像名:[tag] . 871 | 872 | ~~~ 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | ### 使用DockerFile 挂载目录 886 | 887 | ~~~dockerfile 888 | # 创建一个dockerfile文件 名字可以随机(建议dockerfile) 889 | # 注意:文件中指令均为大写 890 | FROM centos 891 | 892 | VOLUME ["volume01","volume02"] # 匿名挂载 893 | 894 | CMD echo "---end---" 895 | CMD /bin/bash 896 | ~~~ 897 | 898 | ~~~shell 899 | docker build -f dockerfile1 -t myCentos . 900 | ~~~ 901 | 902 | 903 | 904 | ![image-20220727190103012](https://gitee.com/sky-dog/note/raw/master/img/202207271901219.png) 905 | 906 | 907 | 908 | ~~~shell 909 | docker run -it --name docker02 --volume-from docker01 kuangshen/centos:1.0 910 | # --volume-from 用于继承另一个容器的挂载属性 911 | # 即将目录挂载到宿主机的同一个路径下 912 | ~~~ 913 | 914 | 915 | 916 | ### 发布自己的镜像 917 | 918 | > [Docker Hub](https://hub.docker.com/) 919 | 920 | 0. 注册 921 | 922 | 1. 登录 923 | 924 | 2. 在服务器上提交镜像 925 | 926 | ~~~shell 927 | [root@Lear ~]# docker login --help 928 | 929 | Usage: docker login [OPTIONS] [SERVER] 930 | 931 | Log in to a Docker registry. 932 | If no server is specified, the default is defined by the daemon. 933 | 934 | Options: 935 | -p, --password string Password 936 | --password-stdin Take the password from stdin 937 | -u, --username string Username 938 | ~~~ 939 | 940 | 941 | 942 | 3. 登录后, docker push 943 | 944 | ![image-20220731111307307](https://gitee.com/sky-dog/note/raw/master/img/202207311113440.png) 945 | 946 | 分层push提交 947 | 948 | 949 | 950 | > 阿里云镜像提交 951 | 952 | 1. 登录阿里云 953 | 954 | 2. 容器镜像服务 955 | 956 | 3. 创建命名空间 957 | 958 | ![image-20220731112211413](https://gitee.com/sky-dog/note/raw/master/img/202207311122494.png) 959 | 960 | 4. 创建容器镜像 961 | 962 | image-20220731112302887 963 | 964 | image-20220731112405998 965 | 966 | 5. 阿里云官方文档说明 967 | 968 | ![image-20220731112805083](https://gitee.com/sky-dog/note/raw/master/img/202207311128214.png) 969 | 970 | ### 小结 971 | 972 | 973 | 974 | image-20220731113115201 975 | 976 | 977 | 978 | ## Docker 网络 979 | 980 | ### 理解 Docker0 981 | 982 | ![image-20220731115345610](https://gitee.com/sky-dog/note/raw/master/img/202207311153670.png) 983 | 984 | 985 | 986 | ~~~shell 987 | # 运行tomcat容器测试 988 | docker run -d -P --name tomcat01 tomcat 989 | 990 | # 先给容器安装网络相关包 991 | apt update && apt install -y iproute2 992 | apt-get install inetutils-ping 993 | # 再使用ip addr查看容器内网络信息 994 | [root@Lear /]# docker exec -it tomcat01 ip addr 995 | 1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 996 | link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 997 | inet 127.0.0.1/8 scope host lo 998 | valid_lft forever preferred_lft forever 999 | 10: eth0@if11: mtu 1500 qdisc noqueue state UP group default 1000 | link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 1001 | inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 1002 | valid_lft forever preferred_lft forever 1003 | 1004 | # 使用宿主机测试ping容器 1005 | [root@Lear /]# ping 172.17.0.2 1006 | PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data. 1007 | 64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.058 ms 1008 | 64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.071 ms 1009 | 64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.074 ms 1010 | 1011 | # 通过linux服务器可以 ping 通容器内部 1012 | ~~~ 1013 | 1014 | > 原理 1015 | 1016 | 1、我们每启动一个docker容器,docker就会给docker容器分配一个ip,我们只要安装了docker,就会有一个网卡 docker0 1017 | 1018 | 桥接模式,使用技术:evth-pair 1019 | 1020 | 1021 | 1022 | 在宿主机中再次使用 ip addr 查看,发现多了一对网卡 (11: veth3908f46@if10) 1023 | 1024 | ![image-20220731132551565](https://gitee.com/sky-dog/note/raw/master/img/202207311325642.png) 1025 | 1026 | ~~~shell 1027 | # 发现容器带来的网卡是一对一对的 1028 | # evth-pair 就是一对虚拟设备接口,成对出现,一端连着协议,一端彼此相连 1029 | # 正因为这个特性,将 evth-pair 作为桥梁,连接各种虚拟网络设备 1030 | # OpenStac , Docker 容器间连接 , OVS连接 , 都是使用 evth-pair 技术 1031 | 1032 | 容器间可以相互ping通 1033 | 1034 | ~~~ 1035 | 1036 | 1037 | 1038 | image-20220731150512889 1039 | 1040 | image-20220731151039267 1041 | 1042 | 1043 | 1044 | Docker 使用Linux的桥接,宿主机中是一个Docker容器的网桥,即Docker0 1045 | 1046 | Docker中所有的网络接口都是虚拟的。虚拟的转发效率高! 1047 | 1048 | 只要停止或删除容器,对应 veth-pair 就没了 1049 | 1050 | image-20220731214642516 1051 | 1052 | 1053 | 1054 | ### --link 1055 | 1056 | 每次启动容器 ip 地址都会发生变化 1057 | 1058 | 如何通过容器名来访问容器网络 1059 | 1060 | ~~~shell 1061 | # 无法通过容器名直接访问 1062 | [root@Lear rollup]# docker exec -it tomcat02 ping tomcat01 1063 | ping: unknown host 1064 | ~~~ 1065 | 1066 | ~~~shell 1067 | # 测试 --link 1068 | [root@Lear rollup]# docker run -d -P --name tomcat03 --link tomcat02 tomcat 1069 | 1070 | # 测试ping 1071 | # 正向可以ping通 1072 | [root@Lear rollup]# docker exec -it tomcat03 ping tomcat02 1073 | PING tomcat02 (172.17.0.3) 56(84) bytes of data. 1074 | 64 bytes from tomcat02 (172.17.0.3): icmp_seq=1 ttl=64 time=0.123 ms 1075 | 64 bytes from tomcat02 (172.17.0.3): icmp_seq=2 ttl=64 time=0.093 ms 1076 | 64 bytes from tomcat02 (172.17.0.3): icmp_seq=3 ttl=64 time=0.094 ms 1077 | # 反向无法ping通 1078 | [root@Lear rollup]# docker exec -it tomcat02 ping tomcat03 1079 | ping: unknown host 1080 | ~~~ 1081 | 1082 | ~~~shell 1083 | # 查看网络 1084 | [root@Lear rollup]# docker network ls 1085 | NETWORK ID NAME DRIVER SCOPE 1086 | d4469cc409f7 bridge bridge local 1087 | e5a5ec26c3f0 host host local 1088 | 02d693215add none null local 1089 | [root@Lear rollup]# docker network inspect d4469cc409f7 1090 | ~~~ 1091 | 1092 | ![image-20220731221556159](https://gitee.com/sky-dog/note/raw/master/img/202207312215266.png) 1093 | 1094 | 原理 1095 | 1096 | 使用 --link 时,在hosts文件中配置了域名解析,直接将tomcat02解析为对应的ip地址 1097 | 1098 | 能够使用 容器名 或 容器id,来访问容器网络 1099 | 1100 | ~~~shell 1101 | [root@Lear rollup]# docker exec -it tomcat03 cat /etc/hosts 1102 | 127.0.0.1 localhost 1103 | ::1 localhost ip6-localhost ip6-loopback 1104 | fe00::0 ip6-localnet 1105 | ff00::0 ip6-mcastprefix 1106 | ff02::1 ip6-allnodes 1107 | ff02::2 ip6-allrouters 1108 | 172.17.0.3 tomcat02 cf77a4d0a845 1109 | 172.17.0.4 b41942f2cf97 1110 | ~~~ 1111 | 1112 | 1113 | 1114 | 现在玩 Docker 已经不建议使用 --link 了 1115 | 1116 | docker0 问题 : 无法使用容器名访问 1117 | 1118 | 我们可以使用自定义网络 1119 | 1120 | 1121 | 1122 | ### 自定义网络 1123 | 1124 | > 查看docker所有网络 1125 | 1126 | ![image-20220801142024347](https://gitee.com/sky-dog/note/raw/master/img/202208011420461.png) 1127 | 1128 | **网络模式** 1129 | 1130 | bridge : 桥接 docker0 (默认) 1131 | 1132 | none : 不配置网络 1133 | 1134 | host : 与宿主机共享网络 1135 | 1136 | container : 容器网络联通 (少用,局限性大) 1137 | 1138 | 1139 | 1140 | **测试** 1141 | 1142 | ~~~shell 1143 | # 我们直接启动的命令 --net bridge 就是docker0 1144 | docker run -d -P --name tomcat01 [-net bridge] tomcat 1145 | 1146 | # docker0 特点: 域名不可访问(只能使用--link) 1147 | 1148 | # 自定义一个网络 1149 | # --driver bridge 1150 | # -subnet 192.168.0.0/16 网络支持范围: 192.168.0.2 ~ 192.168.255.255 1151 | # --gateway 192.168.0.1 路由网关 1152 | [root@Lear /]# docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet 1153 | d7f0ea06cca6a0e19a9e51344deb7c51a66553703be47686d65c346d35ef19f3 1154 | [root@Lear /]# docker network ls 1155 | NETWORK ID NAME DRIVER SCOPE 1156 | d4469cc409f7 bridge bridge local 1157 | e5a5ec26c3f0 host host local 1158 | d7f0ea06cca6 mynet bridge local 1159 | 02d693215add none null local 1160 | ~~~ 1161 | 1162 | image-20220803141230732 1163 | 1164 | ~~~shell 1165 | # 启动容器测试 1166 | 9e512bd57a2851d815f8fadee0e0ce197505e397621e3b6c4cc044da6d151868 1167 | [root@Lear /]# docker run -d -P --name tomcat-net-02 --net mynet tomcat 1168 | 709ca79a1764e35cdcbbf7226d7b968ae93b96825785c87fbd4896bfb7598dc8 1169 | [root@Lear /]# docker network inspect mynet 1170 | [ 1171 | { 1172 | "Name": "mynet", 1173 | "Id": "d7f0ea06cca6a0e19a9e51344deb7c51a66553703be47686d65c346d35ef19f3", 1174 | "Created": "2022-08-03T14:09:33.135986794+08:00", 1175 | "Scope": "local", 1176 | "Driver": "bridge", 1177 | "EnableIPv6": false, 1178 | "IPAM": { 1179 | "Driver": "default", 1180 | "Options": {}, 1181 | "Config": [ 1182 | { 1183 | "Subnet": "192.168.0.0/16", 1184 | "Gateway": "192.168.0.1" 1185 | } 1186 | ] 1187 | }, 1188 | "Internal": false, 1189 | "Attachable": false, 1190 | "Ingress": false, 1191 | "ConfigFrom": { 1192 | "Network": "" 1193 | }, 1194 | "ConfigOnly": false, 1195 | "Containers": { 1196 | "709ca79a1764e35cdcbbf7226d7b968ae93b96825785c87fbd4896bfb7598dc8": { 1197 | "Name": "tomcat-net-02", 1198 | "EndpointID": "0d2cf16c67b4104cd6c874602577b7abde9cfec21099632d512909798d0f5a02", 1199 | "MacAddress": "02:42:c0:a8:00:03", 1200 | "IPv4Address": "192.168.0.3/16", 1201 | "IPv6Address": "" 1202 | }, 1203 | "9e512bd57a2851d815f8fadee0e0ce197505e397621e3b6c4cc044da6d151868": { 1204 | "Name": "tomcat-net-01", 1205 | "EndpointID": "16a8e57b686989075a06588409571335c2763b887b4712897a83cca6cdf917ca", 1206 | "MacAddress": "02:42:c0:a8:00:02", 1207 | "IPv4Address": "192.168.0.2/16", 1208 | "IPv6Address": "" 1209 | } 1210 | }, 1211 | "Options": {}, 1212 | "Labels": {} 1213 | } 1214 | ] 1215 | ~~~ 1216 | 1217 | ~~~shell 1218 | # 测试ping通 1219 | [root@Lear /]# docker exec -it tomcat-net-01 ping 192.168.03 1220 | PING 192.168.03 (192.168.0.3) 56(84) bytes of data. 1221 | 64 bytes from 192.168.0.3: icmp_seq=1 ttl=64 time=0.122 ms 1222 | 64 bytes from 192.168.0.3: icmp_seq=2 ttl=64 time=0.097 ms 1223 | 64 bytes from 192.168.0.3: icmp_seq=3 ttl=64 time=0.090 ms 1224 | ^C 1225 | --- 192.168.03 ping statistics --- 1226 | 3 packets transmitted, 3 received, 0% packet loss, time 2000ms 1227 | rtt min/avg/max/mdev = 0.090/0.103/0.122/0.013 ms 1228 | [root@Lear /]# docker exec -it tomcat-net-01 ping tomcat-net-02 1229 | PING tomcat-net-02 (192.168.0.3) 56(84) bytes of data. 1230 | 64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.075 ms 1231 | 64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=2 ttl=64 time=0.098 ms 1232 | 64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=3 ttl=64 time=0.064 ms 1233 | ^C 1234 | --- tomcat-net-02 ping statistics --- 1235 | ~~~ 1236 | 1237 | **推荐这么使用网络** 1238 | 1239 | 自定义的网络docker已经帮我们维护好了对应关系 1240 | 1241 | 1242 | 1243 | 好处: 1244 | 1245 | * redis - 不同集群使用不同网络,保证集群安全和健康 1246 | * mysql- 不同集群使用不同网络,保证集群安全和健康 1247 | 1248 | ![image-20220803181719805](https://gitee.com/sky-dog/note/raw/master/img/202208031817064.png) 1249 | 1250 | ### 网络连通 1251 | 1252 | ![image-20220803182135746](https://gitee.com/sky-dog/note/raw/master/img/202208031821854.png) 1253 | 1254 | ![image-20220809180730300](https://gitee.com/sky-dog/note/raw/master/img/202208091807498.png) 1255 | 1256 | ~~~shell 1257 | docker network connect mynet tomcat01 1258 | # 直接将tomcat的网络配置进mynet了 1259 | # 一个容器 两个ip 1260 | ~~~ 1261 | 1262 | 1263 | 1264 | ### 实战 1265 | 1266 | > redis 集群 1267 | 1268 | ![image-20220809190050918](https://gitee.com/sky-dog/note/raw/master/img/202208091900106.png) 1269 | 1270 | 1271 | 1272 | ~~~shell 1273 | # 创建网卡 1274 | docker network create redis --subnet 172.38.0.0/16 1275 | 1276 | # 通过脚本创建六个redis配置 1277 | for port in $(seq 1 6); \ 1278 | do \ 1279 | mkdir -p /mydata/redis/node-${port}/conf 1280 | touch /mydata/redis/node-${port}/conf/redisonf 1281 | cat << EOF >/mydata/redis/node-${port}/conf/redisconf 1282 | port 6379 1283 | bind 0.0.0.0 1284 | cluster-enabled yes 1285 | cluster-config-file nodes.conf 1286 | cluster-node-timeout 5000 1287 | cluster-announce-ip 172.38.0.1${port} 1288 | cluster-announce-port 6379 1289 | cluster-announce-bus-port 16379 1290 | appendonly yes 1291 | EOF 1292 | done 1293 | 1294 | # 启动六个redis 1295 | for port in $(seq 1 6); \ 1296 | do \ 1297 | docker run -p 637${port}:6379 -p 1637${port}:16379 --name redis-${port} \ 1298 | -v /mydata/redis/node-${port}/data:/data \ 1299 | -v /mydata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \ 1300 | -d --net redis --ip 172.38.0.1${port} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf; \ 1301 | done 1302 | 1303 | # 例:启动一个redis 1304 | docker run -p 6371:6379 -p 16371:16379 --name redis-1 \ 1305 | -v /mydata/redis/node-1/data:/data \ 1306 | -v /mydata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf \ 1307 | -d --net redis --ip 172.38.0.11 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf; \ 1308 | 1309 | # 进入其中一个容器 1310 | # ps:redis镜像中没有/bin/bash 1311 | docker exec -it redis-1 /bin/sh 1312 | 1313 | # 创建集群 1314 | redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1 1315 | 1316 | # 测试集群 1317 | redis-cli -c 1318 | cluster info 1319 | ~~~ 1320 | 1321 | 1322 | 1323 | > SpringBoot 微服务打包镜像 1324 | 1325 | 1. 构建SpringBoot项目 1326 | 2. 打包应用 1327 | 3. 编写dockerfile 1328 | 4. 构建镜像 1329 | 5. 发布运行 1330 | 1331 | 1332 | 1333 | ~~~dockerfile 1334 | FROM java:8 1335 | 1336 | COPY *.jar /app.jar 1337 | 1338 | CMD ["--server.port=9091"] 1339 | 1340 | EXPOSE 9091 1341 | 1342 | ENTRYPOINT ["java","-jar",] 1343 | 1344 | ~~~ 1345 | 1346 | 1347 | 1348 | 1349 | 1350 | 1351 | 1352 | 1353 | 1354 | 1355 | 1356 | 1357 | 1358 | 1359 | 1360 | 1361 | 1362 | 1363 | 1364 | 1365 | 1366 | ## 进阶 1367 | 1368 | ### Docker Compose 1369 | 1370 | 1371 | 1372 | #### 简介 1373 | 1374 | (自动化脚本) 1375 | 1376 | DockerFile build run 手动操作单个容器 1377 | 1378 | 如果多个微服务相互依赖就会很麻烦 1379 | 1380 | DOcker Compose 可以高效的管理、定义多个容器 1381 | 1382 | 1383 | 1384 | > 官方介绍 1385 | 1386 | * 定义、运行多个容器 1387 | * YAML 配置文件 1388 | * single commond. 命令有哪些 1389 | 1390 | Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration. To learn more about all the features of Compose, see [the list of features](https://docs.docker.com/compose/#features). 1391 | 1392 | 所有环境都能够使用 compose 1393 | 1394 | Compose works in all environments: production, staging, development, testing, as well as CI workflows. You can learn more about each case in [Common Use Cases](https://docs.docker.com/compose/#common-use-cases). 1395 | 1396 | **三步骤:** 1397 | 1398 | Using Compose is basically a three-step process: 1399 | 1400 | 1. Define your app’s environment with a `Dockerfile` so it can be reproduced anywhere. 1401 | * DockerFile 保证项目在任何环境可以运行 1402 | 2. Define the services that make up your app in `docker-compose.yml` so they can be run together in an isolated environment. 1403 | * services 是什么 1404 | * `docker-compose.yml 怎么写 1405 | 3. Run `docker compose up` and the [Docker compose command](https://docs.docker.com/compose/#compose-v2-and-the-new-docker-compose-command) starts and runs your entire app. You can alternatively run `docker-compose up` using Compose standalone(`docker-compose` binary). 1406 | * 启动项目 1407 | 1408 | 1409 | 1410 | 作用 : 批量容器编排 1411 | 1412 | 1413 | 1414 | > 理解 1415 | 1416 | Compose 是 Docker 官方的开源项目,需要独立安装 1417 | 1418 | `Dockerfile` 让程序在任何地方运行 1419 | 1420 | Yaml文件 例: 1421 | 1422 | ~~~yaml 1423 | version: "3.9" # optional since v1.27.0 1424 | services: 1425 | web: 1426 | build: . 1427 | ports: 1428 | - "8000:5000" 1429 | volumes: 1430 | - .:/code 1431 | - logvolume01:/var/log 1432 | depends_on: 1433 | - redis 1434 | redis: 1435 | image: redis 1436 | volumes: 1437 | logvolume01: {} 1438 | ~~~ 1439 | 1440 | Compose : 重要概念 1441 | 1442 | * 服务 services , 容器/应用 (web、redis、mysql ...) 1443 | * 项目 project , 一组关联的容器 1444 | 1445 | 1446 | 1447 | #### 安装 1448 | 1449 | ~~~shell 1450 | # 第一步 下载 1451 | # 官方地址 (可能很慢) 1452 | sudo curl -L "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose 1453 | 1454 | # 这个可能快点 1455 | curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.5/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose 1456 | 1457 | # 第二步 授权 1458 | sudo chmod +x /usr/local/bin/docker-compose 1459 | 1460 | ~~~ 1461 | 1462 | ![安装成功](https://gitee.com/sky-dog/note/raw/master/img/202208111726995.png) 1463 | 1464 | #### 体验测试 1465 | 1466 | 1467 | 1468 | 1469 | 1470 | 1471 | 1472 | 1473 | 1474 | 1475 | 1476 | 1477 | 1478 | 1479 | 1480 | 1481 | 1482 | 1483 | 1484 | 1485 | 1486 | ### Docker Swam 1487 | 1488 | (配置集群) 1489 | 1490 | 1491 | 1492 | 1493 | 1494 | 1495 | 1496 | 1497 | 1498 | 1499 | 1500 | 1501 | 1502 | 1503 | 1504 | ### CI/CD jenkins 1505 | 1506 | 1507 | 1508 | 1509 | 1510 | ### Docker Stack 1511 | 1512 | 1513 | 1514 | 1515 | 1516 | ### Docker Secret 1517 | 1518 | 1519 | 1520 | 1521 | 1522 | 1523 | 1524 | ### Docker Config 1525 | 1526 | 1527 | 1528 | 1529 | 1530 | 1531 | 1532 | ### k8s 1533 | 1534 | 1535 | 1536 | 1537 | 1538 | 1539 | 1540 | 1541 | 1542 | 1543 | 1544 | 1545 | 1546 | 1547 | 1548 | 1549 | 1550 | 1551 | 1552 | 1553 | 1554 | 1555 | 1556 | 1557 | 1558 | 1559 | 1560 | 1561 | 1562 | 1563 | 1564 | 1565 | 1566 | 1567 | 1568 | 1569 | 1570 | 1571 | 1572 | 1573 | 1574 | 1575 | 1576 | 1577 | 1578 | 1579 | 1580 | 1581 | 1582 | 1583 | 1584 | 1585 | 1586 | 1587 | 1588 | 1589 | 1590 | 1591 | 1592 | 1593 | 1594 | 1595 | 1596 | 1597 | 1598 | 1599 | 1600 | 1601 | 1602 | 1603 | 1604 | 1605 | 1606 | 1607 | 1608 | 1609 | 1610 | 1611 | 1612 | 1613 | 1614 | 1615 | 1616 | 1617 | 1618 | 1619 | 1620 | 1621 | 1622 | 1623 | 1624 | 1625 | 1626 | 1627 | 1628 | 1629 | 1630 | 1631 | 1632 | 1633 | 1634 | 1635 | 1636 | 1637 | 1638 | 1639 | 1640 | 1641 | 1642 | 1643 | 1644 | 1645 | 1646 | 1647 | 1648 | 1649 | 1650 | 1651 | 1652 | -------------------------------------------------------------------------------- /etc/blog/RocketMQ入门.md: -------------------------------------------------------------------------------- 1 | # RocketMQ简单入门 2 | 3 | 本文若有不当之处欢迎提出pr/issue 4 | 5 | 主要内容: 6 | 7 | 1. **[初识MQ](#1.初识MQ)** 8 | 9 | 2. **[RocketMQ简介](#2.RocketMQ简介)** 10 | 11 | 3. **[RocketMQ安装](#3.RocketMQ安装)** 12 | 13 | 4. **[RocketMQ快速入门](#4.RocketMQ快速入门)** 14 | 15 | 5. **[SpringBoot集成RocketMQ](#5.SpringBoot集成RocketMQ)** 16 | 17 | 6. **[最后](#6.最后)** 18 | 19 | 20 | 21 | ## 1.初识MQ 22 | 23 | ### 1.1.同步和异步通讯 24 | 25 | 微服务间通讯有同步和异步两种方式: 26 | 27 | 同步通讯:就像打电话,需要实时响应。 28 | 29 | 异步通讯:就像发邮件,不需要马上回复。 30 | 31 | 两种方式各有优劣,打电话可以立即得到响应,但是你却不能跟多个人同时通话。发送邮件可以同时与多个人收发邮件,但是往往响应会有延迟。 32 | 33 | 34 | 35 | #### 1.1.1.同步通讯 36 | 37 | Feign调用就属于同步方式,虽然调用可以实时得到结果,但存在下面的问题: 38 | 39 | ![](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308030849476.png) 40 | 41 | 42 | 43 | **总结:** 44 | 45 | 同步调用的优点: 46 | 47 | - 时效性较强,可以立即得到结果 48 | 49 | 同步调用的问题: 50 | 51 | - 耦合度高 52 | - 性能和吞吐能力下降 53 | - 有额外的资源消耗 54 | - 有级联失败问题(由于一个故障导致了连锁反应,使得系统中的其他组件或节点也相继失败) 55 | 56 | 57 | 58 | #### 1.1.2.异步通讯 59 | 60 | 异步调用则可以避免上述问题: 61 | 62 | 63 | 64 | 我们以购买商品为例,用户支付后需要调用订单服务完成订单状态修改,调用物流服务,从仓库分配响应的库存并准备发货。 65 | 66 | 在事件模式中,支付服务是事件发布者(publisher),在支付完成后只需要发布一个支付成功的事件(event),事件中带上订单id。 67 | 68 | 订单服务和物流服务是事件订阅者(Consumer),订阅支付成功的事件,监听到事件后完成自己业务即可。 69 | 70 | 71 | 72 | 为了解除事件发布者与订阅者之间的耦合,两者并不是直接通信,而是有一个中间人(Broker)。发布者发布事件到Broker,不关心谁来订阅事件。订阅者从Broker订阅事件,不关心谁发来的消息。 73 | 74 | ![image-20230801111039523](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011110579.png) 75 | 76 | Broker 是一个像数据总线一样的东西,所有的服务要接收数据和发送数据都发到这个总线上,这个总线就像协议一样,让服务间的通讯变得标准和可控。 77 | 78 | 79 | 80 | 好处: 81 | 82 | - 吞吐量提升:无需等待订阅者处理完成,响应更快速 83 | 84 | - 故障隔离:服务没有直接调用,不存在级联失败问题 85 | - 调用间没有阻塞,不会造成无效的资源占用 86 | - 耦合度极低,每个服务都可以灵活插拔,可替换 87 | - 流量削峰:不管发布事件的流量波动多大,都由Broker接收,订阅者可以按照自己的速度去处理事件 88 | 89 | 90 | 91 | 缺点: 92 | 93 | - 架构复杂了,业务没有明显的流程线,不好管理 94 | - 需要依赖于Broker的可靠、安全、性能 95 | 96 | 好在现在开源软件或云平台上 Broker 的软件是非常成熟的,比较常见的一种就是我们今天要学习的MQ技术。 97 | 98 | 99 | 100 | ### 1.2.技术对比: 101 | 102 | MQ,中文是消息队列(MessageQueue),字面来看就是存放消息的队列。也就是事件驱动架构中的Broker。 103 | 104 | 几种常见MQ的对比: 105 | 106 | | | **RabbitMQ** | **ActiveMQ** | **RocketMQ** | **Kafka** | 107 | | ---------- | ----------------------- | ------------------------------ | ------------ | ---------- | 108 | | 公司/社区 | Rabbit | Apache | 阿里 | Apache | 109 | | 开发语言 | Erlang | Java | Java | Scala&Java | 110 | | 协议支持 | AMQP,XMPP,SMTP,STOMP | OpenWire,STOMP,REST,XMPP,AMQP | 自定义协议 | 自定义协议 | 111 | | 可用性 | 高 | 一般 | 高 | 高 | 112 | | 单机吞吐量 | 一般 | 差 | 高 | 非常高 | 113 | | 消息延迟 | 微秒级 | 毫秒级 | 毫秒级 | 毫秒以内 | 114 | | 消息可靠性 | 高 | 一般 | 高 | 一般 | 115 | 116 | 追求可用性:Kafka、 RocketMQ 、RabbitMQ 117 | 118 | 追求可靠性:RabbitMQ、RocketMQ 119 | 120 | 追求吞吐能力:RocketMQ、Kafka 121 | 122 | 追求消息低延迟:RabbitMQ、Kafka 123 | 124 | 不同的消息队列系统在不同场景下有各自的优势和适用性。以下是各个消息队列系统在不同场合下的最佳选择: 125 | 126 | 1. Kafka: 127 | - 最佳场合:大规模数据处理、实时日志收集和分析、流式处理。 128 | - 优势:高吞吐量、低延迟、水平扩展能力强、长期消息存储,适合构建大规模的实时数据流处理平台,如实时日志收集和分析、事件流处理等。 129 | 2. RabbitMQ: 130 | - 最佳场合:传统的企业级应用、轻量级的消息传递场景。 131 | - 优势:简单易用、支持多种消息协议、适合点对点和发布/订阅模式,对于传统的企业应用和中小规模的消息传递需求,是一种可靠的选择。 132 | 3. ActiveMQ: 133 | - 最佳场合:中小规模的企业应用、Java生态系统中的集成需求。 134 | - 优势:Java开发环境友好、支持多种消息协议,适合与Java生态系统的其他组件集成,如Spring框架等。 135 | 4. RocketMQ: 136 | - 最佳场合:大规模的分布式系统、互联网应用、金融领域的消息处理。 137 | - 优势:高吞吐量、低延迟、丰富的消息存储模式,适用于处理大规模的消息传递场景,特别是在互联网和金融领域。 138 | 139 | 综合考虑以上因素,可以做如下简单**总结**: 140 | 141 | - 如果需要处理大规模的实时数据流、日志收集和分析等高吞吐量场景,首选`Kafka`。 142 | - 如果对于消息传递的简单性和易用性有较高要求,适合中小规模的企业应用和轻量级消息传递需求,可以选择`RabbitMQ`或`ActiveMQ`。 143 | - 如果在大规模的分布式系统、互联网应用或金融领域需要处理消息传递,`RocketMQ`是一个较好的选择。 144 | 145 | ## 2.RocketMQ简介 146 | 147 | 官网: http://rocketmq.apache.org/ 148 | 149 | RocketMQ是阿里巴巴2016年**MQ中间件**,使用Java语言开发,RocketMQ 是一款开源的**分布式消息系统**,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。同时,广泛应用于多个领域,包括异步通信解耦、企业解决方案、金融支付、电信、电子商务、快递物流、广告营销、社交、即时通信、移动应用、手游、视频、物联网、车联网等。 150 | 151 | RocketMQ的设计目标是支持大规模消息处理,具有高并发、高可用和容错能力。它在多个方面提供了强大的功能和特性: 152 | 153 | 1. 分布式架构:RocketMQ采用分布式架构,支持在多个节点之间进行消息的发送和接收,实现了水平扩展能力。 154 | 2. 高吞吐量:RocketMQ可以在大规模并发场景下实现高吞吐量的消息处理,适用于高并发的业务场景。 155 | 3. 低延迟:RocketMQ具有较低的消息传递延迟,适用于需要实时性的应用场景。 156 | 4. 消息可靠性:RocketMQ提供了多种消息存储模式,可以确保消息的可靠传递,包括同步刷盘和异步刷盘等方式。 157 | 5. 消息顺序性:RocketMQ支持消息的顺序传递,可以确保同一消息队列中的消息按照发送顺序被消费。 158 | 6. 支持多种消息模式:RocketMQ支持发布/订阅模式和点对点模式,可以根据业务需求选择合适的消息模式。 159 | 7. 灵活的部署方式:RocketMQ支持多种部署方式,可以在单机上运行,也可以搭建集群部署。 160 | 8. 丰富的监控和管理工具:RocketMQ提供了丰富的监控和管理工具,方便管理员对消息队列进行监控和管理。 161 | 162 | 163 | 164 | ### 核心概念 165 | 166 | **Producer**:消息的发送者,生产者;举例:发件人。 167 | 168 | **Consumer**:消息接收者,消费者;举例:收件人。 169 | 170 | **Broker**:消息队列的中间服务器,负责存储消息并将消息传递给消费者;举例:快递。 171 | 172 | **NameServer**:可以理解为是一个注册中心,主要是用来保存topic路由信息,管理Broker。在NameServer的集群中,NameServer与NameServer之间是没有任何通信的;举例:各个快递公司的管理机构相当于broker的注册中心,保留了broker的信息。 173 | 174 | **Queue**:队列,消息存放的位置,一个Broker中可以有多个队列。 175 | 176 | **Topic**:消息的逻辑分类,生产者发送消息到指定的Topic,消费者从指定的Topic订阅消息。一个Topic可以有多个Producer和多个Consumer。 177 | 178 | **ProducerGroup**:生产者组 。 179 | 180 | **ConsumerGroup**:消费者组,多个消费者组可以同时消费一个主题的消息。 181 | 182 | 183 | 184 | ### 工作流程 185 | 186 | 该部分转载自 [掘金文章]([RocketMQ保姆级教程 - 掘金 (juejin.cn)](https://juejin.cn/post/7134227366481494046#heading-4)) 187 | 188 | ![img](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308012236257.webp) 189 | 190 | 通过这张图就可以很清楚的知道,RocketMQ大致的工作流程: 191 | 192 | * `Broker`启动的时候,会往每台NameServer(因为NameServer之间不通信,所以每台都得注册)注册自己的信息,这些信息包括自己的ip和端口号,自己这台Broker有哪些topic等信息。 193 | 194 | * `Producer`在启动之后会跟会NameServer建立连接,定期从NameServer中获取Broker的信息,当发送消息的时候,会根据消息需要发送到哪个topic去找对应的Broker地址,如果有的话,就向这台Broker发送请求;没有找到的话,就看根据是否允许自动创建topic来决定是否发送消息。 195 | 196 | * `Broker`在接收到Producer的消息之后,会将消息存起来,持久化,如果有从节点的话,也会主动同步给从节点,实现数据的备份 197 | 198 | * `Consumer`启动之后也会跟会NameServer建立连接,定期从NameServer中获取Broker和对应topic的信息,然后根据自己需要订阅的topic信息找到对应的Broker的地址,然后跟Broker建立连接,获取消息,进行消费 199 | 200 | 201 | 202 | 203 | 204 | ## 3.RocketMQ安装 205 | 206 | 本文档所涉及的是单机版的RocketMQ安装教程,能够满足基本的学习使用,属于入门级的教程,如果想要搭集群部署,可以参考其他资料,进行配置即可 207 | 208 | #### 3.1.Windos下的安装 209 | 210 | >**所需环境** 211 | > 212 | >Windows 64位系统 213 | >JDK1.8(64位) 214 | >Maven 215 | 216 | 217 | 218 | 进入[RocketMQ官网下载]([下载 | RocketMQ (apache.org)](https://rocketmq.apache.org/download/)) 219 | 220 | ##### 1、选择**Binary 下载** 221 | 222 | ![image-20230801144754675](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011447766.png) 223 | 224 | ##### 2、将压缩包解压至自定路径 225 | 226 | ![image-20230801145153332](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011451403.png) 227 | 228 | ##### 3、配置系统中的环境变量 229 | 230 | > 变量名:ROCKETMQ_HOME 231 | > 232 | > 变量值:(如图浏览目录选择指定bin-release文件夹路径) 233 | 234 | ![image-20230801145430167](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011454227.png) 235 | 236 | 237 | 238 | ##### 4.启动RocketMQ 239 | 240 | 在自己安装的RocketMQ的bin目录下执行cmd命令,输入下面命令,启动NameServer 241 | 242 | > start mqnamesrv.cmd 243 | 244 | ![image-20230801150123283](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011501372.png) 245 | 246 | 若出现如上图所示的命令框,说明启动成功,**保留窗口切勿关闭** 247 | 248 | **继续启动broker** 249 | 250 | 与上述同样的路径下呼出cmd,执行如下命令: 251 | 252 | > start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable = true 253 | 254 | ![image-20230801150447169](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011504276.png) 255 | 256 | **看到上述命令框弹出即完成对RocketMQ的启动。** 257 | 258 | ==注意:== 259 | 260 | RocketMQ默认的虚拟机内存较大,启动如果因为内存不足报错可执行以下步骤: 261 | 262 | 用记事本打开bin目录下的 `runbroker.cmd` 263 | 264 | ![image-20230801152309835](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011523942.png) 265 | 266 | > 1. `-Xms2g`:设置JVM初始堆内存大小为2GB。 267 | > 2. `-Xmx2g`:设置JVM最大堆内存大小为2GB。 268 | > 269 | > 可修改为 -Xms256m -Xmx256m -Xmn128m 270 | 271 | 272 | 273 | 同理打开`runserver.cmd` 274 | 275 | ![image-20230801152644369](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011526463.png) 276 | 277 | > 修改jvm参数为 278 | > 279 | > -Xms256m -Xmx256m -Xmn512m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m 280 | 281 | 282 | 283 | ##### 5.配置可视化页面 284 | 285 | 下载可视化插件源码 286 | 287 | github下载地址:https://github.com/apache/rocketmq-dashboard 288 | 289 | 复制下载链接后使用git下载 290 | 291 | 可自建文件夹,进入后使用git bash下载 292 | 293 | ![image-20230801163401327](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011634391.png) 294 | 295 | > git clone https://github.com/apache/rocketmq-dashboard.git 296 | 297 | ![image-20230801161727083](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011617144.png) 298 | 299 | 300 | 301 | 下载完成后,进入`application.yml`中查看配置 302 | 303 | ![image-20230801163618945](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011636007.png) 304 | 305 | ![image-20230801170930502](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011709586.png) 306 | 307 | 保存后进入到 ../rocketmq-dashboard目录下,鼠标右键进入git控制台 308 | 309 | > 执行 mvn clean package -Dmaven.test.skip=true 310 | 311 | 将该文件打包成jar包,该jar包保存在 该目录的 target子目录下 312 | 313 | ![image-20230801164211692](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011642741.png) 314 | 315 | 打包完成! 316 | 317 | ![image-20230801165117196](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011651260.png) 318 | 319 | 在 target子目录下可找到对应的jar包 320 | 321 | ![image-20230801165150755](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011651823.png) 322 | 323 | 在该目录下打开cmd,输入指令==(请保证已经运行NameServer和broker)==: 324 | 325 | > java -jar rocketmq-dashboard-1.0.1-SNAPSHOT.jar 326 | 327 | ![image-20230801170551977](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011705070.png) 328 | 329 | 成功执行jar包 330 | 331 | 然后在网页中访问 http://127.0.0.1:8080/#/ 即可进入rocketmq的图形化界面 332 | 333 | ![image-20230801170643300](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011706400.png) 334 | 335 | 336 | 337 | 338 | 339 | #### 3.2.Linux下的安装 340 | 341 | ==请提前设置服务器的防火墙,放通9876和10909(默认的 RocketMQ Broker 端口号)端口== 342 | 343 | 进入[RocketMQ官网下载]([下载 | RocketMQ (apache.org)](https://rocketmq.apache.org/download/)) 344 | 345 | ##### 1、选择**Binary 下载** 346 | 347 | ![image-20230801144754675](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011735189.png) 348 | 349 | 350 | 351 | ##### 2、在linux中创建RocketMQ文件夹 352 | 353 | > mkdir RocketMQ 354 | 355 | ![image-20230801172901401](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011729451.png) 356 | 357 | 358 | 359 | ##### 3、将rocketmq-all-5.1.2-bin-release.zip压缩文件上传到linux服务器中 360 | 361 | 连接工具[XSHELL - NetSarang Website](https://www.xshell.com/zh/xshell/) 362 | 363 | ![image-20230801174800693](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011748741.png) 364 | 365 | 将压缩包上传到第2步创建的文件中 366 | 367 | ![image-20230801175003080](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011750131.png) 368 | 369 | ##### 4、解压zip包 370 | 371 | > cd ./RocketMQ/ 372 | > 373 | > unzip rocketmq-all-5.1.2-bin-release.zip 374 | 375 | ![image-20230801175140268](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011752656.png) 376 | 377 | 如果你的服务器没有unzip命令,则下载安装一个 378 | 379 | > yum install unzip 380 | 381 | 382 | 383 | ##### 5、配置环境变量 384 | 385 | > vim /etc/profile 386 | > 387 | > 在文件末尾添加 388 | > 389 | > export NAMESRV_ADDR=**服务器IP**:9876 390 | 391 | 392 | 393 | ##### 6、修改脚本文件 394 | 395 | 修改目录/root/RocketMQ/rocketmq-all-5.1.2-bin-release/bin下的配置文件: `runserver.sh`、`runbroker.sh` 396 | 397 | 修改`runserver.sh `中原有内存配置 398 | 399 | ```sh 400 | JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn512m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m" 401 | ``` 402 | 403 | 修改runbroker.sh 中原有内存配置 404 | 405 | ```sh 406 | JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn128m" 407 | ``` 408 | 409 | 修改目录/root/RocketMQ/rocketmq-all-5.1.2-bin-release/conf/broker.conf文件 410 | 411 | 在最后添加上 412 | 413 | ``` 414 | namesrvAddr = (服务器ip):9876 415 | autoCreateTopicEnable=true 416 | brokerIP1 = (服务器ip) 417 | ``` 418 | 419 | ![image-20230801191136957](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011911005.png) 420 | 421 | ##### 7、启动 422 | 423 | 进入/root/RocketMQ/rocketmq-all-5.1.2-bin-release 424 | 425 | 首先在安装目录下创建一个logs文件夹,用于存放日志 426 | 427 | > mkdir logs 428 | 429 | 运行两条命令,启动NameServer和broker 430 | 431 | >nohup sh bin/mqnamesrv > ./logs/namesrv.log & 432 | > 433 | >nohup sh bin/mqbroker -c conf/broker.conf > ./logs/broker.log & 434 | 435 | ![image-20230801192010772](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011920827.png) 436 | 437 | 运行后可在logs文件夹下看到两个日志文件 438 | 439 | ![image-20230801192127832](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011921911.png) 440 | 441 | ![image-20230801193014578](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011930651.png) 442 | 443 | 444 | 445 | ##### 8.配置可视化页面 446 | 447 | 前置步骤参考windows下的第5步[5.配置可视化页面](#5.配置可视化页面) 448 | 449 | 将jar包上传到服务器的/root/RocketMQ中 450 | 451 | ![image-20230801193449152](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011934222.png) 452 | 453 | 然后在RockerMQ中运行指令: 454 | 455 | > nohup java -jar rocketmq-dashboard-1.0.1-SNAPSHOT.jar rocketmq.config.namesrvAddr=127.0.0.1:9876 456 | 457 | 命令拓展:--server.port指定运行的端口 458 | 459 | --rocketmq.config.namesrvAddr=127.0.0.1:9876 指定namesrv地址 460 | 461 | ![image-20230801193916469](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011939517.png) 462 | 463 | ![image-20230801194920398](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011949490.png) 464 | 465 | 成功运行! 466 | 467 | 最后访问 **服务器ip:8080** 即可访问到图形化界面 468 | 469 | ![image-20230801195122389](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308011951502.png) 470 | 471 | 472 | 473 | 474 | 475 | #### 3.3.docker安装(推荐) 476 | 477 | ##### 1、下载RockerMQ需要的镜像 478 | 479 | > docker pull rocketmqinc/rocketmq 480 | > 481 | > docker pull styletang/rocketmq-console-ng 482 | 483 | ![image-20230801202211846](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308012022924.png) 484 | 485 | 486 | 487 | ##### 2、启动NameServer服务 488 | 489 | 创建NameServer数据存储路径 490 | 491 | > mkdir -p /home/rocketmq/data/namesrv/logs /home/rocketmq/data/namesrv/store 492 | 493 | 启动NameServer容器 494 | 495 | > docker run -d --name rmqnamesrv -p 9876:9876 -v /home/rocketmq/data/namesrv/logs:/root/logs -v /home/rocketmq/data/namesrv/store:/root/store -e "MAX_POSSIBLE_HEAP=100000000" rocketmqinc/rocketmq sh mqnamesrv 496 | 497 | 498 | 这是一个Docker命令,用于在Docker容器中运行RocketMQ Name Server(消息服务器)。让我们逐步解释这个命令: 499 | 500 | ``` 501 | bashCopy codedocker run -d \ 502 | --name rmqnamesrv \ 503 | -p 9876:9876 \ 504 | -v /home/rocketmq/data/namesrv/logs:/root/logs \ 505 | -v /home/rocketmq/data/namesrv/store:/root/store \ 506 | -e "MAX_POSSIBLE_HEAP=100000000" \ 507 | rocketmqinc/rocketmq sh mqnamesrv 508 | ``` 509 | 510 | 解释: 511 | 512 | - `docker run`: 这是Docker命令的基本部分,用于运行一个新的容器。 513 | - `-d`: 这是一个选项,表示在后台(detached mode)运行容器。 514 | - `--name rmqnamesrv`: 这是为容器指定一个名称,该名称为"rmqnamesrv"。 515 | - `-p 9876:9876`: 这是端口映射的选项,将主机的端口9876映射到容器的端口9876。RocketMQ的Name Server默认监听端口是9876,通过这个映射,可以从主机的9876端口访问容器中运行的RocketMQ Name Server。 516 | - `-v /home/rocketmq/data/namesrv/logs:/root/logs`: 这是用于将主机的`/home/rocketmq/data/namesrv/logs`目录映射到容器内的`/root/logs`目录。这样做的目的是将RocketMQ Name Server的日志文件存储在主机的目录中,方便查看和管理。 517 | - `-v /home/rocketmq/data/namesrv/store:/root/store`: 这是用于将主机的`/home/rocketmq/data/namesrv/store`目录映射到容器内的`/root/store`目录。这样做的目的是将RocketMQ Name Server的存储文件存储在主机的目录中。 518 | - `-e "MAX_POSSIBLE_HEAP=100000000"`: 这是用于设置环境变量的选项,设置了RocketMQ Name Server的最大堆内存大小为100,000,000字节,约为100MB。 519 | - `rocketmqinc/rocketmq`: 这是指定要运行的Docker镜像的名称。在这里,它使用了RocketMQ官方提供的Docker镜像,名为`rocketmqinc/rocketmq`。 520 | - `sh mqnamesrv`: 这是在容器中要运行的命令。在这里,它运行了RocketMQ Name Server的启动命令。 521 | 522 | 523 | 524 | ##### 3、启动Broker服务 525 | 526 | 创建Broker数据存储路径 527 | 528 | > mkdir -p /home/rocketmq/data/broker/logs /home/rocketmq/data/broker/store 529 | 530 | 创建conf配置文件目录 531 | 532 | > mkdir /home/rocketmq/conf 533 | 534 | 在配置文件目录下创建broker.conf配置文件 535 | 536 | ``` 537 | # 所属集群名称,如果节点较多可以配置多个 538 | brokerClusterName = DefaultCluster 539 | #broker名称,master和slave使用相同的名称,表明他们的主从关系 540 | brokerName = broker-a 541 | #0表示Master,大于0表示不同的slave 542 | brokerId = 0 543 | #表示几点做消息删除动作,默认是凌晨4点 544 | deleteWhen = 04 545 | #在磁盘上保留消息的时长,单位是小时 546 | fileReservedTime = 48 547 | #有三个值:SYNC_MASTER,ASYNC_MASTER,SLAVE;同步和异步表示Master和Slave之间同步数据的机制; 548 | brokerRole = ASYNC_MASTER 549 | #刷盘策略,取值为:ASYNC_FLUSH,SYNC_FLUSH表示同步刷盘和异步刷盘;SYNC_FLUSH消息写入磁盘后才返回成功状态,ASYNC_FLUSH不需要; 550 | flushDiskType = ASYNC_FLUSH 551 | # 设置broker节点所在服务器的ip地址 552 | autoCreateTopicEnable=true 553 | brokerIP1 = 你服务器外网ip 554 | ``` 555 | 556 | 启动Broker容器 (==注意先开放10911和10909端口==) 557 | 558 | > docker run -d --name rmqbroker --link rmqnamesrv:namesrv -p 10911:10911 -p 10909:10909 -v /home/rocketmq/data/broker/logs:/root/logs -v /home/rocketmq/data/broker/store:/root/store -v /home/rocketmq/conf/broker.conf:/opt/rocketmq/conf/broker.conf --privileged=true -e "NAMESRV_ADDR=namesrv:9876" -e "MAX_POSSIBLE_HEAP=200000000" rocketmqinc/rocketmq sh mqbroker -c /opt/rocketmq/conf/broker.conf 559 | 560 | 解释: 561 | 562 | - `docker run`: 这是Docker命令的基本部分,用于运行一个新的容器。 563 | - `-d`: 这是一个选项,表示在后台(detached mode)运行容器。 564 | - `--name rmqbroker`: 这是为容器指定一个名称,该名称为"rmqbroker"。 565 | - `--link rmqnamesrv:namesrv`: 这是用于将已经运行的RocketMQ Name Server容器 "rmqnamesrv" 链接到当前运行的Broker容器。这样Broker容器就可以通过"namesrv"主机名访问Name Server。 566 | - `-p 10911:10911`: 这是端口映射的选项,将主机的端口10911映射到容器的端口10911。RocketMQ的Broker默认监听端口是10911,通过这个映射,可以从主机的10911端口访问容器中运行的RocketMQ Broker。 567 | - `-p 10909:10909`: 同上,将主机的端口10909映射到容器的端口10909。RocketMQ的Broker默认监听的另一个端口是10909,该端口用于向主节点发送心跳。 568 | - `-v /home/rocketmq/data/broker/logs:/root/logs`: 这是用于将主机的`/home/rocketmq/data/broker/logs`目录映射到容器内的`/root/logs`目录。这样做的目的是将RocketMQ Broker的日志文件存储在主机的目录中,方便查看和管理。 569 | - `-v /home/rocketmq/data/broker/store:/root/store`: 这是用于将主机的`/home/rocketmq/data/broker/store`目录映射到容器内的`/root/store`目录。这样做的目的是将RocketMQ Broker的存储文件存储在主机的目录中。 570 | - `-v /home/rocketmq/conf/broker.conf:/opt/rocketmq/conf/broker.conf`: 这是用于将主机的`/home/rocketmq/conf/broker.conf`文件映射到容器内的`/opt/rocketmq/conf/broker.conf`文件。这个文件是RocketMQ Broker的配置文件,通过这个映射,可以将自定义的Broker配置应用到容器中。 571 | - `--privileged=true`: 这是为容器添加特权模式,这样容器就可以获得更高的权限。 572 | - `-e "NAMESRV_ADDR=namesrv:9876"`: 这是用于设置环境变量的选项,设置了RocketMQ Broker的Name Server地址为"namesrv:9876"。`NAMESRV_ADDR`是RocketMQ Broker连接Name Server的地址,这里设置为"namesrv:9876"表示通过名为"namesrv"的容器连接Name Server。 573 | - `-e "MAX_POSSIBLE_HEAP=200000000"`: 这是用于设置环境变量的选项,设置了RocketMQ Broker的最大堆内存大小为200,000,000字节,约为200MB。 574 | - `rocketmqinc/rocketmq`: 这是指定要运行的Docker镜像的名称。在这里,它使用了RocketMQ官方提供的Docker镜像,名为`rocketmqinc/rocketmq`。 575 | - `sh mqbroker -c /opt/rocketmq/conf/broker.conf`: 这是在容器中要运行的命令。在这里,它运行了RocketMQ Broker的启动命令,通过`-c`参数指定了配置文件的路径。 576 | 577 | 启动控制台 (==注意先开放9999端口==) 578 | 579 | > docker run -d --name rmqadmin -e "JAVA_OPTS=-Drocketmq.namesrv.addr=服务器的ip:9876 \ 580 | > 581 | > -Dcom.rocketmq.sendMessageWithVIPChannel=false \ 582 | > 583 | > -Duser.timezone='Asia/Shanghai'" -v /etc/localtime:/etc/localtime -p 9999:8080 styletang/rocketmq-console-ng 584 | 585 | 解释: 586 | 587 | - `docker run`: 这是Docker命令的基本部分,用于运行一个新的容器。 588 | - `-d`: 这是一个选项,表示在后台(detached mode)运行容器。 589 | - `--name rmqadmin`: 这是为容器指定一个名称,该名称为"rmqadmin"。 590 | - `-e "JAVA_OPTS=..."`: 这是用于设置Java虚拟机(JVM)运行时的参数。在这里,它设置了三个参数: 591 | - `-Drocketmq.namesrv.addr=服务器的ip:9876`:这是用于设置RocketMQ Name Server的地址。您需要将"服务器的ip"替换为实际的RocketMQ Name Server的IP地址,端口为9876。 592 | - `-Dcom.rocketmq.sendMessageWithVIPChannel=false`:这是用于设置RocketMQ消息发送时是否启用VIP通道的参数,将其设置为false表示禁用VIP通道。 593 | - `-Duser.timezone='Asia/Shanghai'`:这是用于设置容器时区的参数,将其设置为'Asia/Shanghai'表示使用上海时区。 594 | - `-v /etc/localtime:/etc/localtime`: 这是用于将主机的时区配置映射到容器内,保持容器与主机的时区一致。 595 | - `-p 9999:8080`: 这是端口映射的选项,将主机的端口9999映射到容器的端口8080。RocketMQ控制台使用8080端口,通过这个映射,可以从主机的9999端口访问容器中运行的RocketMQ控制台。 596 | - `styletang/rocketmq-console-ng`: 这是指定要运行的Docker镜像的名称。在这里,它使用了RocketMQ控制台NG的Docker镜像,名为`styletang/rocketmq-console-ng`。 597 | 598 | 599 | 600 | 正常启动后的docker ps 601 | 602 | ![image-20230801214538318](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308012145429.png) 603 | 604 | ##### 4、访问控制台 605 | 606 | http://你的服务器外网ip:9999/ 607 | 608 | ![image-20230801214801893](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308012148095.png) 609 | 610 | 611 | 612 | 613 | 614 | ## 4.RocketMQ快速入门 615 | 616 | [4.x文档 ](https://rocketmq.apache.org/zh/docs/4.x/) 下文基于该文档 617 | 618 | [5.0文档](https://rocketmq.apache.org/zh/docs/) 619 | 620 | 通过该部分可以快速入门RocketMQ提供的多种发送消息的模式,例如同步消息,异步消息,顺序消息,延迟消息,事务消息等 621 | 622 | ### ==消息发送和监听的流程== 623 | 624 | #### 消息生产者 625 | 626 | 1.创建消息生产者`producer`,并制定生产者组名 627 | 628 | 2.指定`NameServer`地址 629 | 630 | 3.启动`producer` 631 | 632 | 4.创建消息对象,指定主题`Topic`、`Tag`和消息体等 633 | 634 | 5.发送消息 635 | 636 | 6.关闭生产者`producer` 637 | 638 | 639 | 640 | #### 消息消费者 641 | 642 | 1.创建消费者`consumer`,制定消费者组名 643 | 644 | 2.指定`NameServe`r地址 645 | 646 | 3.创建监听订阅主题`Topic`和`Tag`等 647 | 648 | 4.处理消息 649 | 650 | 5.启动消费者`consumer` 651 | 652 | 653 | 654 | 了解了消息发送和监听的流程,我们可以开始简单的代码实现 655 | 656 | 657 | 658 | ### Start 659 | 660 | 创建一个空项目 `RocketMQ-study` 661 | 662 | 在空项目下创建一个新模板: 663 | 664 | ![image-20230801233051349](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308012330430.png) 665 | 666 | 667 | 668 | #### 简单测试 669 | 670 | ![image-20230801233155749](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308012331826.png) 671 | 672 | 引入依赖: 673 | 674 | ```xml 675 | 676 | 677 | org.springframework.boot 678 | spring-boot-starter-web 679 | 680 | 681 | 682 | 683 | org.apache.rocketmq 684 | rocketmq-client 685 | 5.1.2 686 | 687 | 688 | 689 | org.projectlombok 690 | lombok 691 | true 692 | 693 | 694 | 695 | junit 696 | junit 697 | 4.12 698 | 699 | 700 | ``` 701 | 702 | 测试一个简单的生产方法和消费方法: 703 | 704 | ```java 705 | package com.wp.rocketmqdemo01.constant; 706 | 707 | public interface MqConstant { 708 | /** 709 | * 生产者组名 710 | */ 711 | String PRODUCER_GROUP = "test-producer-group"; 712 | /** 713 | * 消费者组名 714 | */ 715 | String CONSUMER_GROUP = "test-consumer-group"; 716 | /** 717 | * 主题 718 | */ 719 | String TOPIC = "test-topic"; 720 | /** 721 | * NameServer地址 722 | */ 723 | String NAME_SRV_ADDR = "ip:9876"; 724 | } 725 | ``` 726 | 727 | ```java 728 | package com.wp.rocketmqdemo01.demo; 729 | 730 | import com.wp.rocketmqdemo01.constant.MqConstant; 731 | import lombok.extern.slf4j.Slf4j; 732 | import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; 733 | import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; 734 | import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; 735 | import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; 736 | import org.apache.rocketmq.client.producer.DefaultMQProducer; 737 | import org.apache.rocketmq.client.producer.SendResult; 738 | import org.apache.rocketmq.common.message.Message; 739 | import org.apache.rocketmq.common.message.MessageExt; 740 | import org.junit.Test; 741 | 742 | import java.util.List; 743 | 744 | @Slf4j 745 | public class SimpleTest01 { 746 | 747 | /** 748 | * 生产者 749 | * @throws Exception 750 | */ 751 | @Test 752 | public void SimpleTestProducer() throws Exception { 753 | // 创建一个生产者(制定一个组名 754 | DefaultMQProducer producer = new DefaultMQProducer(MqConstant.PRODUCER_GROUP); 755 | // 指定NameServer地址,连接到NameServer 756 | producer.setNamesrvAddr(MqConstant.NAME_SRV_ADDR); 757 | // 启动生产者 758 | producer.start(); 759 | // 创建一个消息 760 | Message message = new Message(MqConstant.TOPIC, MqConstant.TAG, "Hello RocketMQ".getBytes()); 761 | // 发送消息 762 | SendResult sendResult = producer.send(message); 763 | log.info("消息发送结果:{}", sendResult); 764 | // 关闭生产者 765 | producer.shutdown(); 766 | } 767 | 768 | /** 769 | * 消费者 770 | * @throws Exception 771 | */ 772 | @Test 773 | public void SimpleTestConsumer() throws Exception { 774 | // 创建一个消费者(制定一个组名 775 | DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(MqConstant.CONSUMER_GROUP); 776 | // 指定NameServer地址,连接到NameServer 777 | consumer.setNamesrvAddr(MqConstant.NAME_SRV_ADDR); 778 | // 订阅主题 *表示订阅所有 779 | consumer.subscribe(MqConstant.TOPIC, "*"); 780 | // 注册消息监听器(一直监听,异步回调方法) 781 | consumer.registerMessageListener(new MessageListenerConcurrently() { 782 | @Override 783 | public ConsumeConcurrentlyStatus consumeMessage(List list, ConsumeConcurrentlyContext consumeConcurrentlyContext) { 784 | // 这个就是消费的方法 业务逻辑 785 | log.info("我是消费者,我正在消费消息"); 786 | log.info(list.get(0).toString()); 787 | for(MessageExt messageExt : list) { 788 | log.info("消费消息:{}", new String(messageExt.getBody())); 789 | } 790 | log.info("消息上下文:{}", consumeConcurrentlyContext); 791 | // 返回消费状态 792 | // 如果消费成功,返回CONSUME_SUCCESS,消息会被消费掉,从mq出队 793 | // 如果消费失败,返回RECONSUME_LATER,消息会重新投递,mq不会出队 794 | return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; 795 | } 796 | }); 797 | // 启动消费者 这个start一定要写在registerMessageListener下面 798 | consumer.start(); 799 | // 保证消费者不退出,挂起当前jvm 800 | System.in.read(); 801 | } 802 | } 803 | ``` 804 | 805 | 分别测试后得到如下结果: 806 | 807 | ![image-20230802175627877](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308021756018.png) 808 | 809 | ![image-20230802175700920](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308021757101.png) 810 | 811 | ![image-20230802175717327](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308021757415.png) 812 | 813 | 814 | 815 | ![image-20230802175759752](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308021757819.png) 816 | 817 | ![image-20230802180033298](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308021800379.png) 818 | 819 | ![image-20230802180108488](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308021801705.png) 820 | 821 | 1. `Broker Offset`(代理器偏移量): Broker Offset是指消息队列中的消息在Broker(消息代理器)上的偏移位置。**当生产者将消息发送到Broker时,每条消息都会被赋予一个唯一的偏移量,表示该消息在队列中的位置**。Broker Offset主要由消息代理器维护和管理,用于追踪消息的存储和处理情况。 822 | 2. `Consumer Offset`(消费者偏移量): Consumer Offset是指消费者在消费消息时的位置偏移。**当消费者成功消费了一条消息后,会将自己的消费偏移量记录下来**,表示下次继续消费消息的起始位置。消费者需要定期更新Consumer Offset,以保证消息处理的准确性和可靠性。 823 | 3. `Diff Total`(差异总数): **Diff Total是Broker Offset和Consumer Offset之间的差异总和**。也就是说,Diff Total表示消息队列中已经被生产者发送并存储在Broker上的消息数量,但尚未被消费者消费的消息数量。Diff Total可以用来监控消息队列的堆积情况,帮助发现消息处理速度跟不上消息产生速度的问题。 824 | 825 | 最简单的测试完成! 826 | 827 | 828 | 829 | #### MQ的消费模式 830 | 831 | 可以大致分为两种:推(Push)模式和拉(Pull)模式 832 | 833 | 1. 推(Push)模式: 在推模式中,消息队列将消息直接推送给消费者。一旦有新的消息产生并发送到队列中,队列会立即将该消息推送给已注册的消费者。这样消费者就可以及时收到并处理消息。推模式适用于需要实时响应和高实时性的场景,比如即时通讯、实时推送等。 834 | 2. 拉(Pull)模式: 在拉模式中,消费者需要主动从消息队列中拉取消息。消费者需要周期性地向队列发起请求,查询是否有新的消息可供消费。如果队列中有新消息,队列会将这些消息返回给消费者,然后消费者再对这些消息进行处理。拉模式适用于不需要实时响应的场景,比如批量处理、数据同步等。 835 | 836 | 每种消费模式都有其适用的场景和优缺点。推模式能够及时将消息推送给消费者,实现了实时性和低延迟,但在高并发场景下可能会产生大量推送请求,增加系统压力。而拉模式需要消费者主动轮询消息队列,可以控制消费的速度,但可能会导致消息处理不及时,影响系统的实时性。 837 | 838 | #### 发送同步消息 839 | 840 | 上面的快速入门就是发送同步消息,**发送过后就会有一个返回值(SEND_OK)**,也就是mq服务器接收到消息后返回的一个确认,这种方式非常安全,但是性能上并没有这么高,而且在mq集群中,也是要等到所有的从机都复制了消息以后才会返回,所以针对重要的消息可以选择这种方式 841 | 842 | ![img](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308022124039.jpg) 843 | 844 | 845 | 846 | #### 发送异步消息 847 | 848 | 异步消息通常用在对响应时间敏感的业务场景,即发送端不能容忍长时间地等待Broker的响应。发送完以后会有一个异步消息通知 849 | 850 | ```java 851 | @Test 852 | public void syncProducer() throws Exception { 853 | // 创建默认的生产者 854 | DefaultMQProducer producer = new DefaultMQProducer(MqConstant.PRODUCER_GROUP+ "_sync"); 855 | // 设置nameServer地址 856 | producer.setNamesrvAddr(MqConstant.NAME_SRV_ADDR); 857 | // 启动实例 858 | producer.start(); 859 | Message msg = new Message("TopicSyncTest", ("异步消息").getBytes()); 860 | producer.send(msg, new SendCallback() { 861 | @Override 862 | public void onSuccess(SendResult sendResult) { 863 | log.info("发送成功 (后执行)"); 864 | } 865 | @Override 866 | public void onException(Throwable e) { 867 | log.info("发送失败 (后执行)"); 868 | } 869 | }); 870 | log.info("先执行"); 871 | // 挂起jvm 因为回调是异步的不然测试不出来 872 | System.in.read(); 873 | // 关闭实例 874 | producer.shutdown(); 875 | } 876 | ``` 877 | 878 | #### 发送单向消息 879 | 880 | 这种方式主要用在不关心发送结果的场景,这种方式吞吐量很大,但是存在消息丢失的风险,例如日志信息的发送 881 | 882 | ```java 883 | // 发送单向消息 884 | producer.sendOneway(msg); 885 | ``` 886 | 887 | 888 | 889 | #### 发送延迟消息 890 | 891 | 消息放入mq后,过一段时间,才会被监听到,然后消费 892 | 893 | 比如下订单业务,提交了一个订单就可以发送一个延时消息,15min后去检查这个订单的状态,如果还是未付款就取消订单释放库存。 894 | 895 | **DelayLevel** 896 | 897 | ```java 898 | Message msg = new Message("TopicTest", ("延迟消息").getBytes()); 899 | // 给这个消息设定一个延迟等级 900 | // messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h 901 | msg.setDelayTimeLevel(3); 902 | // 发送单向消息 903 | producer.send(msg); 904 | 905 | ``` 906 | 907 | ==注意== 908 | 909 | RocketMQ支持以下几个固定的延时等级,等级1就对应1s,以此类推,最高支持2h延迟 910 | 911 | private String messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h"; 912 | 913 | | 投递等级(delay level) | 延迟时间 | 投递等级(delay level) | 延迟时间 | 914 | | ----------------------- | -------- | ----------------------- | -------- | 915 | | 1 | 1s | 10 | 6min | 916 | | 2 | 5s | 11 | 7min | 917 | | 3 | 10s | 12 | 8min | 918 | | 4 | 30s | 13 | 9min | 919 | | 5 | 1min | 14 | 10min | 920 | | 6 | 2min | 15 | 20min | 921 | | 7 | 3min | 16 | 30min | 922 | | 8 | 4min | 17 | 1h | 923 | | 9 | 5min | 18 | 2h | 924 | 925 | **修改延时级别** 926 | 927 | RocketMQ的延迟等级可以进行修改,以满足自己的业务需求,可以修改/添加新的level。具体见[该文章]([RocketMQ进阶-延时消息 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/142441996)) 928 | 929 | 同时5.0支持使用时间戳来设置延迟时间[定时/延时消息 | RocketMQ (apache.org)](https://rocketmq.apache.org/zh/docs/featureBehavior/02delaymessage) 930 | 931 | #### 发送批量消息 932 | 933 | 可以一次性发送一组消息,那么这一组消息会被当做一个消息消费 934 | 935 | ```java 936 | List msgs = Arrays.asList( 937 | new Message("TopicTest", "我是一组消息的A消息".getBytes()), 938 | new Message("TopicTest", "我是一组消息的B消息".getBytes()), 939 | new Message("TopicTest", "我是一组消息的C消息".getBytes()) 940 | 941 | ); 942 | SendResult send = producer.send(msgs); 943 | 944 | ``` 945 | 946 | 947 | 948 | #### 发送顺序消息 949 | 950 | 顺序消息是一种特殊类型的消息,可以保证按照发送的顺序进行消费,从而保证了消息的有序性。 951 | 952 | 在 RocketMQ 中,保证消息顺序发送的关键是要将相关的消息发送到同一个队列中,并且消费者按照队列的顺序来消费消息 953 | 954 | 以下是实现顺序消息的步骤: 955 | 956 | 1. 创建一个指定顺序的 MessageQueueSelector。 在发送消息时,你需要指定一个 MessageQueueSelector 来选择目标消息队列。该 Selector 将根据某种规则将相关的消息发送到同一个队列中,保证了消息的顺序性。 957 | 2. 设置 MessageQueueSelector 选择消息队列的逻辑。 在实现 MessageQueueSelector 接口的 select 方法中,你需要编写逻辑来选择目标队列。可以根据消息的某个属性或者业务关联来确定消息应该发送到哪个队列。 958 | 3. 发送消息时使用 MessageQueueSelector。 在发送消息时,使用 producer.send(msg, selector, orderId) 方法来指定消息发送的队列。其中,selector 参数即为你实现的 MessageQueueSelector 接口的实例,orderId 是一个标识消息顺序的参数。 959 | 960 | ```java 961 | String[] tags = new String[] {"TagA", "TagB", "TagC", "TagD", "TagE"}; 962 | for (int i = 0; i < 100; i++) { 963 | int orderId = i % 10; 964 | Message msg = 965 | new Message("TopicTest", tags[i % tags.length], "KEY" + i, 966 | ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET)); 967 | SendResult sendResult = producer.send(msg, new MessageQueueSelector() { 968 | @Override 969 | public MessageQueue select(List mqs, Message msg, Object arg) { 970 | // 当前主题有多少个队列 971 | int queueNumber = mqs.size(); 972 | // 这个arg就是后面传入的 orderId 973 | Integer id = (Integer) arg; 974 | // 用这个值去%队列的个数得到一个队列 975 | int index = id % queueNumber 976 | // 返回选择的这个队列即可 ,那么相同的orderId 就会被放在相同的队列里 实现First In, First //Out了 977 | return mqs.get(index); 978 | } 979 | }, orderId); 980 | ``` 981 | 982 | **消费者的监听 MessageListenerOrderly如下** 983 | 984 | ```java 985 | // 注册一个消费监听 MessageListenerOrderly 是顺序消费 单线程消费 986 | consumer.registerMessageListener(new MessageListenerOrderly() { 987 | @Override 988 | public ConsumeOrderlyStatus consumeMessage(List msgs, ConsumeOrderlyContext context) { 989 | MessageExt messageExt = msgs.get(0); 990 | System.out.println(new String(messageExt.getBody())); 991 | return ConsumeOrderlyStatus.SUCCESS; 992 | } 993 | }); 994 | 995 | ``` 996 | 997 | 998 | 999 | 1000 | 1001 | ## 5.SpringBoot集成RocketMQ 1002 | 1003 | 1004 | 1005 | ### 搭建rocketmq-producer(消息生产者)模块 1006 | 1007 | ![image-20230803004733801](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308030047989.png) 1008 | 1009 | 1010 | 1011 | ![image-20230803004824128](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308030048242.png) 1012 | 1013 | #### 完整的依赖 1014 | 1015 | ```xml 1016 | 1017 | 1018 | org.springframework.boot 1019 | spring-boot-starter-web 1020 | 1021 | 1022 | 1023 | org.springframework.boot 1024 | spring-boot-devtools 1025 | runtime 1026 | true 1027 | 1028 | 1029 | org.springframework.boot 1030 | spring-boot-configuration-processor 1031 | true 1032 | 1033 | 1034 | org.projectlombok 1035 | lombok 1036 | true 1037 | 1038 | 1039 | org.springframework.boot 1040 | spring-boot-starter-test 1041 | test 1042 | 1043 | 1044 | 1045 | org.apache.rocketmq 1046 | rocketmq-spring-boot-starter 1047 | 2.1.1 1048 | 1049 | 1050 | 1051 | 1052 | ``` 1053 | 1054 | #### 修改配置文件application.yml 1055 | 1056 | ```yaml 1057 | spring: 1058 | application: 1059 | name: rocketmq-producer 1060 | rocketmq: 1061 | name-server: ip:9876 # rocketMq的nameServer地址 1062 | producer: 1063 | group: boot-test-producer-group # 生产者组别 1064 | send-message-timeout: 3000 # 消息发送的超时时间 1065 | retry-times-when-send-async-failed: 2 # 异步消息发送失败重试次数 1066 | max-message-size: 4194304 # 消息的最大长度 1067 | 1068 | ``` 1069 | 1070 | #### 添加测试类的内容: 1071 | 1072 | ```java 1073 | /** 1074 | * 注入rocketMQTemplate,我们使用它来操作mq 1075 | */ 1076 | @Autowired 1077 | private RocketMQTemplate rocketMQTemplate; 1078 | 1079 | /** 1080 | * 测试发送简单的消息 1081 | * @throws Exception 1082 | */ 1083 | @Test 1084 | public void testSimpleMsg() throws Exception { 1085 | // boot-test是topic,我是一个简单的消息是消息内容 1086 | SendResult sendResult = rocketMQTemplate.syncSend("boot-test", "我是一个简单的消息"); 1087 | // 拿到消息的发送状态 1088 | log.info(String.valueOf(sendResult.getSendStatus())); 1089 | // 拿到消息的id 1090 | log.info(sendResult.getMsgId()); 1091 | } 1092 | ``` 1093 | 1094 | 1095 | 1096 | #### 执行后,可得到: 1097 | 1098 | ![image-20230803013843291](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308030138389.png) 1099 | 1100 | ![image-20230803014006848](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308030140939.png) 1101 | 1102 | 1103 | 1104 | ### 同理创建消费者模块 1105 | 1106 | #### 修改配置文件application.yml 1107 | 1108 | ```yaml 1109 | spring: 1110 | application: 1111 | name: rocketmq-consumer 1112 | rocketmq: 1113 | name-server: 47.115.209.249:9876 # rocketMq?nameServer?? 1114 | ``` 1115 | 1116 | #### 添加测试类的内容: 1117 | 1118 | ```java 1119 | import lombok.extern.slf4j.Slf4j; 1120 | import org.apache.rocketmq.spring.annotation.MessageModel; 1121 | import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; 1122 | import org.apache.rocketmq.spring.core.RocketMQListener; 1123 | import org.springframework.stereotype.Component; 1124 | 1125 | /** 1126 | * 创建一个简单消息的监听 1127 | * 1.类上添加注解@Component和@RocketMQMessageListener 1128 | * @RocketMQMessageListener(topic = "powernode", consumerGroup = "powernode-group") 1129 | * topic指定消费的主题,consumerGroup指定消费组,一个主题可以有多个消费者组,一个消息可以被多个不同的组的消费者都消费 1130 | * 2.实现RocketMQListener接口,注意泛型的使用,可以为具体的类型,如果想拿到消息 1131 | * 的其他参数可以写成MessageExt 1132 | */ 1133 | @Component 1134 | @RocketMQMessageListener(topic = "boot-test", consumerGroup = "boot-test-consumer-group",messageModel = MessageModel.CLUSTERING) 1135 | @Slf4j 1136 | public class SimpleMsgListener implements RocketMQListener { 1137 | @Override 1138 | public void onMessage(String s) { 1139 | log.info("接收到消息:{}",s); 1140 | } 1141 | } 1142 | ``` 1143 | 1144 | #### 执行后,可得到: 1145 | 1146 | ![image-20230803014921527](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308030149615.png) 1147 | 1148 | ![image-20230803014943326](https://gitee.com/poldroc/typora-drawing-bed01/raw/master/imgs/202308030149401.png) 1149 | 1150 | 1151 | 1152 | 1153 | 1154 | ## 6.最后 1155 | 1156 | 从文章整体没有涉及太深入的一些机制和原理的讲解,因此仅作为入门学习 1157 | -------------------------------------------------------------------------------- /etc/blog/SSO.md: -------------------------------------------------------------------------------- 1 | # SSO 2 | 3 | ## 什么是SSO 4 | 5 | 单点登录: Single Sign On (简称**SSO**) 6 | 7 | 我们最初学习的时候,一般都是单机系统,也就是说所有的功能全部写在同一个系统上。 8 | 9 | 而后来,我们为了提高系统性能和可维护性,并且可以合理利用资源、降低耦合性,我们学习会**分布式**,把单个系统拆分为多个模块。 10 | 11 | ![image-20230714134349032](https://gitee.com/sky-dog/note/raw/master/img/202307141343147.png) 12 | 13 | 这时候单点登录就出现了作用,我们这里以阿里的天猫和淘宝为例: 14 | 15 | 我们可以很明显的知道这是两个系统,但是当你登录了天猫的时候,淘宝也会自动登录 16 | 17 | image-20230714134504492 18 | 19 | 简单来说,单点登录就是**在多个系统中,用户只需一次登录,各个系统即可感知到该用户已经登录**。这就是单点登录的意义。 20 | 21 | 而单点登录的应用,我们在很多系统中都能见到。 22 | 23 | ## SSO原理 24 | 25 | 在SSO体系中,主要包括三部分: 26 | 27 | * User (多个,一般指浏览器等客户端应用) 28 | * Web服务 (多个,一般指你开发的web接口服务等) 29 | * SSO 认证中心 (1个,专门来解决单点登录问题) 30 | 31 | 而SSO的实现基本核心原则如下: 32 | 33 | * 所有的登录都在 SSO 认证中心进行 34 | * SSO 认证中心通过一些方法来告诉 Web 应用当前访问用户究竟是不是已通过认证的用户 35 | * SSO 认证中心和所有的 Web 应用建立一种信任关系, SSO 认证中心对用户身份正确性的判断会通过某种方法告之 Web 应用,而且判断结果必须被 Web 应用信任。 36 | 37 | ### 登录 38 | 39 | 上面介绍我们知道,在SSO中有一个独立的认证中心,只有认证中心能接受用户的用户名密码等安全信息,其他系统不提供登录入口,只接受认证中心的间接授权。 40 | 41 | 那其他的系统如何访问受保护的资源? 42 | 43 | 这里就是通过认证中心间接授权通过token令牌来实现,当SSO验证了用户信息的正确性后,就会创建授权token,在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌,即得到了授权,可以借此创建局部会话,局部会话登录方式与单系统的登录方式相同。 44 | 45 | ![image-20230714135639832](https://gitee.com/sky-dog/note/raw/master/img/202307141356931.png) 46 | 47 | 大部分的SSO系统大致这样的一个流程: 48 | 49 | 这里我们举个例子,比如我们连接福大的`校园网`之后,我们访问某个网站,这时候我们校园网会给你重定向到一个`福大统一认证中心` 50 | 51 | (这个就可以理解为是SSO的服务),当我们在认证中心登录帐号之后,用户与认证中心会建立全局绘画(一份生成token,写道cookie中,保留在浏览器)。然后认证在想会**重定向**回你原本访问的服务,重定向的地址大概可能会是这个样子: `www.bilibili.com?token=balabalabalbalbablablabla`,你访问的系统就会去找sso中心去验证token是否正确。 52 | 53 | (上述例子不一定准确,仅供理解参考) 54 | 55 | ### 注销 56 | 57 | 既然有登陆那么就自然有注销,单点登录也要单点注销,在一个子系统中注销,所有子系统的会话都将被销毁。原理图如下: 58 | 59 | ![image-20230714140509685](https://gitee.com/sky-dog/note/raw/master/img/202307141405751.png) 60 | 61 | SSO认证中心一直监听全局会话的状态,一旦全局会话销毁,监听器将通知所有注册系统执行注销操作 62 | 63 | 同样的我们也来分析一下具体的流程: 64 | 65 | * 用户向系统1发起注销请求 66 | * 系统1根据用户与系统1建立的session拿到token,向sso提出注销请求 67 | * sso验证token有效,销毁全局session,同时拿到所有使用这个token的系统地址 68 | * sso向所有的系统发起注销请求 69 | * 各个系统注销,销毁局部绘画 70 | * sso重定向到登录界面 71 | 72 | ## CAS原理 73 | 74 | CAS,CAS全称为Central Authentication Service即**中央认证服务**,是一个企业多语言单点登录的解决方案,并努力去成为一个身份验证和授权需求的综合平台。 75 | 76 | CAS系统主要分成两部分: 77 | 78 | * Cas Server 79 | * 负责完成对用户的认证工作,需要独立部署,Cas Server会处理用户名/密码等凭证(Credentials) 80 | * Cas Client 81 | * 负责处理对客户端受保护资源的访问请求(就是用户对需要登录才能访问的请求),需要对请求方进行身份认证时,重定向到Cas Server进行认证。(原则上,Cas Client 不应该再接收任何有关用户名的账号或密码等Credentials) 82 | 83 | ### CAS协议 84 | 85 | CAS协议是一个简单而强大的基于票据的协议,它涉及一个或多个客户端和一台服务器。即在CAS中,通过**TGT**(Ticket Granting Ticket)来获取 **ST**(Service Ticket),通过ST来访问具体服务。 86 | 87 | 其中主要的关键概念: 88 | 89 | * TGT(Ticket Granting Ticket)是存储在TGCcookie中的代表用户的SSO会话。 90 | * 该ST(Service Ticket),作为参数在GET方法的URL中,代表由CAS服务器授予访问CASified应用程序(包含CAS客户端的应用程序)具体用户的权限。 91 | 92 | #### 基本协议模式 93 | 94 | 结合官方的流程图,我们可以知道,CAS中的单点登录流程: 95 | 96 | ![image-20230714141522403](https://gitee.com/sky-dog/note/raw/master/img/202307141415490.png) 97 | 98 | 我们可以发现这里的流程与上面SSO执行的基本是一致,只是CAS协议流程图中,更加清楚的指定了我们在访问过程当中的各种情况,在SSO中令牌也是我们在CAS中的ST(Service Ticket)。 99 | 100 | 首先用户访问受保护的资源,权限没有认证,所以会把请求的URL以参数跳转到CAS认证中心,CAS认证中心发现没有SSO session,所以弹出登录页面,输入用户信息,提交到CAS认证中心进行信息的认证,如果信息正确,CAS认证中心就会创建一个SSO session和CASTGC cookie,这个CASTGC cookie包含了TGT,而用户session则以TGT为key创建,同时服务端会分发一个ST返回给用户。用户拿到了ST后,访问带参数ST的资源地址,同时应用将ST发送给CAS认证中心,CAS认证中心对ST进行校验,同时判断相应的cookie(包含TGT)是否正确(通过先前设定的key),判断ST是否是有效的,结果会返回一个包含成功信息的XML给应用。应用在建立相应的session和cookie跳转到浏览器,用户再通过浏览器带cookie去应用访问受保护的资源地址,cookie和后端session验证成功便可以成功访问到信息。 101 | 102 | 第二次访问应用时,浏览器就会携带相应的cookie信息,后台session验证用户是否登录,与一般单系统应用登录模式一样。 103 | 104 | 当我们访问其他的应用,与前面的步骤也是基本相同,首先用户访问受保护的资源,跳转回浏览器,浏览器含有先前登录的CASTGC cookie,CASTGC cookie包含了TGT并发送到CAS认证中心,CAS认证中心校验TGT是否有效,如果有效分发浏览器一个带ST参数的资源地址URL,应用程序拿到ST后,再发送给CAS认证中心,如果认证了ST有效后,结果会返回一个包含成功信息的XML给应用。同样的步骤,应用在建立相应的session和cookie跳转到浏览器,用户再通过浏览器带cookie去应用访问受保护的资源地址,验证session成功便可以成功访问到信息。 -------------------------------------------------------------------------------- /etc/blog/docker-compose简易部署.md: -------------------------------------------------------------------------------- 1 | ## docker compose 简易编排部署 2 | 3 | 以springboot+mysql+redis作为示例参考 4 | 5 | ps: 6 | 7 | 1. 以下操作均为**简易版**部署,具体开发以需求为准,文档仅供参考 8 | 2. 本文档仅留作记录(传承的艺术防止忘记命令),故具体操作细节没有补充,**使用前请保证自身能流畅使用docker** 9 | 10 | *** 11 | 12 | ### 文件前期准备 13 | 14 | 1. docker 安装 15 | 16 | 2. docker compose安装 17 | 18 | 3. 项目本体(springboot + redis + mysql) 19 | 20 | 4. maven打包项目为jar 21 | 22 | #### maven打包要点: 23 | 24 | 1. 注意mysql和redis连接地址需要更改为底下docker-compose.yml文件中的对应容器名称(container_name) 25 | 26 | 这边不知道为什么的需要复习docker network相关知识,docker compose启动容器会默认创建新network并将所有编排容器连接到此网络中使得能够相互访问,(也可以自己在yml里面自定义网络,更安全可控) 27 | 28 | 2. 注意服务端需要暴露的端口号 29 | 30 | 31 | 32 | ### 编写docerfile构建springboot镜像 33 | 34 | 部署步骤: 35 | 36 | 1. 例如创建feige文件夹,mkdir feige 37 | 1. cd feige 38 | 1. mkdir feigeapp 39 | 40 | feige文件夹中要有docker-compose.yml 和 feigeapp 文件夹 41 | 42 | feigeapp文件夹中编写dockerfile并将jar包放在此文件夹中 43 | 44 | ```do 45 | # Docker image for springboot file run 46 | # VERSION 0.0.1 47 | # Author: 48 | # 基础镜像使用java 49 | FROM openjdk:8 50 | # 作者 51 | MAINTAINER feige 52 | # VOLUME 指定了临时文件目录为/tmp。 53 | # 其效果是在主机 /var/lib/docker 目录下创建了一个临时文件,并链接到容器的/tmp 54 | VOLUME /tmp 55 | # 将jar包添加到容器中并更名为xx.jar 56 | ADD demo.jar test.jar 57 | # 运行jar包 58 | RUN bash -c 'touch /test.jar' 59 | # 指定docker容器启动时执行的命令,格式: 60 | # ENTRYPOINT ["executable", "param1","param2"...] 61 | # 例:指定docker容器启动时运行jar包 62 | # ENTRYPOINT ["java", "-jar","/mall-tiny-docker-file.jar"] 63 | ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/test.jar"] 64 | 65 | ``` 66 | 67 | 执行docker build . -t {命名}:{tag} 68 | 69 | 例:docker build . -t feige-demo:v1.0 70 | 71 | 到此镜像打包完成,可以自行选择上传阿里云或者docker云社区 72 | 73 | ### docker-compose编排部署所有容器 74 | 75 | docker-compose.yml内容(大致) 76 | 77 | ``` dockerfile 78 | #compose版本跟docker版本的对应关系 79 | version: "3" 80 | 81 | services: 82 |  #指定服务名称 83 |  mysql: 84 |    #指定服务使用的镜像 85 |    image: mysql:8.0.31 86 |    #指定容器名称 87 |    container_name: feigemysql 88 |    restart: always 89 |    #指定服务运行的端口 90 |    ports : 91 |      - "3306:3306" 92 |    #指定容器的环境变量 93 |    environment: 94 | #数据库密码 95 |      - MYSQL_ROOT_PASSWORD=123456 96 |        #创建的库 97 |      - MYSQL_DATABASE=feigemysql 98 |      #允许多IP连接数据库 99 |      - MYSQL_ROOT_HOST=%   100 |     101 |  #指定服务名称 102 |  redis: 103 |    image: redis 104 |    #指定容器名称 105 |    container_name: feigeredis 106 |    ports: 107 |      - "6379:6379" 108 |   #设置redis密码 109 |    command: 110 | --requirepass "xxxxx" 111 | #这一行使得只有container内的root有真正的root权限 112 | #privileged: true 113 | 114 |  #指定服务名称 115 |  feigeapp: 116 | # 镜像名:版本 117 | image: feige-demo:v1 118 |    container_name: feige-demo 119 |    #restart: always 120 |    #指定服务运行的端口 121 |    ports: 122 |      - "8080:8080" 123 |    #启动时,要覆盖的环境变量配置 124 |    environment: 125 |   #数据库IP 126 |      - DATABASE_HOST=mysql 127 |      #数据库用户名 128 |      - DATABASE_USER=root 129 |      #数据库密码 130 |      - DATABASE_PASSWORD=123456 131 |      #初始化的数据库 132 |      - DATABASE_NAME=feigemysql 133 |      #数据库端口 134 |      - DATABASE_PORT=3306 135 |      #redis的IP 136 |      - REDIS_HOST=redis 137 |      #redis的端口 138 |      - REDIS_PORT=6379 139 |    #依赖的服务 140 |    depends_on: 141 |     #服务名称 142 |      - mysql 143 |      - redis 144 | ``` 145 | 146 | ps: 147 | 148 | 1. 示例中mysql没有使用volume挂载,挂载时需要宿主机文件以及容器中指定文件两个目录(注意目录必须存在且路径正确)否则无法启动 149 | 2. 当指定了**外部配置文件**与外部存储路径时(没有指定的话,不需要挂载mysql-files),也需要指定 /var/lib/mysql-files的外部目录,否则导致闪退问题 150 | 2. 端口号映射关系: {宿主机映射端口}:{docker容器端口} 151 | 152 | 153 | 154 | ### 一些常用命令(用于debug) 155 | 156 | ```do 157 | #获取容器日志 158 | docker logs {container_id} 159 | 160 | #查看容器中进程信息ps 161 | docker top {container_id} 162 | 163 | #进入当前容器后开启一个新终端,可以在里面操作 164 | docker exec 165 | 166 | #管理网络 167 | docker network {--help} 168 | ``` 169 | 170 | -------------------------------------------------------------------------------- /etc/blog/gRPC.md: -------------------------------------------------------------------------------- 1 | ## GRPC介绍 2 | 3 | > [gRPC官网](https://grpc.io/) 4 | > 5 | > [gRPC 官方文档中文版_V1.0 (oschina.net)](http://doc.oschina.net/grpc) 6 | 7 | ### RPC协议介绍 8 | 9 | RPC 全称为 Remote Procedure Call , 远程过程调用。这是一种协议,是用来屏蔽分布式计算中的各种调用细节,使你能够像调用本地方法一样,直接调用一个远程的函数。可以理解为一种规范。 10 | 11 | 下面简单说明一下客户端与服务端的沟通过程,以此来引出rpc的作用 12 | 13 | **客户端 与 服务器 沟通的过程**: 14 | 15 | * 客户端 发送 数据 (字节流) 16 | * 服务器 接受并解析。根据**约定**知道需要执行上面。把结果返回给客户 17 | 18 | RPC: 19 | 20 | * 封装上述过程,使操作更加优化便捷 21 | * 使用大家都认可的协议,使其规范化 22 | * 做成一些框架,直接或间接产生利益 23 | 24 | ### gRPC框架介绍 25 | 26 | ![image-20230120131739507](https://gitee.com/sky-dog/note/raw/master/img/202301201331160.png) 27 | 28 | ​ 而gRPC是一种开源的,高性能的通用RPC框架,能够跨平台支持多种语言。(隔壁Kitex目前还只能支持unix平台) 29 | 30 | ​ 在gRPC中,我们称调用方为client,被调用方为server。跟其他RPC框架一样,gRPC也是基于"服务定义"的思想。简单的来说,就是我们通过某种方式来描述一个服务,这种描述方式是语言无关的。在这个"服务定义"的过程种,我们描述了我们提供的服务名是什么,有哪些方法开源被调用,有什么样的入参,有什么样的回参 31 | 32 | ​ 而就是说,在定义好了这些服务、方法之后,**gRPC会频闭底层的细节,client只需要直接调用定义好的方法,就能够拿到预期返回的结果。**对于server端来说,只需要实现我们定义的方法。同样的,gRPC也会帮助我们屏蔽底层的细节,我们只需要实现所定义方法的具体逻辑即可。 33 | 34 | ​ 你可以发现,在上面的描述过程种,所谓的"服务定义",就跟定义接口是类似的。我更愿意理解为是一种"约定",双方规定好接口,然后server实现接口,client调用接口,至于其他的细节就交给gRPC管理 35 | 36 | ​ 此外,gRPC还是语言无关的,我们开源使用C++开发服务端,使用Golang、Java等作为客户端来调用服务器的接口。为了实现这一点,我们在"服务定义"和在编码解码过程中,同样应该做到语言无关。 37 | 38 | ![image-20230120133428187](https://gitee.com/sky-dog/note/raw/master/img/202301201334224.png) 39 | 40 | ### ProtoBuf介绍 41 | 42 | ​ 因此,gRPC使用了Protocol Buffss(谷歌开发的一套成熟的数据结构序列化机制) 43 | 44 | ​ 我们可以把他当成一个代码生成工具以及序列化工具。这个工具可以把我们定义的方法,转换成特定语言的代码。比如你定义了一种类型的参数,他会帮你转换成Golang种的结构体,你定义的方法,体会帮你转换成func函数。此外,在发送请求和接受响应的时候,这个工具还会完成对应的编码和解码工作,将你即将发送的数据编码成gRPC能够传输的形式,又或者将即将接受到的数据解码成编程语言所能理解的数据格式。 45 | 46 | ​ 可能有些同学对序列化和反序列化的概念不太理解,下面简单解释一下这两个概念。 47 | 48 | * 序列化: 将数据结构或对象转换成二进制串的过程 49 | * 反序列化: 将在序列化过程中产生的二进制串转换成数据结构或对象的过程 50 | 51 | 52 | 53 | ​ 而protobuf是谷歌开源的一种数据格式,适合高性能,对想要速度有要求的数据传输场景。因为protobuf是二进制数据格式,需要编码和解码。数据本身不具有可读性。因此只能反序列化之后得到真正可读的数据。 54 | 55 | ​ 相比于其他数据结构,他有着这样几点优势: 56 | 57 | * 序列化后体积相比Json和XML很小,更适合网络传输 58 | * 支持跨平台多语言 59 | * 消息格式升级和兼容性好 60 | * 序列化和反序列化速度很快 61 | 62 | 63 | 64 | ## Proto 65 | 66 | ### 安装ProtoBuf 67 | 68 | 1、下载protocol buffers [Releases · protocolbuffers/protobuf (github.com)](https://github.com/protocolbuffers/protobuf/releases/) 69 | 70 | * Protocol buffers,通常称为 Protobuf,是Google开发的一种协议,可以对结构数据进行序列化和反序列化操作,在网络通信中很有用 71 | * 我们下载对应操作系统的zip文件,解压,并将bin目录配置到环境变量中即可 72 | * 最后在cmd或bash中输入`protoc`命令查看是否安装并配置成功 73 | 74 | 2、安装`gRPC`核心库 75 | 76 | ~~~go 77 | go get google.golang.org/grpc 78 | ~~~ 79 | 80 | 3、安装protocol编译器。它开源生成各种不同语言的代码。因此,除了这个编译器,我们还需要配合各个语言的代码生成工具。对于Golang来说,这个工具是`protoc-gen-go`。 81 | 82 | ​ 这里有个小坑,`github.com/golang/protobuf/protoc-gen-go`和`google.golang.org/protobuf/cmd/protoc-gen-go`是不同的!前者是旧版本,后者是Google接管的新版本,他们的API是不同的,用于生成的命令,生成的文件都是不一样的。由于目前的`gRPC-go`源码中的example是使用后者的生成方式,所以我们也采用google版本。 83 | 84 | 下面我们通过命令安装两个库(因为这些文件在安装grpc时以及下载了,这里只需要install即可) 85 | 86 | ~~~go 87 | go install google.golang.org/protobuf/cmd/protoc-gen-go@latest 88 | go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest 89 | ~~~ 90 | 91 | ### Proto文件编写 92 | 93 | 下面简单写一个小demo,新建两个文件夹,分别作为客户端和服务端。 94 | 95 | ![image-20230120174308269](https://gitee.com/sky-dog/note/raw/master/img/202301201743327.png) 96 | 97 | proto文件内容如下(可以当作模板记下来) 98 | 99 | ~~~protobuf 100 | // 使用proto3语法 101 | syntax = "proto3"; 102 | 103 | // 生成的go文件处于哪个目录哪个包中 104 | // 这里声称在当前目录,service包中 105 | option go_package = ".;service"; 106 | 107 | // 我们需要定义一个服务,在服务中需要有一个方法,这个方法可以接收客户端参数,返回服务端响应 108 | // 其实很容易可以看出,我们定义了一个service,称为SayHello,这个服务有一个rpc方法,名为SayHello 109 | // 这个方法会发送一个HelloRequest返回一个HelloResponse 110 | service SayHello { 111 | rpc SayHello(HelloRequest) returns (HelloResponse) {} 112 | } 113 | 114 | // message关键字,可以理解为结构体 115 | // 这个比较特别的是变量后的"赋值"(这里并不是赋值,而是定义这个变量在message中的位置) 116 | message HelloRequest { 117 | string requestName = 1; 118 | // int64 age = 2; 119 | } 120 | 121 | message HelloResponse { 122 | string responseMsg = 1; 123 | } 124 | ~~~ 125 | 126 | 接下来可以通过protoc生成对应语言的代码,打开Terminal,进入proto目录,输入一些代码即可 127 | 128 | 两个命令(当作模板可以记下) 129 | 130 | ~~~protobuf 131 | protoc --go_out=. hello.proto 132 | protoc --go-grpc_out=. hello.proto 133 | 134 | protoc -I internal/service/pb internal/service/pb/*.proto --go_out=. 135 | protoc -I internal/service/pb internal/service/pb/*.proto --go-grpc_out=. 136 | ~~~ 137 | 138 | 输入完可以发现在proto目录下生成了两个文件,我们使用时只需要重写或修改其中的我们定义的方法,加上业务逻辑即可 139 | 140 | 141 | 142 | ### Proto文件介绍 143 | 144 | #### message 145 | 146 | message: protobuf 中一个定义消息类型是通过关键字 message 指定的。**消息就是需要传输的数据格式的定义** 147 | 148 | message 关键字类似于C++/Java中的class,C/Go中的struct 149 | 150 | 在消息中承载的数据分别对应每个字段,其中每个字段都有一个名字和一种类型 151 | 152 | 一个proto文件可以定义多个消息类型 153 | 154 | #### 字段规则 155 | 156 | * `required`: 消息体中必填字段。不设置会导致编码异常。**在protobuf2中使用,在protobuf3中被删去。** 157 | 158 | * `optional`: 消息体中可选字段。**protobuf3中没有了reuired,optional等说明关键字,都默认为optional。** 159 | * `repeated`: 消息体中可重复字段。重复的值的顺序会被保留,在go中重复的会被定义为切片。 160 | 161 | #### 消息号 162 | 163 | 在消息体的定义中,**每个字段都必须要有一个唯一的标识号**,标识号是[1, 2^29-1]范围内的一个整数。 164 | 165 | 形式看上去与"赋值"类似。 166 | 167 | #### 嵌套消息 168 | 169 | 可以在其他消息类型中定义、使用消息类型,在下面的例子中,person消息就定义在PersonInfo消息内 170 | 171 | ~~~protobuf 172 | message PersonInfo{ 173 | message Person{ 174 | string name = 1; 175 | int32 height = 2; 176 | repeated int32 weight = 3; 177 | } 178 | repeated Person info = 1; 179 | } 180 | ~~~ 181 | 182 | 如果要在父消息外重用这个消息类型,需要使用PersonInfo.Person的形式来使用它,如: 183 | 184 | ~~~protobuf 185 | message PersonMessage{ 186 | PersonInfo.Person info = 1; 187 | } 188 | ~~~ 189 | 190 | #### 服务定义 191 | 192 | 如果要将消息类型用在RPC系统,可以在.proto文件中定义一个RPC服务接口,protocol buffer编译器将会根据所选择的不同语言生成服务接口代码及存根。 193 | 194 | ~~~protobuf 195 | service SearchService{ 196 | # rpc 服务函数名 (参数) 返回 (返回参数) 197 | rpc Search(SearchRequest) returns (RequestResponse) 198 | } 199 | ~~~ 200 | 201 | ## 服务端编写 202 | 203 | * 创建gRPC Server对象,你可以理解为Server端的抽象对象 204 | 205 | * 将 server (其包含需要被调用的服务端接口) 注册到 gRPC Server 的内部注册中心 206 | 207 | 这样可以在接受到请求时,通过内部的服务发现,发现该服务端接口并转接进行逻辑处理 208 | 209 | * 创建 Listen,监听TCP端口 210 | 211 | * gRPC Server 开始 lis.Accept,直到 Stop 212 | 213 | 下面给出以之前写的Hello服务为例,实现以下服务端的编写 214 | 215 | ~~~go 216 | // 实现生成代码中未实现的服务 217 | // hello Server 218 | type server struct { 219 | pb.UnimplementedSayHelloServer 220 | } 221 | 222 | func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) { 223 | return &pb.HelloResponse{ResponseMsg: "hello" + req.RequestName}, nil 224 | } 225 | ~~~ 226 | 227 | 注册并开启rpc服务 228 | 229 | ~~~go 230 | func main() { 231 | // 开启端口 232 | listen, err := net.Listen("tcp", ":9090") 233 | if err != nil { 234 | panic("port open failed") 235 | } 236 | // 创建grpc服务 237 | grpcServer := grpc.NewServer() 238 | // 在grpc服务端中注册我们自己编写的服务 239 | pb.RegisterSayHelloServer(grpcServer, &server{}) 240 | // 启动服务 241 | err = grpcServer.Serve(listen) 242 | if err != nil { 243 | panic("service open failed") 244 | } 245 | } 246 | ~~~ 247 | 248 | 249 | 250 | ## 客户端编写 251 | 252 | * 创建与给定目标(服务端)的连接交互 253 | * 创建 server 的服务端对象 254 | * 发送 RPC 请求,请求同步响应,得到回调后返回响应结果 255 | * 输出响应结果 256 | 257 | 同样以刚刚的Hello服务为例,编写一下客户端侧的代码demo 258 | 259 | ~~~go 260 | func main() { 261 | // 连接服务 262 | // 第二个参数为安全配置(这里新建了空的加密配置,即不进行安全加密) 263 | conn, err := grpc.Dial("127.0.0.1:9090", grpc.WithTransportCredentials(insecure.NewCredentials())) 264 | if err != nil { 265 | panic("fail to connect server") 266 | } 267 | // 关闭grpc连接,都记得关闭! 268 | defer conn.Close() 269 | // 与服务端建立连接 270 | client := pb.NewSayHelloClient(conn) 271 | resp, _ := client.SayHello(context.Background(), 272 | &pb.HelloRequest{ 273 | RequestName: "二火", 274 | }) 275 | fmt.Println(resp.GetResponseMsg()) 276 | } 277 | ~~~ 278 | 279 | 280 | 281 | 我们分别启动服务端和客户端,可以看见: 282 | 283 | ![image-20230120203203783](https://gitee.com/sky-dog/note/raw/master/img/202301202032871.png) 284 | 285 | 286 | 287 | ## 认证-安全传输 288 | 289 | ### 介绍 290 | 291 | gRPC 是一个典型的 C/S 模型,需要开发客户端和服务端,客户端与服务端需要达成协议,使用某一个确认的传输协议来传输数据,**gRPC通常默认是使用protobuf来作为传输协议**,当然也是可以使用其他自定义的。 292 | 293 | ![image-20230120203523973](https://gitee.com/sky-dog/note/raw/master/img/202301202035083.png) 294 | 295 | ​ 那么,客户端与服务端要通信之前,客户端如何知道自己的数据是发给哪一个明确的服务端的呢?反过来,服务端是否也需要有一种方式来弄清楚自己的数据要返回给谁呢? 296 | 297 | ​ 那么就不得不提到gRPC的认证 298 | 299 | ​ 此处说到的认证,不是用户的身份认证,而是指多个server和多个client之间,如何识别对方是谁,并且可以安全地进行数据传输 300 | 301 | * SSL / TLS 认证 (采用http2协议) 302 | * 基于Token的认证方式 (基于安全链接) 303 | * 不采用任何措施的连接,这是不安全的连接 (默认采用http1) 304 | * 自定义的身份认证 305 | 306 | 客户端和服务端之间调用,我们可以通过加入证书的方式,实现调用的安全性 307 | 308 | TLS (Transport Layer Security , 安全传输层),TLS是建立在传输层TCP协议之上的协议,服务于应用层,它的前身是SSL (Secure Socket Layer , 安全套接字层),它实现了将应用层的报文进行加密后再交由TCP进行传输的功能。 309 | 310 | TLS协议主要解决如下三个网络安全问题。 311 | 312 | * 保密(message privacy),保密通过加密encryption实现,所有信息都加密传输,第三方无法嗅探 313 | * 完整性(message integrity),通过MAC校验机制,一旦被篡改,通信双方会立刻发现 314 | * 认证(mutual authentication),双方认证,双方都可以配备证书,防止身份被冒充 315 | 316 | **生产环境可以购买证书或使用一些平台发放的免费证书** 317 | 318 | 下面解释一些有关证书的概念: 319 | 320 | * key: 服务器上的私钥文件,用于对发送给客户端数据的加密,以及对客户端接受到数据的解密。 321 | * csr: 证书签名请求文件,用于提交给证书颁发机构(CA), 322 | * crt: 由证书办法机构(CA)签名后的证书,或是开发者自签名的证书,包含证书持有人的信息,持有者的公钥,以及签署者的签名等信息。 323 | * pem: 是基于Base64编码的证书格式,扩展名包括pem、crt、cer 324 | 325 | ### SSL / TLS 认证 326 | 327 | 首先通过`openSSL`生成证书和私钥 328 | 329 | * [openssl官网](https://www.openssl.org/source)下载 330 | 331 | 其他人做的[便捷版安装包](http://slproweb.com/products/Win32OpenSSL.html) 332 | 333 | * 我们使用便捷版安装包,一直下一步即可 334 | * 将bin目录配置到环境变量 335 | * 命令行测试 openssl 336 | 337 | ~~~shell 338 | # 1、生成私钥 339 | openssl genrsa -out server.key 2048 340 | 341 | # 2、生成证书 全部回车、可以不填 342 | openssl req -new -x509 -key server.key -out server.crt -days 36500 343 | # 国家名称 344 | Country Name (2 letter code) [AU]:CN 345 | # 省名称 346 | State or Province Name (full name) [Some-State]:Fujian 347 | # 城市名称 348 | Locality Name (eg, city) []:Fuzhou 349 | # 公司组织名称 350 | Organization Name (eg, company) [Internet widgits Pty Ltd]: ByteDance 351 | # 部门名称 352 | Organizational Unit Name (eg, section) []:go 353 | # 服务器or网站名称 354 | Organizational Unit Name (e.g. server FQDN or YOUR name) []:dousheng 355 | # 邮件 356 | Email Address []:362664609@qq.com 357 | 358 | # 3、生成csr 359 | openssl req -new -key server.key -out server.csr 360 | ~~~ 361 | 362 | ~~~shell 363 | #更改openssl.cnf (Linux是openssl.cfg) 364 | #1)从openssl/bin下将openssl.cnf复制到项目目录中 365 | #2)找到 [ CA_default ], 打开 copy_extensions = copy (解除注释即可) 366 | #3)找到 [ req ], 打开 req_extensions = v3_req # The extensions to add to a certificate request 367 | #4)找到[ v3_req ], 添加 subjectAltName = @alt_names 368 | #5)添加新的标签 [ alt_names ], 和标签字段 369 | DNS.1 = *.skydog.ltd 370 | ~~~ 371 | 372 | ~~~shell 373 | # 生成证书私钥 test.key 374 | openssl genpkey -algorithm RSA -out test.key 375 | 376 | # 通过私钥test.key生成证书请求文件test.csr(注意cfg和cnf) 377 | openssl req -new -nodes -key test.key -out test.csr -days 3650 -subj "/C=cn/OU=myorg/0=mycomp/CN=myname" -config ./openssl.cnf -extensions v3_req 378 | #test.csr是上面生成的证书请求文件。ca.crt/server.key是CA证书文件和key,用来对test.csr进行签名认证。这两个文件在第一部分生成。 379 | 380 | # 生成SAN证书 (pem) 381 | openssl x509 -req -days 365 -in test.csr -out test.pem -CA server.crt -CAkey server.key -CAcreateserial -extfile ./openssl.cnf -extensions v3_req 382 | ~~~ 383 | 384 | 385 | 386 | #### 代码中添加认证 387 | 388 | ##### 服务端 389 | 390 | ~~~// 391 | // TSL认证 392 | // 两个参数分别是 cretFile, keyFile (自签名文件, 私钥文件) 393 | creds, _ := credentials.NewServerTLSFromFile("F:\\Project\\GoProjects\\grpc-study\\key\\test.pem", 394 | "F:\\Project\\GoProjects\\grpc-study\\key\\test.key") 395 | // 并在创建gRPC服务时,加入认证 396 | grpcServer := grpc.NewServer(grpc.Creds(creds)) 397 | ~~~ 398 | 399 | ##### 客户端 400 | 401 | ~~~go 402 | creds, _ := credentials.NewClientTLSFromFile("F:\\Project\\GoProjects\\grpc-study\\key\\test.pem", 403 | "*.skydog.ltd") 404 | // 并在连接服务时请求认证 405 | conn, err := grpc.Dial("127.0.0.1:9090", grpc.WithTransportCredentials(creds)) 406 | ~~~ 407 | 408 | #### 409 | 410 | ### Token认证 411 | 412 | 我们先看一个gRPC提供给我们的一个接口,这个接口中有两个方法,接口位于credentials包下,这个接口需要客户端来实现 413 | 414 | ~~~go 415 | type PerPRCCredentials interface { 416 | // 获取元数据信息,也就是客户端提供的kv键值对,context用于控制超时与取消,uri是请求入口的uri 417 | GetRequestMetadata(ctx context.Context, uri ...string) (map[string] string, error) 418 | // 是否需要基于TLS认证进行安全传输,进入过返回值是true,则必须要上TLS验证,返回值是false则不用 419 | RequireTransportSecurity() bool 420 | } 421 | ~~~ 422 | 423 | #### 客户端代码 424 | 425 | 定义并实现自己的Token解析类 426 | 427 | ~~~go 428 | type PerPRCCredentials interface { 429 | // 获取元数据信息,也就是客户端提供的kv键值对,context用于控制超时与取消,uri是请求入口的uri 430 | GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) 431 | // 是否需要基于TLS认证进行安全传输,进入过返回值是true,则必须要上TLS验证,返回值是false则不用 432 | RequireTransportSecurity() bool 433 | } 434 | 435 | type ClientTokenAuth struct { 436 | } 437 | 438 | func (c ClientTokenAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { 439 | return map[string]string{ 440 | "appId": "SkyDog", 441 | "appKey": "114514", 442 | }, nil 443 | } 444 | func (c ClientTokenAuth) RequireTransportSecurity() bool { 445 | return false 446 | } 447 | ~~~ 448 | 449 | 在客户端中添加自定义安全配置 450 | 451 | ~~~go 452 | var opts []grpc.DialOption 453 | opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) 454 | opts = append(opts, grpc.WithPerRPCCredentials(new(ClientTokenAuth))) 455 | conn, err := grpc.Dial("127.0.0.1:9090", opts...) 456 | ~~~ 457 | 458 | #### 服务端 459 | 460 | 在服务端侧做好token校验工作 461 | 462 | ~~~go 463 | // 在业务中获取元数据信息 464 | md, ok := metadata.FromIncomingContext(ctx) 465 | if !ok { 466 | return nil, errors.New("token not found") 467 | } 468 | var appId string 469 | var appKey string 470 | if v, ok := md["appId"]; ok { 471 | appId = v[0] 472 | } 473 | if v, ok := md["appKey"]; ok { 474 | appKey = v[0] 475 | } 476 | // 用户 对应-> appId 477 | if appId != "SkyDog" || appKey != "114514" { 478 | return nil, errors.New("token invalid") 479 | } 480 | ~~~ 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | -------------------------------------------------------------------------------- /etc/blog/grpc & springboot.md: -------------------------------------------------------------------------------- 1 | # grpc SpringBoot实践 2 | 3 | ## grpc简介 4 | 5 | gRPC 是google提供的一个高性能、开源和通用的 RPC 框架,在gRPC里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务 6 | 7 | Cloud Native兴起,云端服务架构不同语言的集成能力也越来越标准化和简单化,不同语言组件间的通讯也需要统一和标准化,而这个标准的通讯协议只能是目前流行的rest或grpc,而rest则侧重于外部通讯,内部通讯首选grpc,此文主要介绍spring boot下grpc使用。 8 | 9 | 使用grpc有几点优势: 10 | 11 | ![image-20230717142920909](https://gitee.com/sky-dog/note/raw/master/img/202307171429993.png) 12 | 13 | 这边我们以golang和java为例,进行一个简单的实践; 14 | 15 | 通过本文可以学会: 16 | 17 | * 如何定于服务接口 18 | * 如何生成服务器和客户端代码 19 | * 如何继承SpringBoot和golang实现一个简单的服务端和客户端 20 | * 这边我们用Springboot作为服务端(网关层)对外提供http服务 21 | * 使用golang作为客户端接收springboot请求对数据库进行操作 22 | 23 | 24 | 25 | ## 实践目标 26 | 27 | demo需求: 一个简单的todolist服务 28 | 29 | **golang实现task模块**,对用户表实现增删改查;**springboot实现user模块**;**springboot实现网关模块**,对外部提供http服务;二者内部使用proto通信,以grpc作为**RPC(远程函数调用)**框架。 30 | 31 | ### demo结构 32 | 33 | ~~~shell 34 | . 35 | ├─grpc-demo-api 36 | │ └─src 37 | │ └─main 38 | │ ├─java 39 | │ └─resources 40 | ├─grpc-demo-gateway 41 | │ └─src 42 | │ └─main 43 | │ ├─java 44 | │ └─resources 45 | ├─grpc-demo-task 46 | │ └─src 47 | │ └─main 48 | │ ├─java 49 | │ └─resources 50 | └─grpc-demo-user 51 | └─src 52 | ~~~ 53 | 54 | ### 准备名为grpc-demo的普通maven项目 55 | 56 | 在父项目的pom中管理好spring boot、mybatis-plus和**grpc及proto**的相关依赖 57 | 58 | ~~~xml 59 | 60 | 61 | 62 | 63 | com.google.protobufd 64 | protobuf-java-util 65 | ${protobuf.version} 66 | 67 | 68 | org.apache.tomcat 69 | annotations-api 70 | 6.0.53 71 | provided 72 | 73 | 74 | com.google.errorprone 75 | error_prone_annotations 76 | 2.3.4 77 | 78 | 79 | net.devh 80 | grpc-server-spring-boot-starter 81 | 2.10.1.RELEASE 82 | 83 | 84 | io.grpc 85 | grpc-bom 86 | ${grpc.version} 87 | pom 88 | import 89 | 90 | 91 | net.devh 92 | grpc-client-spring-boot-starter 93 | 2.10.1.RELEASE 94 | 95 | 96 | 97 | org.springframework.boot 98 | spring-boot-starter-web 99 | 100 | 101 | 102 | org.springframework.boot 103 | spring-boot-starter-test 104 | test 105 | 106 | 107 | 108 | org.projectlombok 109 | lombok 110 | true 111 | 112 | 113 | 114 | mysql 115 | mysql-connector-java 116 | runtime 117 | 118 | 119 | 120 | org.mybatis.spring.boot 121 | mybatis-spring-boot-starter 122 | 2.2.2 123 | 124 | 125 | com.baomidou 126 | mybatis-plus-boot-starter 127 | 3.5.1 128 | 129 | 130 | 131 | io.springfox 132 | springfox-swagger2 133 | 2.7.0 134 | 135 | 136 | io.springfox 137 | springfox-swagger-ui 138 | 2.7.0 139 | 140 | 141 | 142 | ~~~ 143 | 144 | 在**grpc-demo-api**(存放proto源文件)中的pom添加依赖proto相关依赖和编译插件 145 | 146 | ~~~xml 147 | 148 | 149 | 150 | 151 | kr.motd.maven 152 | os-maven-plugin 153 | 1.6.2 154 | 155 | 156 | 157 | 158 | 159 | 160 | org.xolstice.maven.plugins 161 | protobuf-maven-plugin 162 | 0.6.1 163 | 164 | com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier} 165 | 166 | grpc-java 167 | io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} 168 | 169 | 170 | 171 | 172 | 173 | compile 174 | compile-custom 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | org.apache.maven.plugins 183 | maven-enforcer-plugin 184 | 1.4.1 185 | 186 | 187 | enforce 188 | 189 | enforce 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | ~~~ 202 | 203 | 载入成功后,能够在maven管理项看见proto插件,编译后可以在target目录下看见编译后的protobuf文件 204 | 205 | image-20230723164435458image-20230724102253074 206 | 207 | 接下来,简单介绍一下protobuf下生成的文件的作用: 208 | 209 | * **grpc-java** 210 | * 生成的是grpc基于java语言特性生成的服务类,封装了服务注册、服务发现等功能,提供了在proto文件中定义的服务模板 211 | * 实现服务就只需要**继承grpc-java下对应的服务类**(如图示中的UserServiceGrpc)并重写对应的方法,即可完成服务的编写 212 | * **java** 213 | * 这个目录下的文件主要是实体定义、通信相关等 214 | * model 215 | * 主要是生成了在proto中定义的实体及其构造类 216 | * service 217 | * 主要是用作grpc的服务注册和通信相关 218 | 219 | ### 实现server端 220 | 221 | #### 用户模块使用依赖 222 | 223 | ~~~xml 224 | 225 | 228 | 229 | grpc-demo 230 | com.west2 231 | 1.0 232 | 233 | 4.0.0 234 | 235 | grpc-demo-user 236 | 237 | 238 | 8 239 | 8 240 | 241 | 242 | 243 | 244 | com.west2 245 | grpc-demo-api 246 | 1.0 247 | 248 | 249 | 250 | 251 | com.google.protobuf 252 | protobuf-java-util 253 | 254 | 255 | 256 | io.grpc 257 | grpc-netty-shaded 258 | runtime 259 | 260 | 261 | 262 | io.grpc 263 | grpc-protobuf 264 | 265 | 266 | 267 | io.grpc 268 | grpc-stub 269 | 270 | 271 | 272 | io.grpc 273 | grpc-testing 274 | test 275 | 276 | 277 | 278 | com.google.errorprone 279 | error_prone_annotations 280 | 281 | 282 | 283 | 284 | net.devh 285 | grpc-server-spring-boot-starter 286 | 287 | 288 | org.springframework.boot 289 | spring-boot-starter 290 | 291 | 292 | 293 | 294 | 295 | 296 | net.devh 297 | grpc-client-spring-boot-starter 298 | 299 | 300 | org.springframework.boot 301 | spring-boot-starter 302 | 303 | 304 | 305 | 306 | 307 | 308 | com.baomidou 309 | mybatis-plus-boot-starter 310 | 311 | 312 | 313 | 314 | mysql 315 | mysql-connector-java 316 | runtime 317 | 318 | 319 | 320 | 321 | com.alibaba 322 | druid-spring-boot-starter 323 | 324 | 325 | 326 | org.slf4j 327 | slf4j-api 328 | 329 | 330 | 331 | org.projectlombok 332 | lombok 333 | 334 | 335 | 336 | org.apache.commons 337 | commons-lang3 338 | 3.7 339 | 340 | 341 | 342 | com.alibaba 343 | fastjson 344 | 345 | 346 | 347 | org.springframework.boot 348 | spring-boot-starter-test 349 | 350 | 351 | 352 | 353 | 354 | ~~~ 355 | 356 | #### 配置文件 357 | 358 | ~~~yml 359 | #net.devh.boot.grpc.server.config.GrpcServerProperties 360 | grpc: 361 | server: 362 | port: 9091 363 | in-process-name: grpc-demo-user 364 | client: 365 | #允许客户端在同一应用程序内使用以下配置连接到服务器 366 | inProcess: 367 | address: in-process:grpc-demo-user 368 | spring: 369 | application: 370 | name: grpc-demo-user 371 | datasource: 372 | url: jdbc:mysql://127.0.0.1:3306/todolist?serverTimezone=GMT%2b8 373 | username: root 374 | password: 9738faq 375 | ~~~ 376 | 377 | 378 | 379 | #### 实现样例 380 | 381 | user模块作为rpc的服务端,为网关模块(客户端)提供用户相关的服务 382 | 383 | 只要继承生成的对应服务类,并重写方法即可实现rpc通信。 384 | 385 | 以下是一个example: 386 | 387 | ```java 388 | /** 389 | * 用户服务实现类 390 | * @author 天狗 391 | */ 392 | @Slf4j 393 | @GrpcService 394 | public class UserServiceGrpcImpl extends UserServiceGrpc.UserServiceImplBase { 395 | 396 | @Resource 397 | private UserMapper userMapper; 398 | 399 | @Override 400 | public void getById(UserIdRequest request, StreamObserver responseObserver) { 401 | log.info("start getById"); 402 | User user = userMapper.selectById(request.getId()); 403 | 404 | // 构造rpc响应 405 | com.west2.demo.grpc.user.model.User.Builder respUser = com.west2.demo.grpc.user.model.User.newBuilder() 406 | .setId(user.getId()).setUsername(user.getUsername()).setPassword(user.getPassword()); 407 | UserResponse resp = UserResponse.newBuilder().setUser(respUser).setCode(200).build(); 408 | 409 | responseObserver.onNext(resp); 410 | responseObserver.onCompleted(); 411 | log.info("end getById"); 412 | } 413 | 414 | @Override 415 | public void add(UserSaveRequest request, StreamObserver responseObserver) { 416 | log.info("start add"); 417 | String id = request.getId(); 418 | User user = new User(); 419 | if (StringUtils.isEmpty(id)) { 420 | id = IdWorker.getIdStr(); 421 | } 422 | user.setId(id); 423 | user.setUsername(request.getUsername()); 424 | user.setPassword(request.getPassword()); 425 | if (0 < userMapper.insert(user)) { 426 | log.info("insert success"); 427 | user = userMapper.selectById(id); 428 | // 构造rpc响应 429 | com.west2.demo.grpc.user.model.User.Builder respUser = com.west2.demo.grpc.user.model.User.newBuilder() 430 | .setId(user.getId()) 431 | .setUsername(user.getUsername()) 432 | .setPassword(user.getPassword()); 433 | UserResponse resp = UserResponse.newBuilder().setUser(respUser).setCode(200).build(); 434 | responseObserver.onNext(resp); 435 | } 436 | responseObserver.onCompleted(); 437 | log.info("end add"); 438 | } 439 | 440 | } 441 | ``` 442 | 443 | ### 实现client端 444 | 445 | #### 网关模块使用依赖 446 | 447 | ~~~xml 448 | 449 | 452 | 453 | grpc-demo 454 | com.west2 455 | 1.0 456 | 457 | 4.0.0 458 | 459 | grpc-demo-gateway 460 | 461 | 462 | 8 463 | 8 464 | 465 | 466 | 467 | 468 | com.west2 469 | grpc-demo-api 470 | 1.0 471 | 472 | 473 | org.springframework.boot 474 | spring-boot-starter-web 475 | 2.2.6.RELEASE 476 | 477 | 478 | 479 | com.alibaba 480 | fastjson 481 | 482 | 483 | 484 | org.projectlombok 485 | lombok 486 | 487 | 488 | 489 | 490 | net.devh 491 | grpc-client-spring-boot-starter 492 | 493 | 494 | org.springframework.boot 495 | spring-boot-starter 496 | 497 | 498 | 499 | 500 | 501 | org.springframework.boot 502 | spring-boot-starter-test 503 | 504 | 505 | 506 | 507 | ~~~ 508 | 509 | #### 配置文件 510 | 511 | ~~~yml 512 | #net.devh.boot.grpc.server.config.GrpcServerProperties 513 | #grpc相关配置 514 | grpc: 515 | client: 516 | userDemoClient: # 与@GrpcClient注解相对应 517 | #禁用传输层安全(https://yidongnan.github.io/grpc-spring-boot-starter/zh-CN/client/security.html) 518 | negotiationType: PLAINTEXT 519 | #grpc服务地址配置(https://yidongnan.github.io/grpc-spring-boot-starter/zh-CN/client/configuration.html#configuration-via-properties) 520 | address: static://127.0.0.1:9091 521 | server: 522 | port: 9092 523 | spring: 524 | profiles: 525 | active: dev 526 | application: 527 | name: grpc-demo-client 528 | ~~~ 529 | 530 | 531 | 532 | #### 实现样例 533 | 534 | 网关模块作为rpc服务的客户端,接收各个服务端(如上面的user模块)传递的数据,并通过web服务以http协议将数据表达给访问网关的用户。 535 | 536 | 在这里(rpc客户端)我们只需要通过框架提供的注解 (`@GrpcClient("YourClientName")`这里的`YourClientName`由你自己在yml中配置的相对于即可) ,直接注入传递数据的对象,通过对象进行通信。 537 | 538 | 下面是一个Example: 539 | 540 | ~~~java 541 | package com.west2.controller; 542 | 543 | import com.alibaba.fastjson.JSONObject; 544 | import com.west2.demo.grpc.user.model.UserResponse; 545 | import com.west2.demo.grpc.user.model.UserSaveRequest; 546 | import com.west2.demo.grpc.user.service.UserServiceGrpc; 547 | import io.grpc.StatusRuntimeException; 548 | import lombok.extern.slf4j.Slf4j; 549 | import net.devh.boot.grpc.client.inject.GrpcClient; 550 | import org.springframework.web.bind.annotation.GetMapping; 551 | import org.springframework.web.bind.annotation.RequestParam; 552 | import org.springframework.web.bind.annotation.RestController; 553 | 554 | /** 555 | * 测试写的UserController 556 | * @author 天狗 557 | */ 558 | @RestController 559 | @Slf4j 560 | public class UserController { 561 | 562 | @GrpcClient("userDemoClient") 563 | private UserServiceGrpc.UserServiceBlockingStub blockingStub; 564 | 565 | @GetMapping(path = "/hello") 566 | public String helloGrpc(@RequestParam(value = "name", defaultValue = "world") String name) { 567 | log.info("开始访问/hello路由"); 568 | UserSaveRequest req = UserSaveRequest.newBuilder().setUsername("testuser").setPassword("114514").build(); 569 | UserResponse resp; 570 | try { 571 | resp = blockingStub.add(req); 572 | } catch (StatusRuntimeException e) { 573 | log.error("RPC Failed: " + e.getMessage(), e); 574 | throw e; 575 | } 576 | log.error("testAdd结果: " + resp.getUser()); 577 | return JSONObject.toJSONString(resp.toString()); 578 | } 579 | 580 | } 581 | ~~~ 582 | 583 | 584 | 585 | 586 | 587 | ## TODO 588 | 589 | 有关golang使用grpc并跨语言通信还没做... 590 | 591 | 以后有时间再来补罢! 592 | 593 | ## Tips 594 | 595 | 完整样例项目见github仓库:https://github.com/SkyDDDog/grpc-demo.git 596 | 597 | ## 引用 598 | 599 | > 官网grpc基础示例https://grpc.io/docs/languages/java/basics/ -------------------------------------------------------------------------------- /etc/blog/swagger使用.md: -------------------------------------------------------------------------------- 1 | # Swagger 2 | ```java 3 | @SpringBootApplication 4 | @ComponentScan(basePackages = {"com.*.*"}) 5 | public class DemoApplication { 6 | public static void main(String[] args) { 7 | SpringApplication.run(DemoApplication.class, args); 8 | } 9 | } 10 | ``` 11 | 12 | ## 版本2.0 13 | ```http 14 | http://domain:port/{context-path}/swagger-ui.html 15 | ``` 16 | 17 | ```xml 18 | 19 | io.springfox 20 | springfox-swagger-ui 21 | 2.9.2 22 | 23 | 24 | 25 | io.springfox 26 | springfox-swagger2 27 | 2.9.2 28 | 29 | 30 | 31 | 32 | com.github.xiaoymin 33 | swagger-bootstrap-ui 34 | 1.9.6 35 | 36 | ``` 37 | 38 | ```java 39 | @Configuration 40 | @EnableSwagger2 41 | public class SwaggerConfig { 42 | @Bean 43 | public Docket createRestApi(Environment environment) { 44 | return new Docket(DocumentationType.SWAGGER_2) 45 | .enable(environment.acceptsProfiles(Profiles.of("dev","test"))) //根据环境判断是否启用swagger 46 | .apiInfo(apiInfo()) 47 | .select() 48 | .group() //可以设置多个docket 49 | //扫描包 50 | .apis(RequestHandlerSelectors.basePackage("com.superdog.cloudnote.controller")) 51 | .paths(PathSelectors.any()) 52 | .build(); 53 | } 54 | 55 | private ApiInfo apiInfo() { 56 | return new ApiInfoBuilder() 57 | .title("云笔记api") 58 | .description("springboot | swagger") 59 | .version("0.0.1") 60 | .build(); 61 | } 62 | 63 | } 64 | ``` 65 | 66 | ```java 67 | @Slf4j 68 | @Configuration 69 | public class WebMvcConfig extends WebMvcConfigurationSupport { 70 | 71 | //配置swagger静态资源 72 | @Override 73 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 74 | registry.addResourceHandler("/**").addResourceLocations( 75 | "classpath:/static/"); 76 | registry.addResourceHandler("swagger-ui.html").addResourceLocations( 77 | "classpath:/META-INF/resources/"); 78 | registry.addResourceHandler("/webjars/**").addResourceLocations( 79 | "classpath:/META-INF/resources/webjars/"); 80 | super.addResourceHandlers(registry); 81 | } 82 | } 83 | ``` 84 | 85 | ## 版本3.0(目前2023.6.12与springboot3.0不完全兼容) 86 | ```http 87 | http://domain:port/{context-path}/swagger-ui/index.html 88 | ``` 89 | 90 | ```xml 91 | 92 | io.springfox 93 | springfox-boot-starter 94 | 3.0.0 95 | 96 | ``` 97 | 98 | ```java 99 | @EnableOpenApi 100 | @Configuration 101 | public class Swagger3Config { 102 | @Bean 103 | public Docket createRestApi(Environment environment) { 104 | //版本选择OAS3 105 | return new Docket(DocumentationType.OAS_30) 106 | .apiInfo(apiInfo()) 107 | //根据环境判断是否启用Swagger 108 | .enable(environment.acceptsProfiles(Profiles.of("dev","test"))) 109 | .select() 110 | .apis(RequestHandlerSelectors.basePackage("com.cloudnote"))//扫描的包路径 111 | .paths(PathSelectors.any()) 112 | .build(); 113 | } 114 | 115 | private ApiInfo apiInfo() { 116 | return new ApiInfoBuilder() 117 | .title("***") 118 | .description("** | Springboot | Swagger3") 119 | // .termsOfServiceUrl("你的项目地址") 120 | .version("1.0") 121 | // .contact(new Contact("联系人", "个人网站地址", "邮箱")) 122 | .build(); 123 | } 124 | } 125 | ``` 126 | -------------------------------------------------------------------------------- /etc/blog/分布式定时任务.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ## 定时任务发展历程 6 | 7 | ### 1、Windows批处理程序 8 | 9 | 我们在平时的工作中即可使用windows的批处理执行定时任务,比如下列的一个例子: 10 | 11 | 在cmd中输入 `shutdown -s -f -t 666`, 即可在666秒后自动关闭电脑 (或在.bat文件中输入这行命令并打开文件即可) 12 | 13 | ### 2、Windows任务计划程序 14 | 15 | 在计算机管理页面,找到任务计划程序,可以在这里导入或创建基本定时任务 16 | 17 | ### 3、Linux命令 - CronJob 18 | 19 | Cron命令在Linux中十分重要,Cron表达式也基本上是每个开发者必学的知识之一。在Linux中我们可以很轻易的使用简单的Cron表达式创建定时任务。当然也有些优缺点 20 | 21 | * 优点: CronJob是Linux系统命令,使用简单,稳定可靠 22 | * 缺点: 只能控制单台机器,且无法适用于其他操作系统 23 | 24 | ### 4、单机定时任务 25 | 26 | 在开发语言中一般都会有Timer、Ticker这种存在,他的优点是可以跨平台支持(开发语言特性),但是也是仅有单机可用 27 | 28 | Examples: 29 | 30 | #### Timer 31 | 32 | ~~~java 33 | // 每隔五分钟定时刷新本地缓存 34 | public static void main(String[] args) throws ParseException { 35 | Timer timer = new Timer(); 36 | timer.schedule(new TimerTask() { 37 | @override 38 | public void run() { 39 | SyncLocalCache(); 40 | } 41 | }, 5000, 5 * 60 * 1000); 42 | } 43 | ~~~ 44 | 45 | #### Ticker 46 | 47 | ~~~go 48 | // 每隔五分钟定时刷新本地缓存 49 | func main() { 50 | ticker := time.newTicker(5 * time.Minute) 51 | for { 52 | select { 53 | case <- ticker.C: 54 | SyncLocalCache() 55 | } 56 | } 57 | } 58 | ~~~ 59 | 60 | #### ScheduledExecutorService 61 | 62 | ~~~java 63 | // 每隔五分钟定时执行多个任务 64 | private static ScheduledExecutorServcice scheduler; 65 | public static void main(String[] args) throws Exception { 66 | scheduler = Executors.newScheduledThreadPool(5); 67 | 68 | scheduler.scheduleAtFixedRate((( 69 | new Runnable() { 70 | @Override 71 | public void run() { 72 | // DoSomething(); 73 | } 74 | })), 0, 300, TimeUnit.SECONDS) 75 | 76 | } 77 | ~~~ 78 | 79 | ### 5、任务调度 - Quartz 80 | 81 | Quartz这个轻量级的定时任务框架将单机单任务的定时任务做到了极致控制,可惜缺点在于无法进行负载均衡的配置 82 | 83 | ![image-20230208204541382](https://gitee.com/sky-dog/note/raw/master/img/202302082045557.png) 84 | 85 | ### 6、分布式定时任务 86 | 87 | 经过漫长的发展,终于迎来了我们今天课程的重点,分布式的定时任务。 88 | 89 | 优点有很多: 90 | 91 | * 平台化管理 92 | * 分布式部署 93 | * 支持海量数据 94 | 95 | 他的基本结构大概是如下图所示,以多个定时器对多个调度器进行控制 96 | 97 | ![image-20230208204824033](https://gitee.com/sky-dog/note/raw/master/img/202302082048173.png) 98 | 99 | 接下来,我们对这个分布式定时任务做一个简单的定义: 100 | 101 | 定时任务是指系统为了自动完成特定任务,**实时**、**延时**、**周期性**完成任务调度的过程。 102 | 103 | 分布式定时任务是把分散的、可靠性差的定时任务纳入统一的平台,并实现集群管理调度和分布式不是的一种定时任务的管理方式。 104 | 105 | #### 按触发机制分类: 106 | 107 | * 定时任务: 特定事件触发,比如今天11:45:14执行 108 | * 延时任务: 延时出发,比如10s后执行 109 | * 周期任务: 固定周期时间,或固定频率周期调度触发,比如每隔5s或者每天12点执行 110 | 111 | 112 | 113 | #### 分布式定时任务的特点: 114 | 115 | * 自动化: 全自动共完成定时任务的调度和执行 116 | * 平台化: 基于平台化的思维管控一系列的分布式定时任务 117 | * 分布式: 在分布式系统环境下运行任务调度,突破单机定时的性能瓶颈 118 | * 伸缩性: 采用集群方式部署,可以随时按需扩缩容 119 | * 高可用: 单点故障不影响最终任务结果,可以做到故障转移 120 | 121 | #### 分布式定时任务的执行方式: 122 | 123 | * 单机任务: 随机触发一台机器执行任务,适用于计算量小、并发度低的任务 124 | * 广播任务: 光波导所有机器上执行同一个任务,比如所有机器一起清理日志 125 | * Map任务: 一个任务可以分出多个子任务,每个子任务负责一部分的计算。适用于计算量大,单机无法满足要求的任务 126 | * MapReduce任务: 在Map任务的基础上,还可以对所有子任务的结果做汇总计算,适用于计算量大,并且需要对子任务结果做汇总的任务 127 | 128 | 129 | 130 | #### 业内常见定时任务框架 131 | 132 | ![image-20230208214350415](https://gitee.com/sky-dog/note/raw/master/img/202302082143552.png) 133 | 134 | ##### Xxl-job 135 | 136 | Xxl-job是大众点评员工徐雪里于2015年发布的分布式任务调度平台,是一个轻量级分布式任务调度框架,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。XXL-JOB 支持分片,简单支持任务依赖,支持子任务依赖,不是跨平台的。 137 | 138 | Xxl-job很大一个优势在于**开源且免费**,并且轻量级,开箱即用,操作简单,上手快,企业维护起来成本不高,因而在**中小型公司**中使用非常广泛 139 | 140 | ##### SchedulerX 141 | 142 | 分布式任务调度SchedulerX 2.0 是阿里巴巴基于Akka架构自研的新一代分布式任务调度平台,提供定时调度、调度任务编排和分布式批量处理等功能。 143 | 144 | SchedulerX 可在阿里云付费使用。他的功能非常强大,**在阿里巴巴内部广泛使用并久经考验。** 145 | 146 | ##### TCT 147 | 148 | 分布式任务调度服务(Tencent Cloud Task)是腾讯自主研发的一款高性能、高可靠通用的分布式任务调度中间件,通过指定时间规则严格触发调度任务,保障调度任务的可靠有序执行。该服务支持国际通用的时间表达式、调度任务执行生命周期管理,解决传统定时调度任务单点及并发性能问题。同时,支持任务分片、流程编排复杂调度任务处理能力,覆盖广泛的任务调度应用场景。 149 | 150 | **TCT仅在腾讯内部使用,未开源,也未商用。** 151 | 152 | 153 | 154 | ### 知识面扩充 155 | 156 | > 分布式定时任务 vs 单机定时任务 157 | 158 | 关系: 159 | 160 | * 都可以实现自动化的定时、延时、周期任务调度 161 | 162 | 差异: 163 | 164 | * 分布式定时任务可以支持更大的业务体量 165 | * 分布式定时任务的性能、伸缩性、稳定性更高 166 | 167 | > 分布式定时任务 vs 大数据处理引擎 168 | 169 | 关系: 170 | 171 | * 都可以对海量数据做处理 172 | * 性能、伸缩性、稳定性都很高 173 | 174 | 差异: 175 | 176 | * 定时并不是大数据处理引擎要解决的核心问题 177 | * 大数据处理引擎往往致力于将源数据处理成结果数据,分布式定时任务除了能做到这个之外,还可以调用HTTP和RPC服务 178 | 179 | 180 | 181 | ## 实现原理 182 | 183 | ### 核心架构 184 | 185 | 分布式定时任务核心要解决**触发、调度、执行**三个问题 186 | 187 | 这里就涉及到三个不同的概念: 188 | 189 | * 触发器: Trigger,解析任务,生成触发事件 190 | * 调度器: Scheduler,分配任务,管理任务生命周期 191 | * 执行器: Executor,获取执行任务单元,执行任务逻辑 192 | 193 | 除此之外,还需要提供一个控制台(Admin),提供任务管理和干预的功能。 194 | 195 | 而他们四者之间的关系正如下图所示: 196 | 197 | image-20230209161350002 198 | 199 | #### 数据流 200 | 201 | 接下来我们看一下分布式定时任务的数据流,也就是分布式定时任务整体上业务流程是如何运行的 202 | 203 | 如下图所示,用户通过告知控制任务对应的信息,在一定触发规则的基础上,执行了某一段任务代码,然后控制台将用户输入的定时诗句存入任务DB,以此完成任务创建工作 204 | 205 | 而任务执行,是控制台通过触发器,当触发器满足用户输入的触发规则,就会触发调度器进行定时任务的调度,最后通过执行器来成功完成整个任务的执行。 206 | 207 | ![image-20230209161734338](https://gitee.com/sky-dog/note/raw/master/img/202302091617526.png) 208 | 209 | #### 功能架构 210 | 211 | 功能架构呢,我们就分别从控制台、触发器、调度器、执行器四个方面,从下图就能很清晰的看见 212 | 213 | ![image-20230209162246833](https://gitee.com/sky-dog/note/raw/master/img/202302091622981.png) 214 | 215 | 216 | 217 | ### 控制台 218 | 219 | #### 基本概念 220 | 221 | 控制台中有多个关键元素,我们接下来一一简单讲解一下 222 | 223 | * 任务: Job,任务元数据 224 | * 任务实例: JobInstance,周期任务会生成多个任务实例 225 | * 任务结果: JobResult,任务实例运行的结果 226 | * 任务历史: JobHistory,用户可以修改任务信息,任务实例对应的任务元数据可以不同,因而使用任务历史存储 227 | 228 | 下图(E-R图)展示了这些元素之间的关系: 229 | 230 | ![image-20230209164147765](https://gitee.com/sky-dog/note/raw/master/img/202302091641865.png) 231 | 232 | ##### 任务 233 | 234 | 任务元数据(Job)是用户对任务属性定义,包括任务类型调度时机、执行行为等。 235 | 236 | image-20230209164403921 237 | 238 | ##### 任务实例 239 | 240 | 任务实例(JobInstance)是一个确定的Job的一次运行实例 241 | 242 | ![image-20230209164437216](https://gitee.com/sky-dog/note/raw/master/img/202302091644322.png) 243 | 244 | ### 触发器 245 | 246 | #### 核心职责 247 | 248 | 给定一系列任务,解析他们的触发规则,在规定的时间点触发任务的调度 249 | 250 | #### 设计约束 251 | 252 | * 需要支持大量任务 253 | * 需要支持秒级的调度 254 | * 周期任务需要多次执行 255 | * 需要保证秒级扫描的高性能并避免资源浪费 256 | 257 | #### 触发器方案 258 | 259 | ##### 定期扫描+延时消息 (腾讯、字节方案) 260 | 261 | 定时扫描机器集群部署,通过分布式锁保证只有一台机器在调度 262 | 263 | 基本流程如下图 264 | 265 | image-20230209165302910 266 | 267 | ##### 时间轮 (Quartz方案) 268 | 269 | 时间轮是一种高效利用县城资源进行批量化调度的一种调度模型。时间轮是一个存储环形队列,底层采用数组实现,数组中的每个元素可以存放一个定时任务列表。 270 | 271 | 其目的是便利任务列表,从中找到当前时间点需触发的任务列表 272 | 273 | 时间轮模型如下: 274 | 275 | image-20230209165422824 276 | 277 | 实现原理如下: 278 | 279 | ![image-20230209165716528](https://gitee.com/sky-dog/note/raw/master/img/202302091657600.png)![image-20230209165723645](https://gitee.com/sky-dog/note/raw/master/img/202302091657751.png) 280 | 281 | ![image-20230209165731285](https://gitee.com/sky-dog/note/raw/master/img/202302091657408.png) 282 | 283 | ![image-20230209165754634](https://gitee.com/sky-dog/note/raw/master/img/202302091657767.png) 284 | 285 | 286 | 287 | #### 高可用 288 | 289 | 触发器的高可用在分布式定时任务框架中的重要性是显而易见的,一旦任务调度受到影响,或者负责扫描和触发的机器或服务挂了,就会出现不可逆转的灾难 290 | 291 | 而解决高可用问题的思路有如下几种: 292 | 293 | * 存储上,不同国别、业务做资源隔离 294 | * 运行时,不同国别、业务分开执行 295 | * 部署时,采用多机房集群化部署,避免单点故障,通过数据库锁或分布式锁保证任务只被触发一次 296 | 297 | 而高可用的问题又引发出了一系列的问题: 298 | 299 | * 只有一个触发器,如果这个触发器故障,整个平台就崩溃了 300 | * 有多个触发器构成集群,可以避免单点故障,弹需要避免同一个任务被多次触发,导致业务紊乱 301 | 302 | 接下来我们就引入了**锁的概念** 303 | 304 | 我们可以通过数据库行锁的形式,在触发调度之前,更新数据库中JobInstance的状态,成功抢锁的才会触发调度,但在这种情况下,多台机器频繁竞争数据库锁,节点越多会导致性能越差 305 | 306 | 或者我们可以使用分布式锁来解决这种问题,在触发调度之前,尝试抢占分布式锁,可使用Redis锁或Zookeeper锁 307 | 308 | 这样的方法性能较高,有较多公司使用这种方案 309 | 310 | 311 | 312 | ### 调度器 313 | 314 | #### 资源来源 315 | 316 | 调度器在定时任务资源来源方面主要分为两个大类,我们分别来介绍 317 | 318 | ##### 业务系统提供机器资源 319 | 320 | 使用该方案的公司: 321 | 322 | * 美团、阿里、字节等等 323 | 324 | 优点: 325 | 326 | * 任务执行逻辑与业务系统公用同一份资源,利用率更高 327 | 328 | 缺点: 329 | 330 | * 更容易发生定时任务脚本影响在线服务的事故 331 | * 不能由定时任务平台控制扩缩容 332 | 333 | ##### 定时任务平台提供机器资源 334 | 335 | 使用该方案的公司: 336 | 337 | * 字节跳动等 338 | 339 | 优点: 340 | 341 | * 任务执行逻辑与业务系统提供的在线服务隔离,避免相互影响 342 | * 可以支持优雅地扩缩容 343 | 344 | 缺点: 345 | 346 | * 消耗更多机器资源 347 | * 需要额外为定时任务平台申请接口调用权限,而不能直接继承业务系统的权限 348 | 349 | #### 资源调度 350 | 351 | ##### 节点选择 352 | 353 | 而调度器选择使用哪个执行器节点进行定时任务的执行也主要分为三种情况: 354 | 355 | * 随机节点执行 356 | * 选择集群中一个可用的执行节点执行调度任务 357 | * 适用场景:定时对账 358 | * 广播执行 359 | * 在集群中所有的执行节点分发调度任务并执行 360 | * 适用场景:批量运维 361 | * 分片执行 362 | * 按照用户自定义的分片逻辑进行拆分,分发到集群中不同节点并行执行,提高资源利用效率 363 | * 适用场景:海量日志统计 364 | 365 | ##### 任务分片 366 | 367 | 我们可以通过任务分片来提高任务执行的效率和资源的利用率 368 | 369 | 我们可以通过下图的模型来理解 370 | 371 | ![image-20230209171502494](https://gitee.com/sky-dog/note/raw/master/img/202302091715600.png) 372 | 373 | 374 | 375 | #### 高级特性 376 | 377 | ##### 任务编排 378 | 379 | 大多数分布式定时任务框架都支持使用有向无环图DAG (Directed Acyclic Graph)进行可视化的任务编排 380 | 381 | 就如下图所示,是一种编排的例子: 382 | 383 | ![image-20230209171747846](https://gitee.com/sky-dog/note/raw/master/img/202302091717930.png) 384 | 385 | ##### 故障转移 386 | 387 | 故障转移是为了确保部分执行单元任务失败时,任务最终成功 388 | 389 | 分片任务基于一致性hash策略分发任务,当某个Executor异常时,调度器会将任务分发到其他的Executor 390 | 391 | ![image-20230209171903663](https://gitee.com/sky-dog/note/raw/master/img/202302091719760.png) 392 | 393 | 394 | 395 | #### 高可用 396 | 397 | 调度器可以做到集群部署,做到完全的无状态,依靠消息队列的重试机制保障任务一定会被调度 398 | 399 | ![image-20230209171953617](https://gitee.com/sky-dog/note/raw/master/img/202302091719706.png) 400 | 401 | ### 执行器 402 | 403 | 执行器的基本框架如下图,基于注册中心,执行器可以做到弹性进行扩缩容 404 | 405 | ![image-20230209172343622](https://gitee.com/sky-dog/note/raw/master/img/202302091723727.png) 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | -------------------------------------------------------------------------------- /etc/blog/单元测试.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 本文仅以 Spring Boot 2.2.0+ 版本 为基础作单元测试的介绍。 4 | 5 | 也就是说这篇文章是关于 Junit Jupiter(也就是Junit5)的使用! 6 | 7 | Junit4与Junit5有部分差别,有关Junit4的使用网上已经一大片文章了!Spring随着版本更新也舍弃了Junit4作为测试依赖。 8 | 9 | ~~如果有空的话会在本文最后补上Junit4和Junit5的异同~~ 10 | 11 | (本机测试版本是 SpringBoot 2.5.10) 12 | 13 | > Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库,在 Spring Boot 2.2.0 版本之前,`spring-boot-starter-test` 包含了 JUnit 4 的依赖,Spring Boot 2.2.0 版本之后替换成了 Junit Jupiter。 14 | 15 | ## 概念 16 | 17 | ### 单元测试是什么 18 | 19 | * **单元测试(unit testing)**,是指对软件中的最小可测试单元进行检查和验证。在Java中单元测试的最小单元是类。 20 | * 单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。执行单元测试,就是为了证明这 段代码的行为和我们期望是否一致。 21 | 22 | ### 为什么要用单元测试 23 | 24 | 现在大多数同学都在用传统的测试方案: Postman或ApiPost之类的工具发保温,用System.out(应该不会有还在用这种方式的了吧 #红温了)或者log打印日志,然后盯着命令行找bug; 25 | 26 | 这样的问题很大: 27 | 28 | * 眼睛看结果是否正确,瞅瞎不说,也太不智能.我们是程序员,能让代码解决的事情,绝不能靠人工去解决,一劳永逸,懒惰是第一生产力! 29 | * postman 只能对controller进行测试。controller要正确,前提是service,dao都正确。发现问题太晚,解决成本高。 30 | * 无法对内部的函数功能做测试 31 | * postman的测试案例与项目工程不再一起,这些案例只能自己一个人用,无法团队共享 32 | 33 | 而单元测试的好处: 34 | 35 | * 测试代码和工程代码在同一工程文件中,便于维护和传承。 36 | * 使用断言自动检测结果 37 | * 测试粒度小,可以小到每个函数 38 | * 测试模块间相互依赖小。开发完一个模块,就可以测试一个模块。 39 | 40 | 41 | 42 | ## 怎么做单元测试 43 | 44 | Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库,在 Spring Boot 2.2.0 版本之前,`spring-boot-starter-test` 包含了 JUnit 4 的依赖,**Spring Boot 2.2.0** 版本之后替换成了 **Junit Jupiter**。 45 | 46 | 正如最前面所说,我们这里使用Junit Jupiter作为例子进行演示 47 | 48 | 这边对**mapper、service、controller**层分别作演示(最好单元测试能覆盖到大多数函数!) 49 | 50 | ### 引入依赖 51 | 52 | 首先在pom.xml文件中添加spring-boot-starter-test依赖 53 | 54 | ~~~xml 55 | 56 | org.springframework.boot 57 | spring-boot-starter-test 58 | test 59 | 60 | ~~~ 61 | 62 | 导入starter之后,会引入一系列相关包,接下来简单介绍一下部分包的作用 63 | 64 | ![image-20230718133542863](https://gitee.com/sky-dog/note/raw/master/img/202307181335956.png) 65 | 66 | * json-path 67 | * JSON XPath解析json 68 | * assertj-core 69 | * 流式断言库 70 | * hamcrest 71 | * 匹配对象库 72 | * junit-jupiter (这里是Junit5) 73 | * Java应用程序单元测试库(核心库) 74 | * mockito 75 | * Java模拟框架,主要是用来mock请求,模拟请求接口的 76 | * jsonassert 77 | * json断言库 78 | 79 | ### 创建测试文件 80 | 81 | - test/java 存放test *.java文件 82 | - test/resource 存放yml配置文件 83 | 84 | 正常来说,当你创建springboot项目时,test/java已经帮你创建好了 (test/resource是非必要的) 85 | 86 | 当然如果你只是创建了一个常规maven项目或是其他方式导致没有这两个目录,可以自行新建并在IDEA中mark为资源目录。 87 | 88 | 然后,根据你的工程源码目录建立对应的包,并写入测试文件 89 | 90 | (这里可以通过IDEA自动生成,利用`alt+insert`快捷键可以呼出Idea的代码生成工具,选中其中的Test就能生成对应的test文件;在工具栏的Navigate选项也能找到对应入口) 91 | 92 | image-20230718134615692image-20230718134648226 93 | 94 | ### 测试基类 95 | 96 | 与Springboot主启动类对应的,有一个测试基类。 97 | 98 | 大致是长下面这个模样,接下来简单介绍一下修饰类的这几个注解 99 | 100 | ![image-20230718135002158](https://gitee.com/sky-dog/note/raw/master/img/202307181350235.png) 101 | 102 | * **@SpringbootTest** 103 | * 让测试在Spring容器环境下执行测试 104 | * **@Transactional** 105 | * 开启事务支持,支持工程代码中配置了的注解式事务 106 | * **@Rollback** 107 | * 设置测试后回滚,默认属性为true。保证测试完成后,测试数据不污染数据库,并且保证测试可以重复执行。 108 | * @ExtendWith(SpringExtension.class) 109 | * 与Junit4中的@RunWith(SpringRunner.class)作用基本相同 110 | * 事实上这句注解是**不必要**的(因为在**@SpringbootTest中已经包含**了,只是为了提醒上一点写了这个注解)![image-20230718135719704](https://gitee.com/sky-dog/note/raw/master/img/202307181358453.png) 111 | 112 | ### 三层模型测试代码 113 | 114 | Springboot项目采用经典三层模型搭建,即**dao/mapper,service,controller**。开发顺序通常是这样,测试顺序也是一般按照这个顺序进行。 115 | 116 | 从单元测试实践经验来说,dao/mapper层单元测试通常**不会强制要求**,而**service和controller则必须进行** 117 | 118 | #### mapper/dao层 119 | 120 | 该层一般框架已经做好了较为全面的测试,这里的测试通常不会强制要求 121 | 122 | 暂略 123 | 124 | **//TODO** 125 | 126 | #### Service层 127 | 128 | 这边直接贴了一个以前写的代码来作演示,框架模板都是之前发过的项目结构(详见[此处](https://github.com/SkyDDDog/template.git))。 129 | 130 | 添加了@Transactional和@Rollback两个注解来防止测试数据污染数据库; 131 | 132 | ```java 133 | package com.lear.service; 134 | 135 | import com.lear.entity.Book; 136 | import org.junit.jupiter.api.Test; 137 | import org.springframework.boot.test.context.SpringBootTest; 138 | import org.springframework.test.annotation.Rollback; 139 | import org.springframework.transaction.annotation.Transactional; 140 | import javax.annotation.Resource; 141 | import java.util.List; 142 | 143 | import static org.junit.jupiter.api.Assertions.*; 144 | 145 | @SpringBootTest 146 | @Transactional 147 | @Rollback 148 | class BookServiceTest { 149 | 150 | @Resource 151 | private BookService bookService; 152 | 153 | @Test 154 | void searchBook() { 155 | String key = ""; 156 | List books = bookService.searchBook(key); 157 | assertTrue(books.size() > 0); 158 | } 159 | 160 | @Test 161 | public void save(){ 162 | Book book = new Book(); 163 | String id = "114514"; 164 | book.setName("Java从入门到入土").setAuthor("二火"); 165 | book.setId(id); 166 | book.setNewRecord(true); 167 | int res = bookService.save(book); 168 | assertTrue(res > 0); 169 | Book queryBook = bookService.get(id); 170 | assertEquals(queryBook.getName(), book.getName()); 171 | assertEquals(queryBook.getAuthor(), book.getAuthor()); 172 | } 173 | 174 | } 175 | ``` 176 | 177 | #### controller层 178 | 179 | controller层的测试主要依赖于**MockMvc**来模拟外部请求我们的接口。 180 | 181 | 通过**@Before**注释在测试前初始化MockMvc对象。(也可以通过添加类注解**@AutoConfigureMockMvc**后,直接通过@Autowired自动注入MockMvc对象) 182 | 183 | 这里介绍一下,**@Before**和**@After**的作用。 184 | 185 | 好像其实不怎么需要介绍...这俩就是在测试开始前和测试结束后分别执行的代码块,基本上用来做一些测试准备工作和资源关闭的动作。。。 186 | 187 | 188 | 189 | 实际上,**@WebMvcTest**也包含了@AutoConfigureMockMvc等专门用来测试controller层的注解,所以我们实际上只需要使用@WebMvcTest即可 190 | 191 | ![image-20230718142537999](https://gitee.com/sky-dog/note/raw/master/img/202307181426812.png) 192 | 193 | 说明: 194 | 195 | * `@WebMvcTest` 注释告诉 Spring Boot 仅实例化 Controller 层,而不去实例化整体上下文,还可以进一步指定仅实例化 Controller 层的某个实例:`@WebMvcTest(HelloController.class)`; 196 | * 因为只实例化了 Controller 层,所以依赖的 Service 层实例需要通过 `@MockBean` 创建,并通过 `Mockito` 的方法指定 Mock 出来的 Service 层实例在特定情况下方法调用时的返回结果。 197 | 198 | ~~~java 199 | package com.lear.controller; 200 | 201 | import com.alibaba.fastjson.JSONObject; 202 | import com.lear.common.CommonResult; 203 | import com.lear.entity.Book; 204 | import com.lear.service.BookService; 205 | import org.junit.jupiter.api.Assertions; 206 | import org.junit.jupiter.api.Test; 207 | import org.mockito.Mockito; 208 | import org.springframework.beans.factory.annotation.Autowired; 209 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 210 | import org.springframework.boot.test.mock.mockito.MockBean; 211 | import org.springframework.test.web.servlet.MockMvc; 212 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; 213 | import org.springframework.test.web.servlet.result.MockMvcResultHandlers; 214 | import org.springframework.test.web.servlet.result.MockMvcResultMatchers; 215 | 216 | import java.util.ArrayList; 217 | 218 | @WebMvcTest(BookController.class) 219 | class BookControllerTest { 220 | 221 | @MockBean 222 | private BookService bookService; 223 | 224 | @Autowired 225 | private BookController bookController; 226 | 227 | @Autowired 228 | private MockMvc mvc; 229 | 230 | @Test 231 | public void testNotNull() { 232 | Assertions.assertNotNull(bookController); 233 | } 234 | 235 | @Test 236 | void getAllBook() throws Exception { 237 | CommonResult result = new CommonResult(); 238 | ArrayList mockResult = new ArrayList<>(); 239 | Book book = new Book(); 240 | String id = "114514"; 241 | book.setName("Java从入门到入土").setAuthor("二火"); 242 | book.setId(id); 243 | mockResult.add(book); 244 | result.success("book", mockResult); 245 | // 因为@WebMvcTest注解只加载了controller层 246 | // 所以controller层自动注入的service会出现问题 247 | // 这里使用mock来模拟service返回 248 | Mockito.when(bookService.findList(null)).thenReturn(mockResult); 249 | this.mvc.perform(MockMvcRequestBuilders.get("/api/book")) 250 | .andDo(MockMvcResultHandlers.print()) 251 | .andExpect(MockMvcResultMatchers.status().isOk()) 252 | .andExpect(MockMvcResultMatchers.content().json(JSONObject.toJSONString(result))); 253 | } 254 | } 255 | ~~~ 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | -------------------------------------------------------------------------------- /etc/blog/编程规范.md: -------------------------------------------------------------------------------- 1 | # **编程规范** 2 | 3 | ## 方法命名 4 | 5 | ### 1. 获取单个对象的方法用 get 作前缀 6 | 7 | 例如:查询单个用户 `getStudent`,按照 ID 查询单个用户 `getStudentById`。 8 | 9 | ### 2. 获取多个对象的方法用 list 作前缀 10 | 11 | 例如:按照 IDS 查询多个用户,`listStudentByIds`。 12 | 13 | ### 3. 获取统计值的方法用 count 作前缀 14 | 15 | 例如:统计全量用户,`countUser`。 16 | 17 | ### 4. 插入的方法用 save 作前缀 18 | 19 | 例如:新增用户,`saveUser`。 20 | 21 | ### 5, 删除的方法用 remove 作前缀 22 | 23 | 例如:删除用户,`removeUser`。 24 | 25 | ### 6. 修改的方法用 update 作前缀 26 | 27 | 例如:修改用户,`updateUser`。 28 | 29 | 30 | 31 | ## 领域模型命名规约 32 | 33 | ### 1. 数据对象 34 | 35 | xxxDO,xxx 即为数据表名。 36 | 37 | ### 2. 数据传输对象 38 | 39 | xxxDTO,xxx 为业务领域相关的名称。 40 | 41 | ### 3. 展示对象 42 | 43 | xxxVO,xxx 一般为网页名称。 44 | 45 | ### 4. 注意事项 46 | 47 | POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。 48 | 49 | 50 | 51 | ## API 路径规约 52 | 53 | ### 1. Get 方法尽量把 ID 等变量放到路径上。 54 | 55 | 例如:获取指定用户的信息。 56 | 57 | ```java 58 | /user/{id} 59 | ``` 60 | 61 | 62 | 63 | ### 2. 多个不可分割的单词,使用中划线拼接。 64 | 65 | 例如:用户验证码接口。 66 | 67 | ```java 68 | /user/verify-code 69 | ``` 70 | 71 | 72 | 73 | ### 3. 参数使用驼峰拼写。 74 | 75 | 例如:获取指定用户购买的指定商品。 76 | 77 | ```java 78 | /order/{productId} 79 | ``` 80 | 81 | 82 | 83 | ### 4. 指向集合的复数名称。 84 | 85 | 例如:获取所有用户列表接口。 86 | 87 | ```java 88 | /users 89 | ``` 90 | 91 | 92 | 93 | ### 5. 不用使用动词定义 URL 94 | 95 | 错误示例: 96 | 97 | ```java 98 | /update/user 99 | ``` 100 | 101 | 102 | 103 | 或者: 104 | 105 | ```java 106 | /get/user 107 | ``` 108 | 109 | 110 | 111 | 正确应该通过 HTTP 方法的语义来定义 URL 的行为。 112 | 113 | 比如说获取用户: 114 | 115 | ```java 116 | GET 117 | /user/{id} 118 | ``` 119 | 120 | 121 | 122 | 添加用户: 123 | 124 | ```java 125 | POST 126 | /user 127 | ``` 128 | 129 | 130 | 131 | 修改用户: 132 | 133 | ```java 134 | PUT 135 | /user 136 | ``` 137 | 138 | 139 | 140 | ### 6. 对非资源 URL 使用动词 141 | 142 | 如果有一个接口,并不是 CRUD 操作,这种情况可以使用动词。 143 | 144 | 例如:向用户发送邮件接口。 145 | 146 | ```java 147 | /user/{id}/send-mail 148 | ``` 149 | 150 | 151 | 152 | ### 7. 在嵌套资源的 URL 中使用关系 153 | 154 | 获取指定订单中所有商品列表。 155 | 156 | ```java 157 | GET 158 | /order/{id}/products 159 | ``` 160 | 161 | 162 | 163 | 获取指定订单中所有指定商品信息。 164 | 165 | ```java 166 | GET 167 | /order/{orderId}/product/{productId} 168 | ``` 169 | 170 | 171 | 172 | ## 注释规范 173 | 174 | ### 1. 注释说明意图即可,无需补充冗余字段 175 | 176 | 【强制】Class、Interface、Enum、@interface 等文件类型,类上注释仅需说明类的意图即可。不需要补充时间和创建人,因为 往往开发代码的不止是一个人,容易造成信息干扰。需要的话,查看提交记录即可。 177 | 178 | ```java 179 | /** 180 | * 适配第三方框架的线程池 181 | */ 182 | public interface ThreadPoolAdapter { 183 | 184 | } 185 | ``` 186 | 187 | 188 | 189 | ### 2. 方法上需要添加注释 190 | 191 | 【强制】方法上需添加注释,并说明清楚方法的意图(接口实现类无需注释);必要时描述 `@param` `@return`。 192 | 193 | ```java 194 | /** 195 | * 适配第三方框架的线程池 196 | */ 197 | public interface ThreadPoolAdapter { 198 | 199 | /** 200 | * 修改框架线程池的核心参数 201 | * 202 | * @param threadPoolBaseInfo 修改线程池的基础参数 203 | * @return 线程池核心参数修改结果 204 | */ 205 | boolean updateThreadPool(ThreadPoolBaseInfo threadPoolBaseInfo); 206 | } 207 | ``` 208 | 209 | 210 | 211 | ### 3. 方法块内部注释规范 212 | 213 | 【强制】方法内部的注释,应该新起一行,而不是跟在代码后面。 214 | 215 | ```java 216 | 正例: 217 | // 刷新动态线程池参数 218 | refreshDynamicPool(parameter, executor); 219 | 220 | 反例: 221 | refreshDynamicPool(parameter, executor); // 刷新动态线程池参数 222 | ``` 223 | 224 | 225 | 226 | ### 4. 方法命名说明方法本身意图 227 | 228 | 【强制】私有方法尽量通过方法命名说明方法语义。 229 | 230 | 231 | 232 | ## 代码开发规约 233 | 234 | 【强制】类、方法和变量的命名要做到顾名思义,避免使用缩写。 235 | 236 | 237 | 238 | 【强制】静态变量使用大写,多个单词使用下划线连接。示例:MESSAGE_CENTER_SEND_TYPR。 239 | 240 | 241 | 242 | 【强制】捕获的异常名称命名为 ex ;捕获异常且不做任何事情,异常名称命名为 ignored。 243 | 244 | 245 | 246 | 【强制】返回值变量使用 result 命名;循环中使用 each 命名循环变量;map 中使用 entry 代替 each 247 | 248 | ```java 249 | result 命名示范: 250 | private void parseDate(String data) { 251 | Result result = JSONUtil.parseObject(data, Result.class); 252 | return result; 253 | } 254 | 或采用 result 为前缀: 255 | private void parseDate(String data) { 256 | Result resultDate = JSONUtil.parseObject(data, Result.class); 257 | return resultDate; 258 | } 259 | 260 | each 命名示范: 261 | appNameLeaseMap.values().forEach(each -> appNameLeaseList.add(each)); 262 | 或是 for 循环: 263 | for (Lease each : appNameLeaseMap.values()) { 264 | appNameLeaseList.add(each); 265 | } 266 | ``` 267 | 268 | 269 | 270 | 【强制】业务系统中优先使用 Guava、HuTool、Common3 等工具类中的方法,不存在指定方法时再创建自定义工具类,禁止创建相同语义方法的工具类。 271 | 272 | 备注:定义组件项目时,尽量使用自定义工具类,避免因版本问题导致不确定的异常。 273 | 274 | 275 | 276 | 【强制】空实现接口或类不允许存在空格。 277 | 278 | ```java 279 | // 正例 280 | public interface AdapterThreadPoolMonitor extends ThreadPoolMonitor { 281 | } 282 | 283 | // 反例 284 | public interface AdapterThreadPoolMonitor extends ThreadPoolMonitor { 285 | 286 | } 287 | ``` 288 | 289 | 290 | 291 | 【建议】方法参数自定义实体对象别名尽量使用 requestParam,非自定义对象可以使用对应语义命名。 292 | 293 | ```java 294 | @PostMapping("/cart-items") 295 | @ApiOperation(value = "新增购物车") 296 | public Result addCartItem(@RequestBody CartItemAddReqDTO requestParam) { 297 | return Results.success(null); 298 | } 299 | ``` 300 | 301 | -------------------------------------------------------------------------------- /etc/blog/阿里巴巴Java开发手册.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/west2-online/learn-java/b3d8cff53b2987e26392385d7b06c6cd78abd8db/etc/blog/阿里巴巴Java开发手册.pdf -------------------------------------------------------------------------------- /etc/etc.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/west2-online/learn-java/b3d8cff53b2987e26392385d7b06c6cd78abd8db/etc/etc.md -------------------------------------------------------------------------------- /img/20230703142015.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/west2-online/learn-java/b3d8cff53b2987e26392385d7b06c6cd78abd8db/img/20230703142015.png --------------------------------------------------------------------------------